diff --git a/vue/components.d.ts b/vue/components.d.ts
index 4f8b7bf..c3196ef 100644
--- a/vue/components.d.ts
+++ b/vue/components.d.ts
@@ -14,12 +14,15 @@ declare module '@vue/runtime-core' {
ABreadcrumbItem: typeof import('ant-design-vue/es')['BreadcrumbItem']
AButton: typeof import('ant-design-vue/es')['Button']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
+ ACollapse: typeof import('ant-design-vue/es')['Collapse']
+ ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel']
ADrawer: typeof import('ant-design-vue/es')['Drawer']
ADropdown: typeof import('ant-design-vue/es')['Dropdown']
AForm: typeof import('ant-design-vue/es')['Form']
AFormItem: typeof import('ant-design-vue/es')['FormItem']
AImage: typeof import('ant-design-vue/es')['Image']
AInput: typeof import('ant-design-vue/es')['Input']
+ AInputGroup: typeof import('ant-design-vue/es')['InputGroup']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AMenu: typeof import('ant-design-vue/es')['Menu']
AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider']
@@ -36,8 +39,10 @@ declare module '@vue/runtime-core' {
ATabPane: typeof import('ant-design-vue/es')['TabPane']
ATabs: typeof import('ant-design-vue/es')['Tabs']
ATag: typeof import('ant-design-vue/es')['Tag']
+ ATextarea: typeof import('ant-design-vue/es')['Textarea']
ATooltip: typeof import('ant-design-vue/es')['Tooltip']
BaseFileListInfo: typeof import('./src/components/BaseFileListInfo.vue')['default']
+ ChangeIndicator: typeof import('./src/components/ChangeIndicator.vue')['default']
ContextMenu: typeof import('./src/components/ContextMenu.vue')['default']
FileItem: typeof import('./src/components/FileItem.vue')['default']
NumInput: typeof import('./src/components/numInput.vue')['default']
diff --git a/vue/src/api/files.ts b/vue/src/api/files.ts
index c1dbb5b..0ebc18c 100644
--- a/vue/src/api/files.ts
+++ b/vue/src/api/files.ts
@@ -9,6 +9,15 @@ export interface FileNodeInfo {
bytes: number
fullpath: string
is_under_scanned_path: boolean
+ gen_info_raw?: string
+ gen_info_obj?: object
+}
+
+export interface GenDiffInfo {
+ empty: boolean
+ ownFile: string
+ otherFile: string
+ diff: object
}
export const getTargetFolderFiles = async (folder_path: string) => {
diff --git a/vue/src/components/ChangeIndicator.vue b/vue/src/components/ChangeIndicator.vue
new file mode 100644
index 0000000..6d90129
--- /dev/null
+++ b/vue/src/components/ChangeIndicator.vue
@@ -0,0 +1,301 @@
+
+
+
+
+
+
P+
+
P-
+
Se
+
St
+
Cf
+
Si
+
Mo
+
Ot
+
+
+
+ This file vs {{ genDiffToPrevious.otherFile }}
+
+
+
+
+ | + Prompt |
+ {{ genDiffToPrevious.diff.prompt }} tokens changed |
+
+
+ | - Prompt |
+ {{ genDiffToPrevious.diff.negativePrompt }} tokens changed |
+
+
+ | Seed |
+ {{ genDiffToPrevious.diff.seed[0] }} vs {{ genDiffToPrevious.diff.seed[1] }} |
+
+
+ | Steps |
+ {{ genDiffToPrevious.diff.steps[0] }} vs {{ genDiffToPrevious.diff.steps[1] }}
+ |
+
+
+ | Cfg Scale |
+ {{ genDiffToPrevious.diff.cfgScale[0] }} vs {{
+ genDiffToPrevious.diff.cfgScale[1] }} |
+
+
+ | Size |
+ {{ genDiffToPrevious.diff.size[0] }} vs {{ genDiffToPrevious.diff.size[1] }}
+ |
+
+
+ | Model |
+ {{ genDiffToPrevious.diff.Model[0] }} vs {{ genDiffToPrevious.diff.Model[1] }}
+ |
+
+
+
+
+
+
+
Other props that changed:
+
+
+
+
+
+
P+
+
P-
+
Se
+
St
+
Cf
+
Si
+
Mo
+
Ot
+
+
+
+ This file vs {{ genDiffToNext.otherFile }}
+
+
+
+
+ | + Prompt |
+ {{ genDiffToNext.diff.prompt }} tokens changed |
+
+
+ | - Prompt |
+ {{ genDiffToNext.diff.negativePrompt }} tokens changed |
+
+
+ | Seed |
+ {{ genDiffToNext.diff.seed[0] }} vs {{ genDiffToNext.diff.seed[1] }} |
+
+
+ | Steps |
+ {{ genDiffToNext.diff.steps[0] }} vs {{ genDiffToNext.diff.steps[1] }} |
+
+
+ | Cfg Scale |
+ {{ genDiffToNext.diff.cfgScale[0] }} vs {{ genDiffToNext.diff.cfgScale[1] }}
+ |
+
+
+ | Size |
+ {{ genDiffToNext.diff.size[0] }} vs {{ genDiffToNext.diff.size[1] }} |
+
+
+ | Model |
+ {{ genDiffToNext.diff.Model[0] }} vs {{ genDiffToNext.diff.Model[1] }} |
+
+
+
+
+
+
+
Other props that changed:
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/vue/src/components/FileItem.vue b/vue/src/components/FileItem.vue
index 173609a..f0f1363 100644
--- a/vue/src/components/FileItem.vue
+++ b/vue/src/components/FileItem.vue
@@ -8,10 +8,12 @@ import { toImageThumbnailUrl, toRawFileUrl } from '@/util/file'
import type { MenuInfo } from 'ant-design-vue/lib/menu/src/interface'
import { computed } from 'vue'
import ContextMenu from './ContextMenu.vue'
+import ChangeIndicator from './ChangeIndicator.vue'
import { useTagStore } from '@/store/useTagStore'
import { CloseCircleOutlined, StarFilled, StarOutlined, PlayCircleFilled } from '@/icon'
import { Tag } from '@/api/db'
import { openVideoModal } from './functionalCallableComp'
+import type { GenDiffInfo } from '@/api/files'
const global = useGlobalStore()
const tagStore = useTagStore()
@@ -26,6 +28,10 @@ const props = withDefaults(
enableRightClickMenu?: boolean,
enableCloseIcon?: boolean
isSelectedMutilFiles?: boolean
+ genDiffToPrevious: GenDiffInfo
+ genDiffToNext: GenDiffInfo
+ genInfo?: string
+ enableChangeIndicator: boolean
}>(),
{ selected: false, enableRightClickMenu: true, enableCloseIcon: false }
)
@@ -105,6 +111,10 @@ const taggleLikeTag = () => {
这么复杂是因为再全屏查看时可能因为直接删除导致fullpath变化,然后整个预览直接退出-->
+
+
+
+
+
.center {
display: flex;
justify-content: center;
@@ -322,5 +333,4 @@ const taggleLikeTag = () => {
flex-direction: column;
align-items: flex-end;
}
-}
-
+}
diff --git a/vue/src/i18n/en.ts b/vue/src/i18n/en.ts
index eb32721..3cab0a1 100644
--- a/vue/src/i18n/en.ts
+++ b/vue/src/i18n/en.ts
@@ -38,6 +38,8 @@ export const en: IIBI18nMap = {
other: 'Other',
livePreview: 'Live Preview',
gridCellWidth: 'Grid Cell Width (px)',
+ showChangeIndicators: 'Show Change Indicators',
+ seedAsChange: 'Seed as Change',
defaultGridCellWidth: 'Default Grid Cell Width (px)',
thumbnailResolution: 'Thumbnail Resolution (px)',
inputTargetFolderPath: 'Enter the absolute path of the target folder',
diff --git a/vue/src/page/fileTransfer/stackView.vue b/vue/src/page/fileTransfer/stackView.vue
index 8648360..a71f9b7 100644
--- a/vue/src/page/fileTransfer/stackView.vue
+++ b/vue/src/page/fileTransfer/stackView.vue
@@ -23,9 +23,12 @@ import FileItem from '@/components/FileItem.vue'
import fullScreenContextMenu from './fullScreenContextMenu.vue'
import BaseFileListInfo from '@/components/BaseFileListInfo.vue'
import { copy2clipboardI18n } from '@/util'
-import { openFolder } from '@/api'
+import { openFolder, getImageGenerationInfo } from '@/api'
import { sortMethods } from './fileSort'
import { isTauri } from '@/util/env'
+import { parse } from 'stable-diffusion-image-metadata'
+import { ref } from 'vue'
+import type { GenDiffInfo } from '@/api/files'
const global = useGlobalStore()
const props = defineProps<{
@@ -83,7 +86,99 @@ watch(
{ immediate: true }
)
+const changeIndchecked = ref(true);
+const seedChangeChecked = ref(false);
+function getRawGenParams() {
+ for (let f in sortedFiles.value) {
+ if (sortedFiles.value[f].gen_info_raw) {
+ continue
+ }
+ let path = sortedFiles.value[f].fullpath
+ q.pushAction(() => getImageGenerationInfo(path)).res.then((v) => {
+ sortedFiles.value[f].gen_info_raw = v
+ sortedFiles.value[f].gen_info_obj = parse(v)
+ })
+ }
+}
+
+function getGenDiff(ownGenInfo: any, idx: any, increment: any, ownFileName?: any) {
+ //init result obj
+ let result: GenDiffInfo = {
+ diff: {},
+ empty: true,
+ ownFile: "",
+ otherFile: ""
+ }
+
+ //check for out of bounds
+ if(idx + increment < 0
+ || idx + increment >= sortedFiles.value.length
+ || sortedFiles.value[idx] == undefined) {
+ return result
+ }
+ //check for gen_info_obj existence
+ if(!("gen_info_obj" in sortedFiles.value[idx])
+ || !("gen_info_obj" in sortedFiles.value[idx + increment])) {
+ return result
+ }
+
+ //diff vars init
+ let gen_a = ownGenInfo
+ let gen_b = sortedFiles.value[idx + increment].gen_info_obj
+ if(gen_b == undefined) {
+ return result
+ }
+
+ //further vars
+ let skip = ["hashes", "resources"]
+ result.diff = {}
+ result.ownFile = ownFileName,
+ result.otherFile = sortedFiles.value[idx + increment].name,
+ result.empty = false
+
+ if(!seedChangeChecked.value) {
+ skip.push("seed")
+ }
+
+ //actual per property diff
+ for (let k in gen_a) {
+ //skip unwanted values
+ if (skip.includes(k)) {
+ continue
+ }
+ //for all non-identical values, compare type based
+ //existence test
+ if (!(k in gen_b)) {
+ result.diff[k] = "+"
+ continue
+ }
+ //content test
+ if (gen_a[k] != gen_b[k]) {
+ if(k.includes("rompt") && gen_a[k] != "" && gen_b[k] != "") {
+ //prompt values are comma separated, handle them differently
+ let tokenize_a = gen_a[k].split(",")
+ let tokenize_b = gen_b[k].split(",")
+ //count how many tokens are different or at a different place
+ let diff_count = 0
+ for (let i in tokenize_a) {
+ if(tokenize_a[i] != tokenize_b[i]) {
+ diff_count++
+ }
+ }
+ result.diff[k] = diff_count;
+ } else {
+ //all others
+ result.diff[k] = [gen_a[k],gen_b[k]]
+ }
+ }
+ }
+
+ //result
+ return result
+}
+
+getRawGenParams();
@@ -191,6 +286,12 @@ watch(
+
+
+
+
+
+
+ v-on:scroll="getRawGenParams()" :item-size="itemSize.first" key-field="fullpath"
+ :item-secondary-size="itemSize.second" :gridItems="gridItems">
+ :is-selected-mutil-files="multiSelectedIdxs.length > 1"
+ :gen-diff-to-next="getGenDiff(file.gen_info_obj, idx, 1, file.name)"
+ :gen-diff-to-previous="getGenDiff(file.gen_info_obj, idx, -1, file.name)"
+ :enable-change-indicator="changeIndchecked"
+ />
@@ -228,7 +334,7 @@ watch(
{{ $t('loadNextPage') }}
-
+