diff --git a/apps/client/src/stylesheets/theme-next/shell.css b/apps/client/src/stylesheets/theme-next/shell.css index 5920ef0e9..3fa23b05a 100644 --- a/apps/client/src/stylesheets/theme-next/shell.css +++ b/apps/client/src/stylesheets/theme-next/shell.css @@ -1259,6 +1259,12 @@ body.layout-horizontal #rest-pane > .classic-toolbar-widget { #center-pane .note-split { padding-top: 2px; background-color: var(--note-split-background-color, var(--main-background-color)); + transition: border-color 250ms ease-in; + border: 1px solid transparent; + + &.active { + border-color: var(--link-selection-outline-color); + } } body:not(.background-effects) #center-pane .note-split { diff --git a/apps/client/src/translations/cn/translation.json b/apps/client/src/translations/cn/translation.json index 1956553e1..f4bbe4a35 100644 --- a/apps/client/src/translations/cn/translation.json +++ b/apps/client/src/translations/cn/translation.json @@ -765,7 +765,14 @@ "note_icon": { "change_note_icon": "更改笔记图标", "search": "搜索:", - "reset-default": "重置为默认图标" + "reset-default": "重置为默认图标", + "search_placeholder_other": "在 {{count}} 个图标包中搜索 {{number}} 个图标", + "search_placeholder_filtered": "在 {{name}} 中搜索 {{number}} 个图标", + "filter": "筛选", + "filter-none": "所有图标", + "filter-default": "默认图标", + "icon_tooltip": "{{name}}\n图标包:{{iconPack}}", + "no_results": "没有找到图标。" }, "basic_properties": { "note_type": "笔记类型", @@ -1445,7 +1452,7 @@ "will_be_deleted_in": "此附件将在 {{time}} 后自动删除", "will_be_deleted_soon": "该附件在不久后将被自动删除", "deletion_reason": ",因为该附件未链接在笔记的内容中。为防止被删除,请将附件链接重新添加到内容中或将附件转换为笔记。", - "role_and_size": "角色:{{role}},大小:{{size}}", + "role_and_size": "角色:{{role}},大小:{{size}},文件类型:{{- mimeType}}", "link_copied": "附件链接已复制到剪贴板。", "unrecognized_role": "无法识别的附件角色 '{{role}}'。" }, diff --git a/apps/client/src/translations/it/translation.json b/apps/client/src/translations/it/translation.json index d05f06be0..d01a7882b 100644 --- a/apps/client/src/translations/it/translation.json +++ b/apps/client/src/translations/it/translation.json @@ -16,13 +16,22 @@ }, "bundle-error": { "title": "Non si è riusciti a caricare uno script personalizzato", - "message": "Lo script della nota con ID \"{{id}}\", dal titolo \"{{title}}\" non è stato inizializzato a causa di:\n\n{{message}}" + "message": "Impossibile eseguire lo script a causa di:\n\n{{message}}" }, "widget-error": { "title": "Impossibile inizializzare un widget", "message-custom": "Il widget personalizzato dalla nota con ID “{{id}}”, intitolato “{{title}}”, non è stato possibile inizializzare a causa di:\n\n{{message}}", "message-unknown": "Un widget sconosciuto non è stato inizializzato a causa di:\n\n{{message}}" - } + }, + "widget-list-error": { + "title": "Impossibile ottenere l'elenco dei widget dal server" + }, + "widget-render-error": { + "title": "Impossibile eseguire il rendering di un widget React personalizzato" + }, + "widget-missing-parent": "Il widget personalizzato non ha la proprietà obbligatoria '{{property}}' definita.\n\nSe questo script deve essere eseguito senza un elemento dell'interfaccia utente, utilizzare invece '#run=frontendStartup'.", + "open-script-note": "Apri script note", + "scripting-error": "Errore script personalizzato: {{title}}" }, "add_link": { "add_link": "Aggiungi un collegamento", @@ -1334,7 +1343,16 @@ "note_icon": { "change_note_icon": "Cambia icona nota", "search": "Ricerca:", - "reset-default": "Ripristina l'icona predefinita" + "reset-default": "Ripristina l'icona predefinita", + "search_placeholder_one": "Cerca {{number}} icona in {{count}} pacchetto", + "search_placeholder_many": "Cerca {{number}} icone in {{count}} pacchetti", + "search_placeholder_other": "Cerca {{number}} icone in {{count}} pacchetti", + "search_placeholder_filtered": "Cerca {{number}} icone in {{name}}", + "filter": "Filtro", + "filter-none": "Tutte le icone", + "filter-default": "Icone predefinite", + "icon_tooltip": "{{name}}\nPacchetto icone: {{iconPack}}", + "no_results": "Nessuna icona trovata." }, "basic_properties": { "note_type": "Tipo di nota", @@ -1792,7 +1810,7 @@ "will_be_deleted_in": "Questo allegato verrà eliminato automaticamente tra {{time}}", "will_be_deleted_soon": "Questo allegato verrà eliminato automaticamente a breve", "deletion_reason": ", perché l'allegato non è collegato al contenuto della nota. Per impedirne l'eliminazione, aggiungi nuovamente il collegamento all'allegato nel contenuto o converti l'allegato in nota.", - "role_and_size": "Ruolo: {{role}}, Dimensione: {{size}}", + "role_and_size": "Ruolo: {{role}}, dimensione: {{size}}, MIME: {{- mimeType}}", "link_copied": "Link all'allegato copiato negli appunti.", "unrecognized_role": "Ruolo di allegato non riconosciuto '{{role}}'." }, @@ -1886,7 +1904,13 @@ "note_detail": { "could_not_find_typewidget": "Impossibile trovare typeWidget per il tipo '{{type}}'", "printing": "Stampa in corso...", - "printing_pdf": "Esportazione in PDF in corso..." + "printing_pdf": "Esportazione in PDF in corso...", + "print_report_title": "Stampa rapporto", + "print_report_collection_content_one": "{{count}} la note nella raccolta non può essere stampata perché non è supportata o è protetta.", + "print_report_collection_content_many": "{{count}} le note nella raccolta non possono essere stampate perché non sono supportate o sono protette.", + "print_report_collection_content_other": "{{count}} le note nella raccolta non possono essere stampate perché non sono supportate o sono protette.", + "print_report_collection_details_button": "Vedi dettagli", + "print_report_collection_details_ignored_notes": "Note ignorate" }, "note_title": { "placeholder": "scrivi qui il titolo della nota...", @@ -1896,7 +1920,8 @@ "note_type_switcher_others": "Altro tipo di nota", "note_type_switcher_templates": "Modello", "note_type_switcher_collection": "Collezione", - "edited_notes": "Note modificate" + "edited_notes": "Note modificate in questo giorno", + "promoted_attributes": "Attributi promossi" }, "search_result": { "no_notes_found": "Non sono state trovate note per i parametri di ricerca specificati.", diff --git a/apps/client/src/translations/ja/translation.json b/apps/client/src/translations/ja/translation.json index 8f5ee621e..1851128ed 100644 --- a/apps/client/src/translations/ja/translation.json +++ b/apps/client/src/translations/ja/translation.json @@ -153,7 +153,14 @@ "note_icon": { "change_note_icon": "ノートアイコンの変更", "search": "検索:", - "reset-default": "アイコンをデフォルトに戻す" + "reset-default": "アイコンをデフォルトに戻す", + "search_placeholder_other": "{{count}} 個のパックから {{number}} 個のアイコンを検索", + "search_placeholder_filtered": "{{name}} で {{number}} 個のアイコンを検索", + "filter": "フィルター", + "filter-none": "すべてのアイコン", + "filter-default": "デフォルトアイコン", + "icon_tooltip": "{{name}}\nアイコンパック: {{iconPack}}", + "no_results": "アイコンが見つかりません。" }, "basic_properties": { "note_type": "ノートタイプ", @@ -2129,7 +2136,7 @@ "will_be_deleted_in": "この添付ファイルは {{time}} 後に自動的に削除されます", "will_be_deleted_soon": "この添付ファイルはすぐに自動的に削除されます", "deletion_reason": "、添付ファイルがノートのコンテンツにリンクされていないためです。削除されないようにするには、添付ファイルのリンクをコンテンツに再度追加するか、添付ファイルをノートに変換してください。", - "role_and_size": "ロール: {{role}},サイズ: {{size}}", + "role_and_size": "ロール: {{role}},サイズ: {{size}}, MIME: {{- mimeType}}", "link_copied": "添付ファイルのリンクをクリップボードにコピーしました。", "unrecognized_role": "添付ファイルのロール「{{role}}」は認識されません。" }, diff --git a/apps/client/src/translations/ru/translation.json b/apps/client/src/translations/ru/translation.json index 6bd20066a..81c12e9b2 100644 --- a/apps/client/src/translations/ru/translation.json +++ b/apps/client/src/translations/ru/translation.json @@ -1012,7 +1012,16 @@ "note_icon": { "search": "Поиск:", "change_note_icon": "Изменить иконку заметки", - "reset-default": "Сбросить к значку по умолчанию" + "reset-default": "Сбросить к значку по умолчанию", + "no_results": "Иконки не найдены.", + "icon_tooltip": "{{name}}\nНабор иконок: {{iconPack}}", + "filter-default": "Иконки по-умолчанию", + "filter-none": "Все иконки", + "filter": "Фильтр", + "search_placeholder_filtered": "Поиск {{number}} иконок в {{name}}", + "search_placeholder_one": "Поиск {{number}} иконки среди {{count}} наборов", + "search_placeholder_few": "Поиск {{number}} иконок среди {{count}} наборов", + "search_placeholder_many": "Поиск {{number}} иконок среди {{count}} наборов" }, "basic_properties": { "editable": "Изменяемое", @@ -2025,7 +2034,7 @@ "lost-websocket-connection-message": "Проверьте конфигурацию обратного прокси (например, nginx или Apache), чтобы убедиться, что соединения WebSocket должным образом разрешены и не заблокированы." }, "attachment_detail_2": { - "role_and_size": "Роль: {{role}}, Размер: {{size}}", + "role_and_size": "Роль: {{role}}, размер: {{size}}, MIME: {{- mimeType}}", "unrecognized_role": "Нераспознанная роль вложения '{{role}}'.", "link_copied": "Ссылка на вложение скопирована в буфер обмена.", "will_be_deleted_soon": "Это вложение скоро будет автоматически удалено", diff --git a/apps/client/src/widgets/containers/split_note_container.ts b/apps/client/src/widgets/containers/split_note_container.ts index 1cee46b73..c006775a4 100644 --- a/apps/client/src/widgets/containers/split_note_container.ts +++ b/apps/client/src/widgets/containers/split_note_container.ts @@ -1,10 +1,11 @@ -import FlexContainer from "./flex_container.js"; import appContext, { type CommandData, type CommandListenerData, type EventData, type EventNames, type NoteSwitchedContext } from "../../components/app_context.js"; -import type BasicWidget from "../basic_widget.js"; import Component from "../../components/component.js"; +import NoteContext from "../../components/note_context.js"; import splitService from "../../services/resizer.js"; import { isMobile } from "../../services/utils.js"; -import NoteContext from "../../components/note_context.js"; +import type BasicWidget from "../basic_widget.js"; +import NoteContextAwareWidget from "../note_context_aware_widget.js"; +import FlexContainer from "./flex_container.js"; interface SplitNoteWidget extends BasicWidget { hasBeenAlreadyShown?: boolean; @@ -74,7 +75,7 @@ export default class SplitNoteContainer extends FlexContainer { const subContexts = activeContext.getSubContexts(); - let noteContext: NoteContext | undefined = undefined; + let noteContext: NoteContext | undefined; if (isMobile() && subContexts.length > 1) { noteContext = subContexts.find(s => s.ntxId !== ntxId); } @@ -201,6 +202,11 @@ export default class SplitNoteContainer extends FlexContainer { async refresh() { this.toggleExt(true); + + // Mark the active note context. + for (const child of this.children as NoteContextAwareWidget[]) { + child.$widget.toggleClass("active", !!child.noteContext?.isActive()); + } } toggleInt(show: boolean) {} // not needed @@ -239,16 +245,16 @@ export default class SplitNoteContainer extends FlexContainer { widget.hasBeenAlreadyShown = true; return [widget.handleEvent("noteSwitched", noteSwitchedContext), this.refreshNotShown(noteSwitchedContext)]; - } else { - return Promise.resolve(); } + return Promise.resolve(); + } if (name === "activeContextChanged") { return this.refreshNotShown(data as EventData<"activeContextChanged">); - } else { - return super.handleEventInChildren(name, data); } + return super.handleEventInChildren(name, data); + } refreshNotShown(data: NoteSwitchedContext | EventData<"activeContextChanged">) { diff --git a/apps/client/src/widgets/type_widgets/File.tsx b/apps/client/src/widgets/type_widgets/File.tsx index b720d6a0d..a26bb0987 100644 --- a/apps/client/src/widgets/type_widgets/File.tsx +++ b/apps/client/src/widgets/type_widgets/File.tsx @@ -10,13 +10,13 @@ import { TypeWidgetProps } from "./type_widget"; const TEXT_MAX_NUM_CHARS = 5000; -export default function FileTypeWidget({ note, parentComponent }: TypeWidgetProps) { +export default function FileTypeWidget({ note, parentComponent, noteContext }: TypeWidgetProps) { const blob = useNoteBlob(note, parentComponent?.componentId); if (blob?.content) { return ; } else if (note.mime === "application/pdf") { - return ; + return ; } else if (note.mime.startsWith("video/")) { return ; } else if (note.mime.startsWith("audio/")) { diff --git a/apps/client/src/widgets/type_widgets/file/Pdf.tsx b/apps/client/src/widgets/type_widgets/file/Pdf.tsx index f254e13c4..1c4e24573 100644 --- a/apps/client/src/widgets/type_widgets/file/Pdf.tsx +++ b/apps/client/src/widgets/type_widgets/file/Pdf.tsx @@ -1,6 +1,7 @@ import { RefObject } from "preact"; import { useCallback, useEffect, useRef } from "preact/hooks"; +import appContext from "../../../components/app_context"; import FBlob from "../../../entities/fblob"; import FNote from "../../../entities/fnote"; import server from "../../../services/server"; @@ -14,10 +15,11 @@ const VARIABLE_WHITELIST = new Set([ "main-text-color" ]); -export default function PdfPreview({ note, blob, componentId }: { +export default function PdfPreview({ note, blob, componentId, ntxId }: { note: FNote, blob: FBlob | null | undefined, componentId: string | undefined; + ntxId: string | null | undefined; }) { const iframeRef = useRef(null); const { onLoad } = useStyleInjection(iframeRef); @@ -49,8 +51,28 @@ export default function PdfPreview({ note, blob, componentId }: { } }, [ blob ]); + // Trigger focus when iframe content is clicked (iframe focus doesn't bubble) + useEffect(() => { + const iframe = iframeRef.current; + if (!iframe) return; + + const handleIframeClick = () => { + if (ntxId) { + appContext.tabManager.activateNoteContext(ntxId); + } + }; + + // Listen for clicks on the iframe's content window + const iframeDoc = iframe.contentWindow?.document; + if (iframeDoc) { + iframeDoc.addEventListener('click', handleIframeClick); + return () => iframeDoc.removeEventListener('click', handleIframeClick); + } + }, [ iframeRef.current?.contentWindow, ntxId ]); + return (historyConfig &&