Skip to content

Commit f1050d3

Browse files
authored
Merge pull request #105 from jiyee/feat-unique-image-name
feat(chrome-extension): add option to use unique name for files
2 parents 9207df8 + 7973938 commit f1050d3

File tree

10 files changed

+130
-10
lines changed

10 files changed

+130
-10
lines changed

.changeset/funny-icons-call.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@dolphin/chrome-extension': patch
3+
---
4+
5+
feat: add option to use unique name for files

apps/chrome-extension/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
"radash": "catalog:prod",
4949
"reka-ui": "catalog:prod",
5050
"serialize-error": "catalog:prod",
51+
"uuid": "catalog:prod",
5152
"vee-validate": "catalog:prod",
5253
"vue": "catalog:prod",
5354
"vue-i18n": "catalog:prod",

apps/chrome-extension/src/common/settings.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export enum SettingKey {
99
DownloadMethod = 'download.method',
1010
TableWithNonPhrasingContent = 'general.table_with_non_phrasing_content',
1111
TextHighlight = 'general.text_highlight',
12+
DownloadFileWithUniqueName = 'download.file_with_unique_name',
1213
}
1314

1415
export enum Theme {
@@ -33,6 +34,7 @@ export interface Settings {
3334
[SettingKey.DownloadMethod]: (typeof DownloadMethod)[keyof typeof DownloadMethod]
3435
[SettingKey.TableWithNonPhrasingContent]: (typeof TableWithNonPhrasingContent)[keyof typeof TableWithNonPhrasingContent]
3536
[SettingKey.TextHighlight]: boolean
37+
[SettingKey.DownloadFileWithUniqueName]: boolean
3638
}
3739

3840
export const fallbackSettings: Settings = {
@@ -43,6 +45,7 @@ export const fallbackSettings: Settings = {
4345
: DownloadMethod.Direct,
4446
[SettingKey.TableWithNonPhrasingContent]: TableWithNonPhrasingContent.ToHTML,
4547
[SettingKey.TextHighlight]: true,
48+
[SettingKey.DownloadFileWithUniqueName]: false,
4649
}
4750

4851
export const getSettings = async <Key extends keyof Settings>(

apps/chrome-extension/src/common/utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { toHast } from 'mdast-util-to-hast'
22
import { toHtml } from 'hast-util-to-html'
33
import type { InvalidTable } from '@dolphin/lark'
4+
import { v4 as uuidv4 } from 'uuid'
45

56
interface Ref<T> {
67
current: T
@@ -71,6 +72,26 @@ export class UniqueFileName {
7172

7273
return newFileName
7374
}
75+
76+
generateWithUUID(originFileName: string): string {
77+
const startDotIndex = originFileName.lastIndexOf('.')
78+
const extension =
79+
startDotIndex === -1 ? '' : originFileName.slice(startDotIndex)
80+
const uuid = uuidv4()
81+
const newFileName = `${uuid}${extension}`
82+
83+
// Ensure UUID-based names are also unique
84+
let finalFileName = newFileName
85+
let counter = 1
86+
while (this.usedNames.has(finalFileName)) {
87+
finalFileName = `${uuid}-${counter.toFixed()}${extension}`
88+
counter++
89+
}
90+
91+
this.usedNames.add(finalFileName)
92+
93+
return finalFileName
94+
}
7495
}
7596

7697
export const transformInvalidTablesToHtml = (

apps/chrome-extension/src/pages/options/download.vue

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,15 @@ import {
2525
} from '@/components/ui/select'
2626
import { Button } from '@/components/ui/button'
2727
import { Skeleton } from '@/components/ui/skeleton'
28+
import { Switch } from '@/components/ui/switch'
2829
import { SettingKey, DownloadMethod } from '@/common/settings'
2930
import { useSettings } from '../shared/settings'
3031
3132
const { t } = useI18n()
3233
3334
const schema = z.object({
3435
[SettingKey.DownloadMethod]: z.enum(DownloadMethod),
36+
[SettingKey.DownloadFileWithUniqueName]: z.boolean(),
3537
})
3638
3739
const { query, mutation } = useSettings()
@@ -43,7 +45,12 @@ const { meta, values, isSubmitting, handleSubmit, resetForm } = useForm({
4345
4446
watch(query.data, newValues => {
4547
if (newValues) {
46-
resetForm({ values: pick(newValues, [SettingKey.DownloadMethod]) })
48+
resetForm({
49+
values: pick(newValues, [
50+
SettingKey.DownloadMethod,
51+
SettingKey.DownloadFileWithUniqueName,
52+
]),
53+
})
4754
}
4855
})
4956
@@ -114,6 +121,28 @@ const downloadMethodDescription = computed(() => {
114121
</Select>
115122
</Field>
116123
</VeeField>
124+
<VeeField
125+
v-slot="{ field, errors }"
126+
:name="`[${SettingKey.DownloadFileWithUniqueName}]`"
127+
>
128+
<Field orientation="horizontal" :data-invalid="!!errors.length">
129+
<FieldContent>
130+
<FieldLabel for="form-vee-download-file-with-unique-name">{{
131+
t('download.file_with_unique_name')
132+
}}</FieldLabel>
133+
<FieldError v-if="errors.length" :errors="errors" />
134+
</FieldContent>
135+
<Skeleton v-if="query.isPending.value" class="h-9 w-40" />
136+
<Switch
137+
v-else
138+
id="form-vee-download-file-with-unique-name"
139+
:name="field.name"
140+
:model-value="field.value"
141+
:aria-invalid="!!errors.length"
142+
@update:model-value="field.onChange"
143+
/>
144+
</Field>
145+
</VeeField>
117146
<Button
118147
type="submit"
119148
class="relative"

apps/chrome-extension/src/pages/shared/i18n.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ export const i18n = createI18n({
3030
'general.table_with_non_phrasing_content.to_html': 'To HTML',
3131
'general.text_highlight':
3232
'Preserve text highlighting (font color, font background color)',
33+
'download.file_with_unique_name':
34+
'Use UUID for image and diagram filenames',
3335
'download.method': 'Download Method',
3436
'download.method.placeholder': 'Select download method',
3537
'download.method.direct': 'Direct Download',
@@ -64,6 +66,7 @@ export const i18n = createI18n({
6466
'general.table_with_non_phrasing_content.filtered': '过滤块级内容',
6567
'general.table_with_non_phrasing_content.to_html': '转换为 HTML',
6668
'general.text_highlight': '保留文本高亮(字体颜色、字体背景颜色)',
69+
'download.file_with_unique_name': '图片和图表文件使用 UUID 命名',
6770
'download.method': '下载方式',
6871
'download.method.placeholder': '选择下载方式',
6972
'download.method.direct': '直接下载',

apps/chrome-extension/src/pages/shared/settings.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ export const useSettings = <
2323
| SettingKey.Theme
2424
| SettingKey.DownloadMethod
2525
| SettingKey.TableWithNonPhrasingContent
26-
| SettingKey.TextHighlight,
26+
| SettingKey.TextHighlight
27+
| SettingKey.DownloadFileWithUniqueName,
2728
>(
2829
options: { keys?: Key[] } = {},
2930
): {
@@ -42,6 +43,7 @@ export const useSettings = <
4243
SettingKey.DownloadMethod,
4344
SettingKey.TableWithNonPhrasingContent,
4445
SettingKey.TextHighlight,
46+
SettingKey.DownloadFileWithUniqueName,
4547
] as Key[],
4648
} = options
4749

apps/chrome-extension/src/scripts/download-lark-docx-as-markdown.ts

Lines changed: 51 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,11 +146,13 @@ const downloadImage = async (
146146
image: mdast.Image,
147147
options: {
148148
signal?: AbortSignal
149+
useUUID?: boolean
150+
markdownFileName?: string
149151
} = {},
150152
): Promise<DownloadResult | null> => {
151153
if (!image.data) return null
152154

153-
const { signal } = options
155+
const { signal, useUUID = false, markdownFileName = '' } = options
154156

155157
const { name: originName, fetchSources, fetchBlob } = image.data
156158

@@ -166,7 +168,12 @@ const downloadImage = async (
166168
const content = await fetchBlob()
167169
if (!content) return null
168170

169-
const name = uniqueFileName.generate('diagram.png')
171+
const baseName = markdownFileName
172+
? `${markdownFileName}-diagram.png`
173+
: 'diagram.png'
174+
const name = useUUID
175+
? uniqueFileName.generateWithUUID(baseName)
176+
: uniqueFileName.generate(baseName)
170177
const filename = `images/${name}`
171178

172179
image.url = filename
@@ -185,7 +192,12 @@ const downloadImage = async (
185192
const sources = await fetchSources()
186193
if (!sources) return null
187194

188-
const name = uniqueFileName.generate(originName)
195+
const baseName = markdownFileName
196+
? `${markdownFileName}-${originName}`
197+
: originName
198+
const name = useUUID
199+
? uniqueFileName.generateWithUUID(baseName)
200+
: uniqueFileName.generate(baseName)
189201
const filename = `images/${name}`
190202

191203
const { src } = sources
@@ -263,11 +275,13 @@ const downloadFile = async (
263275
file: mdast.Link,
264276
options: {
265277
signal?: AbortSignal
278+
useUUID?: boolean
279+
markdownFileName?: string
266280
} = {},
267281
): Promise<DownloadResult | null> => {
268282
if (!file.data?.name || !file.data.fetchFile) return null
269283

270-
const { signal } = options
284+
const { signal, useUUID = false, markdownFileName = '' } = options
271285

272286
const { name, fetchFile } = file.data
273287

@@ -280,7 +294,12 @@ const downloadFile = async (
280294
const result = await withSignal(
281295
async () => {
282296
try {
283-
const filename = `files/${uniqueFileName.generate(name)}`
297+
const baseName = markdownFileName ? `${markdownFileName}-${name}` : name
298+
const filename = `files/${
299+
useUUID
300+
? uniqueFileName.generateWithUUID(baseName)
301+
: uniqueFileName.generate(baseName)
302+
}`
284303

285304
const response = await fetchFile({ signal: controller.signal })
286305
try {
@@ -352,9 +371,18 @@ const downloadFiles = async (
352371
*/
353372
batchSize?: number
354373
signal?: AbortSignal
374+
useUUID?: boolean
375+
markdownFileName?: string
355376
} = {},
356377
): Promise<DownloadResult[]> => {
357-
const { onProgress, onComplete, batchSize = 3, signal } = options
378+
const {
379+
onProgress,
380+
onComplete,
381+
batchSize = 3,
382+
signal,
383+
useUUID = false,
384+
markdownFileName = '',
385+
} = options
358386

359387
let completeEventCalled = false
360388
const onCompleteOnce = () => {
@@ -385,8 +413,16 @@ const downloadFiles = async (
385413
try {
386414
const result =
387415
file.type === 'image'
388-
? await downloadImage(file, { signal })
389-
: await downloadFile(file, { signal })
416+
? await downloadImage(file, {
417+
signal,
418+
useUUID,
419+
markdownFileName,
420+
})
421+
: await downloadFile(file, {
422+
signal,
423+
useUUID,
424+
markdownFileName,
425+
})
390426

391427
if (result) {
392428
_results.push(result)
@@ -499,6 +535,7 @@ const main = async (options: { signal?: AbortSignal } = {}) => {
499535
SettingKey.DownloadMethod,
500536
SettingKey.TableWithNonPhrasingContent,
501537
SettingKey.TextHighlight,
538+
SettingKey.DownloadFileWithUniqueName,
502539
])
503540

504541
const { root, images, files, invalidTables } = docx.intoMarkdownAST({
@@ -559,11 +596,15 @@ const main = async (options: { signal?: AbortSignal } = {}) => {
559596
Toast.remove(TranslationKey.IMAGE)
560597
},
561598
signal,
599+
useUUID: settings[SettingKey.DownloadFileWithUniqueName],
600+
markdownFileName: recommendName,
562601
}),
563602
// Diagrams must be downloaded one by one
564603
downloadFiles(diagrams, {
565604
batchSize: 1,
566605
signal,
606+
useUUID: settings[SettingKey.DownloadFileWithUniqueName],
607+
markdownFileName: recommendName,
567608
}),
568609
downloadFiles(files, {
569610
onProgress: progress => {
@@ -580,6 +621,8 @@ const main = async (options: { signal?: AbortSignal } = {}) => {
580621
Toast.remove(TranslationKey.FILE)
581622
},
582623
signal,
624+
useUUID: settings[SettingKey.DownloadFileWithUniqueName],
625+
markdownFileName: recommendName,
583626
}),
584627
])
585628
results.flat(1).forEach(({ filename, content }) => {

pnpm-lock.yaml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ catalogs:
6969
reka-ui: 2.5.1
7070
serialize-error: ^12.0.0
7171
tslib: ^2.8.1
72+
uuid: 13.0.0
7273
vee-validate: 4.15.1
7374
vue: ^3.5.22
7475
vue-i18n: 12.0.0-alpha.3

0 commit comments

Comments
 (0)