diff --git a/mise.toml b/mise.toml index d92acf3..b3f1828 100644 --- a/mise.toml +++ b/mise.toml @@ -6,3 +6,6 @@ watchexec = "latest" [settings] idiomatic_version_file_enable_tools = ["node"] + +[env] +SIZE_LIMIT = "100mb" diff --git a/packages/frontend/locales/cs.json b/packages/frontend/locales/cs.json index e174950..2450acf 100644 --- a/packages/frontend/locales/cs.json +++ b/packages/frontend/locales/cs.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "Ve výchozím nastavení se pro každou poznámku generuje bezpečné heslo. Pokud chcete, můžete si nastavit vlastní heslo, které nebude součástí odkazu.", "custom_password": "Vlastní heslo" - } + }, + "pasting": "Vkládání...", + "pasted_files": "Vložené soubory", + "remove": "Odstranit" }, "show": { "errors": { diff --git a/packages/frontend/locales/de.json b/packages/frontend/locales/de.json index 2b7f856..a43e794 100644 --- a/packages/frontend/locales/de.json +++ b/packages/frontend/locales/de.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "Standardmäßig wird für jede Notiz ein generiertes, sicheres Passwort verwendet. Alternativ können Sie ein eigenes Kennwort festlegen, welches nicht im Link enthalten ist.", "custom_password": "Benutzerdefiniertes Passwort" - } + }, + "pasting": "Einfügen...", + "pasted_files": "Eingefügte Dateien", + "remove": "Entfernen" }, "show": { "errors": { diff --git a/packages/frontend/locales/en.json b/packages/frontend/locales/en.json index 5e3cbc1..adc9829 100644 --- a/packages/frontend/locales/en.json +++ b/packages/frontend/locales/en.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "By default, a securely generated password is used for each note. You can however also choose your own password, which is not included in the link.", "custom_password": "custom password" - } + }, + "pasting": "Pasting...", + "pasted_files": "Pasted Files", + "remove": "Remove" }, "show": { "errors": { diff --git a/packages/frontend/locales/es.json b/packages/frontend/locales/es.json index b482c22..e17d18b 100644 --- a/packages/frontend/locales/es.json +++ b/packages/frontend/locales/es.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "Por defecto, se utiliza una contraseña generada de forma segura para cada nota. No obstante, también puede elegir su propia contraseña, la cual no se incluye en el enlace.", "custom_password": "contraseña personalizada" - } + }, + "pasting": "Pegando...", + "pasted_files": "Archivos pegados", + "remove": "Eliminar" }, "show": { "errors": { diff --git a/packages/frontend/locales/fr.json b/packages/frontend/locales/fr.json index 41a7b50..1c655fb 100644 --- a/packages/frontend/locales/fr.json +++ b/packages/frontend/locales/fr.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "Par défaut, un mot de passe généré de manière sécurisée est utilisé pour chaque note. Vous pouvez toutefois choisir votre propre mot de passe, qui n'est pas inclus dans le lien.", "custom_password": "mot de passe personnalisé" - } + }, + "pasting": "Collage...", + "pasted_files": "Fichiers collés", + "remove": "Supprimer" }, "show": { "errors": { diff --git a/packages/frontend/locales/it.json b/packages/frontend/locales/it.json index b2636b9..32b156d 100644 --- a/packages/frontend/locales/it.json +++ b/packages/frontend/locales/it.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "Per impostazione predefinita, per ogni nota viene utilizzata una password generata in modo sicuro. È tuttavia possibile scegliere la propria password, che non è inclusa nel link.", "custom_password": "password personalizzata" - } + }, + "pasting": "Incollando...", + "pasted_files": "File incollati", + "remove": "Rimuovi" }, "show": { "errors": { diff --git a/packages/frontend/locales/ja.json b/packages/frontend/locales/ja.json index 97833e2..9e3d625 100644 --- a/packages/frontend/locales/ja.json +++ b/packages/frontend/locales/ja.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "デフォルトでは、安全に生成されたパスワードが各ノートに使用されます。しかし、リンクに含まれない独自のパスワードを選択することもできます。", "custom_password": "カスタムパスワード" - } + }, + "pasting": "貼り付け中...", + "pasted_files": "貼り付けたファイル", + "remove": "削除" }, "show": { "errors": { diff --git a/packages/frontend/locales/pl.json b/packages/frontend/locales/pl.json index a92a0ee..930c1b5 100644 --- a/packages/frontend/locales/pl.json +++ b/packages/frontend/locales/pl.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "Domyślnie dla każdej notatki używane jest bezpiecznie wygenerowane hasło. Możesz jednak wybrać własne hasło, które nie jest uwzględnione w linku.", "custom_password": "własne hasło" - } + }, + "pasting": "Wklejanie...", + "pasted_files": "Wklejone pliki", + "remove": "Usuń" }, "show": { "errors": { diff --git a/packages/frontend/locales/ru.json b/packages/frontend/locales/ru.json index c2f33ac..1c304f2 100644 --- a/packages/frontend/locales/ru.json +++ b/packages/frontend/locales/ru.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "По умолчанию для каждой заметки используется безопасно сгенерированный пароль. Однако вы также можете выбрать свой собственный пароль, который не включен в ссылку.", "custom_password": "пользовательский пароль" - } + }, + "pasting": "Вставка...", + "pasted_files": "Вставленные файлы", + "remove": "Удалить" }, "show": { "errors": { diff --git a/packages/frontend/locales/zh-TW.json b/packages/frontend/locales/zh-TW.json index 819aea0..2dd31f7 100644 --- a/packages/frontend/locales/zh-TW.json +++ b/packages/frontend/locales/zh-TW.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "預設情況下,每個筆記都會使用安全生成的密碼。您也可以選擇自己的密碼,該密碼不會包含在連結中。", "custom_password": "自定義密碼" - } + }, + "pasting": "正在粘貼...", + "pasted_files": "粘貼的檔案", + "remove": "移除" }, "show": { "errors": { diff --git a/packages/frontend/locales/zh.json b/packages/frontend/locales/zh.json index be5e506..7aaf1d9 100644 --- a/packages/frontend/locales/zh.json +++ b/packages/frontend/locales/zh.json @@ -36,7 +36,10 @@ "advanced": { "explanation": "默认情况下,每个笔记都使用安全生成的密码。但是,您也可以选择您自己的密码,该密码未包含在链接中。", "custom_password": "自定义密码" - } + }, + "pasting": "正在粘贴...", + "pasted_files": "粘贴的文件", + "remove": "移除" }, "show": { "errors": { diff --git a/packages/frontend/src/lib/ui/PastedFilesPreview.svelte b/packages/frontend/src/lib/ui/PastedFilesPreview.svelte new file mode 100644 index 0000000..ba72838 --- /dev/null +++ b/packages/frontend/src/lib/ui/PastedFilesPreview.svelte @@ -0,0 +1,155 @@ + + +{#if files.length > 0} +
+

{$t('home.pasted_files')}

+
+ {#each files as entry, index} +
+ {#if isImage(entry.type)} + {entry.name} + {:else} +
+
+ {entry.name.split('.').pop()?.toUpperCase() || entry.type} +
+
+ {/if} +
{entry.name}
+
+ +
+
+ {/each} +
+
+{/if} + + diff --git a/packages/frontend/src/lib/ui/ShowNote.svelte b/packages/frontend/src/lib/ui/ShowNote.svelte index 61eb1ec..1a3bee0 100644 --- a/packages/frontend/src/lib/ui/ShowNote.svelte +++ b/packages/frontend/src/lib/ui/ShowNote.svelte @@ -80,6 +80,15 @@ {file.type} - {prettyBytes(file.size)} + {#if file.type.startsWith('image/')} + {#key file.name} + {file.name} + {/key} + {/if} {/each} {/if} @@ -130,4 +139,13 @@ margin-bottom: 0.5rem; word-wrap: break-word; } + + .preview { + display: block; + max-width: 100%; + max-height: 60vh; + margin-bottom: 0.5rem; + border-radius: 0.25rem; + border: 2px solid var(--ui-bg-1); + } diff --git a/packages/frontend/src/lib/views/Create.svelte b/packages/frontend/src/lib/views/Create.svelte index a79aad6..983120b 100644 --- a/packages/frontend/src/lib/views/Create.svelte +++ b/packages/frontend/src/lib/views/Create.svelte @@ -10,6 +10,7 @@ import FileUpload from '$lib/ui/FileUpload.svelte' import Loader from '$lib/ui/Loader.svelte' import MaxSize from '$lib/ui/MaxSize.svelte' + import PastedFilesPreview from '$lib/ui/PastedFilesPreview.svelte' import Result, { type NoteResult } from '$lib/ui/NoteResult.svelte' import Switch from '$lib/ui/Switch.svelte' import TextArea from '$lib/ui/TextArea.svelte' @@ -29,6 +30,7 @@ let customPassword: string | null = $state(null) let description = $state('') let loading: string | null = $state(null) + let isPasting = $state(false) $effect(() => { if (!advanced) { @@ -57,6 +59,62 @@ } }) + async function handlePaste(e: ClipboardEvent) { + e.preventDefault() + const data = e.clipboardData + if (!data) return + + const raw: File[] = [] + + if (data.files.length) { + raw.push(...Array.from(data.files)) + } + + for (let i = 0; i < data.items.length; i++) { + const item = data.items[i] + if (item.kind === 'file') { + const file = item.getAsFile() + if (file) raw.push(file) + } + } + + if (raw.length === 0) return + + const seen = new Set() + const pasted: File[] = [] + for (const f of raw) { + const key = `${f.name}|${f.size}` + if (!seen.has(key)) { + seen.add(key) + pasted.push(f) + } + } + + isPasting = true + + const dtos: FileDTO[] = await Promise.all( + pasted.map(async (file) => { + const ext = file.name.includes('.') ? '' : `.${file.type.split('/')[1] || 'bin'}` + const name = + file.name || `pasted-file-${Date.now()}-${Math.round(Math.random() * 1000)}${ext}` + const renamed = new File([file], name, { type: file.type }) + return { + name: renamed.name, + size: renamed.size, + type: renamed.type, + contents: new Uint8Array(await renamed.arrayBuffer()), + } + }) + ) + + if (dtos.length > 0) { + if (!isFile) isFile = true + files = [...files, ...dtos] + } + + isPasting = false + } + class EmptyContentError extends Error {} async function submit(e: SubmitEvent) { @@ -110,18 +168,28 @@

{@html $status?.theme_text || $t('home.intro')}

-
+
- {#if isFile} - - {:else} -