diff --git a/.github/workflows/flutter-linux.yml b/.github/workflows/flutter-linux.yml
index a712d7eb..dd993135 100644
--- a/.github/workflows/flutter-linux.yml
+++ b/.github/workflows/flutter-linux.yml
@@ -111,7 +111,7 @@ jobs:
- name: Run real WebKit, PlantUML, Mermaid, OpenAPI, and D2 smoke tests
run: |
xvfb-run -a -s '-screen 0 1280x1024x24' \
- env WEBKIT_DISABLE_COMPOSITING_MODE=1 LIBGL_ALWAYS_SOFTWARE=1 \
+ env GDK_BACKEND=x11 LIBGL_ALWAYS_SOFTWARE=1 \
/usr/bin/python3 -u tools/visualization_smoke.py \
--assets build/linux/x64/release/bundle/share/busymark/visualization \
--d2 build/linux/x64/release/bundle/libexec/busymark/d2 \
@@ -123,7 +123,7 @@ jobs:
report="${RUNNER_TEMP}/visualization-release-x11.json"
timeout --signal=TERM 300s \
xvfb-run -a -s '-screen 0 1280x1024x24' \
- env BUSYMARK_RELEASE_SMOKE=1 WEBKIT_DISABLE_COMPOSITING_MODE=1 \
+ env GDK_BACKEND=x11 BUSYMARK_RELEASE_SMOKE=1 \
LIBGL_ALWAYS_SOFTWARE=1 \
build/linux/x64/release/bundle/busymark \
--visualization-release-smoke="$report"
@@ -151,8 +151,7 @@ jobs:
done
test -S "$runtime_dir/wayland-99"
XDG_RUNTIME_DIR="$runtime_dir" WAYLAND_DISPLAY=wayland-99 \
- GDK_BACKEND=wayland WEBKIT_DISABLE_COMPOSITING_MODE=1 \
- LIBGL_ALWAYS_SOFTWARE=1 \
+ GDK_BACKEND=wayland LIBGL_ALWAYS_SOFTWARE=1 \
/usr/bin/python3 -u tools/visualization_smoke.py \
--assets build/linux/x64/release/bundle/share/busymark/visualization \
--d2 build/linux/x64/release/bundle/libexec/busymark/d2 \
@@ -161,7 +160,7 @@ jobs:
report="${RUNNER_TEMP}/visualization-release-wayland.json"
XDG_RUNTIME_DIR="$runtime_dir" WAYLAND_DISPLAY=wayland-99 \
GDK_BACKEND=wayland BUSYMARK_RELEASE_SMOKE=1 \
- WEBKIT_DISABLE_COMPOSITING_MODE=1 LIBGL_ALWAYS_SOFTWARE=1 \
+ LIBGL_ALWAYS_SOFTWARE=1 \
timeout --signal=TERM 300s \
build/linux/x64/release/bundle/busymark \
--visualization-release-smoke="$report"
@@ -228,7 +227,7 @@ jobs:
mkdir -p "$(dirname "$report")"
timeout --signal=TERM 300s \
xvfb-run -a -s '-screen 0 1280x1024x24' \
- env BUSYMARK_RELEASE_SMOKE=1 WEBKIT_DISABLE_COMPOSITING_MODE=1 \
+ env GDK_BACKEND=x11 BUSYMARK_RELEASE_SMOKE=1 \
LIBGL_ALWAYS_SOFTWARE=1 snap run busymark \
--visualization-release-smoke="$report"
python3 -c 'import json,sys; report=json.load(open(sys.argv[1], encoding="utf-8")); assert report["ok"], report' "$report"
@@ -257,7 +256,7 @@ jobs:
report="$HOME/snap/busymark/common/visualization-release-wayland.json"
XDG_RUNTIME_DIR="$runtime_dir" WAYLAND_DISPLAY=wayland-99 \
GDK_BACKEND=wayland BUSYMARK_RELEASE_SMOKE=1 \
- WEBKIT_DISABLE_COMPOSITING_MODE=1 LIBGL_ALWAYS_SOFTWARE=1 \
+ LIBGL_ALWAYS_SOFTWARE=1 \
timeout --signal=TERM 300s snap run busymark \
--visualization-release-smoke="$report"
python3 -c 'import json,sys; report=json.load(open(sys.argv[1], encoding="utf-8")); assert report["ok"], report' "$report"
diff --git a/README.md b/README.md
index 2326f36b..d1dafd74 100644
--- a/README.md
+++ b/README.md
@@ -14,13 +14,81 @@ Markdown and Writerside editor for Linux.
* **Markdown editing** — Source, Editor, Reading, and Split views with formatting tools, syntax highlighting, code folding, tables, images, links, code blocks, callouts, collapsible content, and document diagnostics.
* **Writerside projects** — Open and create Writerside-compatible projects; edit Markdown and XML topics; manage instances, tables of contents, reusable TOC libraries, and project structure.
-* **Project navigation and search** — Files, TOC, and Outline views, tabbed documents, command palette, keyboard shortcuts, document search and replace, reviewed workspace-wide replacement, and Markdown TOC generation.
+* **Project navigation and search** — Files, Topics, and Outline views, tabbed documents, command palette, keyboard shortcuts, document search and replace, reviewed workspace-wide replacement, and Markdown TOC generation.
* **Technical documentation** — Local rendering of Mermaid, PlantUML, D2, fenced OpenAPI specifications, and MathJax mathematical expressions.
* **PDF and HTML publishing** — Export Markdown documents and Writerside instances to configurable PDF or portable offline HTML, with controls for layout, typography, tables of contents, heading numbering, and HTML styling.
* **Git integration** — Review changes and diffs, stage and unstage files, commit, create and switch branches, fetch, pull, push, inspect file and project history, compare historical versions, and restore earlier file versions.
+* **Clipboard and Local History** — Reuse source, rich-text, and image fragments during the current session; compare and restore persistent on-device document revisions independently of Git.
* **AI-assisted editing** — Optional Ollama, OpenAI, and Gemini integration with explicit edit scope and shared context, proposal review before changes are applied, and AI-assisted Git commit-message drafting.
* **Workspace reliability** — Detect files changed outside BusyMark, recover unsaved documents, restore previous workspace sessions, manage remote-image permissions, and protect Git operations behind workspace trust.
+## Installation
+
+Install BusyMark from the Snap Store:
+
+```bash
+sudo snap install busymark --beta
+```
+
+The Snap uses strict confinement. See [Snap confinement](docs/snap-confinement.md)
+for filesystem and Git-integration details.
+
+## Run from source
+
+BusyMark currently uses Flutter 3.47.2. Install the Linux packages required by
+Flutter and BusyMark's CMake configuration:
+
+```bash
+sudo apt-get install -y \
+ clang \
+ cmake \
+ curl \
+ fonts-noto-core \
+ fonts-noto-mono \
+ libgtk-3-dev \
+ libhandy-1-dev \
+ libsecret-1-dev \
+ libwebkit2gtk-4.1-dev \
+ ninja-build \
+ pkg-config \
+ xz-utils
+```
+
+Node.js 22 or newer with npm is also required to build the bundled web
+components.
+
+Prepare and run the application:
+
+```bash
+flutter doctor
+flutter pub get
+flutter run -d linux
+```
+
+Pass a Markdown file or documentation directory after `--` to open it at
+startup:
+
+```bash
+flutter run -d linux -- /absolute/path/to/document.md
+flutter run -d linux -- /absolute/path/to/documentation
+```
+
+Build and run the standard checks with:
+
+```bash
+flutter build linux
+flutter analyze
+flutter test
+```
+
+The build assembles BusyMark's runtime components. Packaged users do not need
+separate Typst, Java, Node.js, or Chromium installations.
+
+The CI workflow installs additional packages for headless X11/Wayland, browser,
+PDF, and sandbox verification. Those packages are not required for an ordinary
+local build; see [the Linux workflow](.github/workflows/flutter-linux.yml) when
+reproducing release verification.
+
## Screenshots
@@ -74,6 +142,11 @@ Markdown and Writerside editor for Linux.
+## Documentation
+
+The [documentation index](docs/README.md) lists user guides and the separate
+developer notes.
+
## Markdown and Writerside
BusyMark opens Markdown files using `.md` and `.markdown` extensions and can work with ordinary documentation folders.
@@ -82,11 +155,18 @@ Writerside-compatible projects are recognized through `writerside.cfg` and the o
More information:
+* [Writerside support and limitations](docs/writerside-support.md)
* [Writerside instances](docs/writerside-instances.md)
* [Writerside videos](docs/videos.md)
* [Admonitions](docs/admonitions.md)
* [Collapsible elements](docs/collapsible-elements.md)
+## Search and replace
+
+BusyMark supports active-document replacement and reviewed workspace-wide
+replacement. See [Search and replace](docs/search-and-replace.md) for search
+options, scope, preview behavior, and stale-result handling.
+
## Diagrams and mathematics
BusyMark renders Mermaid, PlantUML, D2, and fenced OpenAPI content locally. Mathematical expressions are rendered with the bundled MathJax environment.
@@ -116,64 +196,12 @@ BusyMark supports Ollama, OpenAI, and Google Gemini. Proposed edits are presente
See [AI editing](docs/local-ai.md) for configuration, privacy, provider behavior, and security information.
-## Installation
-
-Install BusyMark from the Snap Store:
-
-```bash
-sudo snap install busymark --beta
-```
-
-The Snap uses strict confinement. See [Snap confinement notes](docs/snap-confinement.md) for details about filesystem and Git integration.
-
-## Run from source
-
-BusyMark currently uses Flutter 3.47.0.
-
-Install the required Linux development packages:
+## History and recovery
-```bash
-sudo apt-get install \
- curl \
- libhandy-1-dev \
- xz-utils \
- libwebkit2gtk-4.1-dev \
- fonts-noto-core \
- fonts-noto-mono
-```
-
-Node.js 22 or newer with npm is also required when building the bundled web components.
-
-Prepare and run the application:
-
-```bash
-flutter doctor
-flutter pub get
-flutter run -d linux
-```
-
-A Markdown file or documentation folder can be opened directly from the command line:
-
-```bash
-flutter run -d linux
-```
-
-## Build
-
-Build the Linux desktop application with:
-
-```bash
-flutter build linux
-```
-
-The build assembles the runtime components required by BusyMark. Packaged users do not need separate Typst, Java, Node.js, or Chromium installations.
-
-## Test
-
-```bash
-flutter analyze
-flutter test
-```
+See [Clipboard History and Local History](docs/history.md) for collection scope,
+limits, retention, restoration, exclusions, and how these tools differ from
+undo, crash recovery, and Git. Implementation and verification details are in
+[the history implementation notes](docs/development/history.md).
## Contributing
diff --git a/docs/README.md b/docs/README.md
new file mode 100644
index 00000000..674884f6
--- /dev/null
+++ b/docs/README.md
@@ -0,0 +1,39 @@
+# BusyMark documentation
+
+This directory contains user guides for BusyMark and a separate set of notes
+for contributors. Start with the workflow you need; syntax and limitation pages
+are intentionally independent so they can be referenced directly.
+
+## Everyday workflows
+
+- [Search and replace](search-and-replace.md)
+- [AI editing](local-ai.md)
+- [Clipboard History and Local History](history.md)
+- [Snap confinement](snap-confinement.md)
+
+## Writerside
+
+- [Supported content and limitations](writerside-support.md)
+- [Instances and TOC libraries](writerside-instances.md)
+- [Admonitions](admonitions.md)
+- [Collapsible elements](collapsible-elements.md)
+- [Videos](videos.md)
+
+## Technical content
+
+- [Mathematical expressions](math.md)
+- [Offline visualizations](visualizations.md)
+
+## Export
+
+- [PDF export](pdf-export.md)
+- [Writerside PDF export](writerside-pdf-export.md)
+- [HTML export](html-export.md)
+
+## Developer notes
+
+- [AI implementation and qualification](development/ai.md)
+- [Export verification](development/export-verification.md)
+- [History implementation](development/history.md)
+- [Visualization implementation and verification](development/visualizations.md)
+- [Writerside verification](development/writerside-verification.md)
diff --git a/docs/clipboard-fix-plan.md b/docs/clipboard-fix-plan.md
deleted file mode 100644
index c40bbf0e..00000000
--- a/docs/clipboard-fix-plan.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Editor clipboard fix
-
-Status: implemented for the Linux desktop host.
-
-Editor copy previously published only plain text. Formatting stayed in a private
-cache inside one Editor widget, so a fresh Editor or another application could
-not retrieve it. Copy now publishes three representations in one GTK clipboard
-operation, and ordinary paste uses the richest supported representation.
-
-| Representation | Purpose |
-| --- | --- |
-| `application/x-busymark-fragment+json` | Preserve BusyMark structure, inline formatting, task states, tables, and Writerside content between instances. |
-| `text/html` | Preserve headings, emphasis, links, lists, tables, and code in compatible external applications. Tasks have visible checked/unchecked symbols. |
-| Standard GTK text targets | Support text-only destinations and Paste without formatting. |
-
-## Implementation
-
-- `rich_clipboard_service.dart` serializes competing writes and reads the system
- clipboard without a private formatting cache. A native write publishes all
- formats together; it is never followed by a plain write that would erase them.
-- `rich_clipboard_host.cc` owns immutable payload bytes independently of the
- originating widget. Reads are asynchronous and reject data from an owner that
- changes during the read. Transfers are limited to 16 MiB. The host advertises
- all formats for clipboard-manager storage using
- [`gtk_clipboard_set_can_store`](https://docs.gtk.org/gtk3/method.Clipboard.set_can_store.html).
- GTK's existing application shutdown performs the storage request.
-- `wysiwyg_clipboard_fragment.dart` uses a versioned, bounded, validated JSON
- schema. Block IDs are regenerated during insertion. Relative links are rebased
- and resolved local media paths are carried between documents/projects.
-- `wysiwyg_clipboard_html.dart` exports semantic HTML directly from the copied
- blocks. On import it normalizes supported CSS emphasis and checkbox state,
- strips executable markup and unsafe URLs, and uses the existing HTML adapter.
- It does not change the document HTML policy or write publication assets.
-- Editor copy snapshots the selection before asynchronous work. Cut deletes only
- after a successful write. Paste and cut recheck the file, document generation,
- active block/cell, and selection before changing the document. Nested children
- are copied once; complete blockquotes retain their container.
-- Insertion preserves inline nodes, including image references, and supports
- partial selections and one-step undo/redo. Table cells retain supported inline
- styles while block boundaries become spaces. Cells displayed as math source
- retain their existing text insertion behavior.
-
-## Validation
-
-- 238 editor/model regression tests pass, including fresh Editor instances,
- same-Editor cut/paste, partial selections, nested lists, blockquotes, tasks,
- tables, code, Writerside, images, RTL, math, undo/redo, malformed fragments,
- serialized writes, failed cut, delayed paste, and a later plain-text copy.
-- A document-wide selection has its own right-click menu for Cut, Copy, Paste,
- Select all, and Refine with AI when available. The selected blocks stay active
- while the menu opens, including when the pointer is over a table cell.
-- Both reported files, `issues.md` and `writerside-support.md`, passed a read-only
- codec and insertion probe. Heading, task, table, blockquote, and code content
- survived; exported HTML also imported successfully.
-- Separate compiled BusyMark processes exchanged JSON, HTML, and plain text on
- isolated X11 and headless GNOME Wayland displays. A subsequent Flutter plain
- copy cleared the rich formats.
-- LibreOffice Writer pasted a Heading 1 paragraph, bold text, and visible checked
- and unchecked task states from the compiled Linux implementation.
-
-The native probe entrypoint is `tools/clipboard_smoke.dart`. Build it with
-`flutter build linux --debug -t tools/clipboard_smoke.dart`, then run `write`,
-`read`, or `plain` with the same temporary output directory on an isolated display.
-Keep `write` running until the reads complete; creating a `stop` file in that
-directory closes it. Rebuild with the default entrypoint afterward.
-
-## Interoperability limits
-
-Clipboard persistence after the source process exits depends on the desktop's
-clipboard manager and the formats it retains. That exit scenario remains a manual
-check. Source-widget disposal does not discard clipboard payloads. External
-applications choose which offered format to consume; plain-text applications
-will still paste plain text. Writerside-specific structures remain editable
-through the BusyMark format and use readable HTML fallbacks externally. Local
-media references point to existing files and do not bundle their bytes.
-
-Hosts without the Linux method channel currently fall back to plain text.
diff --git a/docs/development/ai.md b/docs/development/ai.md
new file mode 100644
index 00000000..9d6cd223
--- /dev/null
+++ b/docs/development/ai.md
@@ -0,0 +1,62 @@
+# AI implementation and qualification
+
+This note describes the engineering boundaries behind BusyMark's user-facing
+[AI editing workflow](../local-ai.md). Keep provider setup and editing
+instructions in the user guide; keep transport, policy, and release checks here.
+
+## Components and invariants
+
+The provider-neutral implementation lives under `lib/src/ai/`:
+
+- provider adapters normalize OpenAI and Gemini SSE and Ollama NDJSON into one
+ typed event stream;
+- `AiCoordinator` owns provider-local model fallback, retry, concurrency,
+ cancellation, and latest-request-wins behavior;
+- `AiMarkdownEditResolver` converts the selected target and context into exact
+ source ranges and safe block insertions;
+- `AiPromptBuilder`, `AiPolicy`, and `AiMarkdownGuard` enforce request budgets
+ and validate the complete candidate document independently of the provider;
+- the Linux credential host stores cloud keys with libsecret without opening
+ the global Secret Service collection.
+
+Routing and retries never cross provider boundaries. Transient `408`, `409`,
+`429`, and selected `5xx` responses may be retried before output is exposed.
+`Retry-After` is honored, with bounded exponential backoff and jitter. A request
+is never restarted after text has streamed because doing so could duplicate
+output.
+
+Input, prompt, instruction, output-token, generated-byte, transport-byte, and
+absolute-duration limits are enforced separately. At most two generations run
+concurrently. A newer request for the same target cancels the older request.
+
+## Deterministic checks
+
+The automated tests cover fragmented and malformed streams, redirects, response
+bounds, provider isolation, fallback, cancellation, deadlines, retries,
+credential redaction, context budgets, Markdown invariants, Source and Editor
+application paths, commit-message validation, and packaging.
+
+Run the normal repository checks first:
+
+```bash
+flutter analyze
+flutter test
+```
+
+## Local Ollama qualification
+
+Release qualification must exercise each exact provider/model combination that
+will be supported. Cloud runs require an intentionally supplied test credential;
+CI must not enable them implicitly. Generated prose is probabilistic, so review
+task quality as well as structural validity.
+
+The maintained local corpus covers all five edit targets, different context
+choices, complete-document editing, and staged-diff commit-message generation:
+
+```bash
+OLLAMA_NO_CLOUD=1 ollama serve
+dart run tools/ai_ollama_qualification.dart --model
+```
+
+The command prints proposals for human review and exits unsuccessfully when a
+request fails or a structural check rejects the result.
diff --git a/docs/development/export-verification.md b/docs/development/export-verification.md
new file mode 100644
index 00000000..ffca9676
--- /dev/null
+++ b/docs/development/export-verification.md
@@ -0,0 +1,83 @@
+# Export verification
+
+This is the maintained verification procedure for BusyMark's PDF and HTML
+export paths. User-facing settings and limitations are documented in
+[PDF export](../pdf-export.md), [HTML export](../html-export.md), and
+[Writerside PDF export](../writerside-pdf-export.md).
+
+## Implementation map
+
+- `lib/src/export/export_options.dart` defines validated, serializable options
+ shared by the export dialogs.
+- `lib/src/export/export_options_editor.dart` provides the Markdown and
+ Writerside settings editors.
+- `lib/src/export/typst_payload_builder.dart`,
+ `lib/src/export/markdown_export_mapper.dart`, and `assets/export/markdown.typ`
+ implement the native PDF model and layout.
+- `lib/src/export/html_document_writer.dart`,
+ `lib/src/export/html_export_styles.dart`, and
+ `lib/src/export/html_export_assets.dart` implement offline HTML output.
+- `lib/src/visualization/visualization_release_smoke.dart` exercises the bundled
+ visualization, math, PDF, and HTML product paths through the release binary.
+
+## Repository checks
+
+Use the repository-pinned Flutter and Dart versions:
+
+```bash
+flutter gen-l10n
+dart format --set-exit-if-changed .
+flutter analyze
+flutter test
+flutter build linux --release
+```
+
+The tests cover option parsing and persistence, dialog behavior, output bounds,
+metadata, TOC hierarchy and numbering, CSS validation, embedded and external
+assets, math and diagram rendering, Writerside resolution, cancellation, and
+preservation of existing output after failure.
+
+## Release product-path smoke
+
+The release-only smoke entry point is gated by `BUSYMARK_RELEASE_SMOKE=1`. Write
+artifacts to a fresh temporary directory:
+
+```bash
+verification_dir="$(mktemp -d)"
+report="$verification_dir/report.json"
+BUSYMARK_RELEASE_SMOKE=1 \
+ xvfb-run -a env WEBKIT_DISABLE_COMPOSITING_MODE=1 \
+ LIBGL_ALWAYS_SOFTWARE=1 build/linux/x64/release/bundle/busymark \
+ --visualization-release-smoke="$report"
+```
+
+Require a successful exit, `"ok": true` in `report.json`, non-empty PDF output,
+and the expected Markdown and Writerside HTML directories. Retain artifacts only
+when they are needed to diagnose a failure; temporary paths are not release
+evidence by themselves.
+
+## Relocated browser verification
+
+`tools/verify_html_export_browser.py` copies the generated HTML away from its
+source location, opens it through both `file:` and a local HTTP server, and
+checks offline resources, navigation, anchors, print behavior, color schemes,
+custom CSS, CSP enforcement, and layout. It requires Selenium, Chromium or
+Chrome, Firefox with geckodriver, and `pdftotext`:
+
+```bash
+browser_output="$verification_dir/browser"
+python3 tools/verify_html_export_browser.py "$verification_dir" \
+ --output "$browser_output"
+```
+
+Review `browser-report.json` and representative screenshots in the output
+directory. No unexpected external request is acceptable. Browser checks do not
+replace a manual inspection of representative PDF and HTML documents, and they
+do not certify physical-printer or assistive-technology behavior.
+
+## CI environment
+
+The Linux workflow in `.github/workflows/flutter-linux.yml` is authoritative for
+native packages, environment flags, browser setup, X11 and Wayland runs, and the
+strict Snap verification. Keep ordinary build prerequisites in the main README;
+do not copy the larger CI-only dependency set into user installation steps.
diff --git a/docs/development/history.md b/docs/development/history.md
new file mode 100644
index 00000000..76d13d23
--- /dev/null
+++ b/docs/development/history.md
@@ -0,0 +1,144 @@
+# History implementation notes
+
+This document records the invariants behind the user-facing
+[Clipboard History and Local History](../history.md) features.
+
+## Clipboard publication and ownership
+
+`RichClipboardService` serializes clipboard reads and writes and owns the
+current BusyMark rich payload independently of `ClipboardHistoryController`.
+Each publication sends three interoperable representations in one Linux host
+operation:
+
+| Representation | Contract |
+| --- | --- |
+| `application/x-busymark-token` | Resolves immutable structured content only in the originating BusyMark process and session. |
+| `text/html` | Preserves supported semantic formatting for other applications. |
+| Standard GTK text targets | Supports text-only destinations and paste without formatting. |
+
+The token maps to a bounded in-memory payload; unknown or stale tokens are
+external data, and matching text is never proof of ownership. Clearing retained
+history does not invalidate the current rich clipboard.
+
+The GTK host owns immutable payload bytes, rejects a read if the clipboard owner
+changes during transfer, and limits transfers to 16 MiB. It advertises formats
+for clipboard-manager persistence. Hosts without the Linux method channel fall
+back to Flutter's plain-text clipboard.
+
+`WysiwygClipboardFragment` uses a bounded, versioned, validated JSON schema.
+Insertion regenerates block IDs, rebases relative links, and ingests retained
+local media through the destination document's asset path. `WysiwygClipboardHtml`
+exports semantic HTML and imports only supported structure, safe URLs, emphasis,
+and task state; executable markup is discarded. Unsupported input follows the
+existing plain-text fallback.
+
+Copy and cut snapshot the selection before asynchronous publication. Cut deletes
+only after a successful write and after revalidating the document, active target,
+and selection. Paste uses the same revalidation and normal one-step undo path.
+
+## Clipboard retention
+
+`ClipboardHistoryController` retains successful BusyMark copy/cut payloads and
+supported external content only after a successful paste. Payloads contain their
+origin metadata, available representations, a SHA-256 equivalence fingerprint,
+and byte accounting. The default policy is 100 entries, 64 MiB total payload,
+and 8 MiB of decoded thumbnails. Deduplication compares every meaningful
+representation.
+
+The insertion registry exposes only the latest mounted editable surface and uses
+identity-safe unregistering. An adapter must capture and revalidate its document,
+revision, mode, selection generation, and target lifetime around asynchronous
+work.
+
+## Local History capture and identity
+
+`LocalHistoryController` treats each `DocumentBuffer` as the source authority.
+Capture events are baseline, explicit save, automatic checkpoint, before reload,
+before discard, before restore, before delete, and observed external change.
+Autosave writes do not create their own history reason.
+
+Observation is serialized per buffer. The first edit starts a fixed checkpoint
+window; later edits replace the one pending full-source snapshot without moving
+the deadline. A flush is a queue barrier. Retryable storage failures retain only
+the latest pending source for that buffer and retry on the configured checkpoint
+cadence. Unsupported or over-limit content fails visibly without a retry loop.
+
+History identity is independent of widgets and temporary buffer IDs. Known file
+and directory moves remap paths while preserving earlier paths. The first save
+of an untitled document continues its lineage. Save As keeps the source lineage
+separate and starts or appends to the destination lineage, protecting an
+existing destination before publication.
+
+## Store and retention
+
+Production revisions live under `local_history` in the platform application
+support directory. Store format 1 uses an index, independently recoverable and
+checksummed full-source JSON records, and a tombstone journal. Record identifiers
+and paths are validated beneath the history root.
+
+Writes are serialized in process and protected by an exclusive cross-process
+file lock. A complete record is published on the same filesystem before its
+index entry. Recovery scans intact records and observes tombstones so retention
+and explicit clearing cannot resurrect removed content. Unknown future store
+versions fail explicitly. Retention runs after successful capture and protects
+the newly written or protective record.
+
+## Comparison and restore safety
+
+Local History scopes ordinary browsing to the active buffer. Scope, snapshot,
+and query generations prevent a delayed read or search from publishing into a
+new document scope. Store-wide document search is a temporary discovery mode
+for retained identities that no longer have an open file.
+
+`SourceComparisonInput` identifies immutable source versions. Comparison uses
+patience anchors, bounded local LCS, and intraline refinement in Dart UTF-16
+offsets. When an unanchored region exceeds the work bound, the comparison is
+marked simplified and exact region restore is disabled.
+
+A restore request owns the history document, revision, current buffer, source
+revision, source text, and selected range. It first persists a protective
+snapshot, then revalidates those values after every await and applies one
+`DocumentBuffer.edited` transaction. Cancellation of a required protective
+capture is a failed restore, not successful protection.
+
+Lifecycle operations flush accepted history work before close, workspace
+replacement, or shutdown. Clear invalidates affected queued work when the clear
+is accepted, so an older checkpoint cannot recreate erased revisions.
+
+## Verification
+
+Run the standard checks with the repository-pinned toolchain:
+
+```bash
+flutter analyze
+flutter test
+```
+
+The native clipboard probe verifies same-process rich-token resolution and
+cross-process HTML/plain-text interoperability:
+
+```bash
+flutter build linux --debug -t tools/clipboard_smoke.dart
+GDK_BACKEND=x11 build/linux/x64/debug/bundle/busymark \
+ roundtrip OUTPUT_DIRECTORY
+```
+
+For the inter-process case, run `write OUTPUT_DIRECTORY`, keep it running, and
+run `read OUTPUT_DIRECTORY` from a second process on the same display. Create
+`OUTPUT_DIRECTORY/stop` to close the writer, then run `plain OUTPUT_DIRECTORY`
+to confirm that an external plain-text owner does not inherit stale rich
+formats. Rebuild the default entry point afterward.
+
+The production-controller visual probe uses disposable workspaces:
+
+```bash
+flutter build linux --debug -t tools/history_visual_smoke.dart
+GDK_BACKEND=x11 build/linux/x64/debug/bundle/busymark \
+ markdown WORKSPACE PRIMARY_FILE DELETE_FILE OUTPUT_DIRECTORY
+GDK_BACKEND=x11 build/linux/x64/debug/bundle/busymark \
+ writerside WORKSPACE PRIMARY_FILE - OUTPUT_DIRECTORY
+```
+
+Under `xvfb-run`, set `GDK_BACKEND=x11` explicitly so an inherited
+`WAYLAND_DISPLAY` cannot select an unrelated Wayland connection. Clipboard
+behavior under Wayland requires a compositor-driven run with real input focus.
diff --git a/docs/development/visualizations.md b/docs/development/visualizations.md
new file mode 100644
index 00000000..bd821752
--- /dev/null
+++ b/docs/development/visualizations.md
@@ -0,0 +1,97 @@
+# Visualization implementation and verification
+
+This note covers the engineering behind [offline visualizations](../visualizations.md).
+User syntax, behavior, and limitations belong in that guide.
+
+## Architecture
+
+`lib/src/visualization/` owns renderer contracts, typed results, diagnostics,
+revision cancellation, scheduling, generated-SVG normalization, OpenAPI
+dependency resolution, and bounded memory and disk caches. Cache keys include
+the engine and sanitizer versions, source, theme, render profile, options, and
+hashes of local dependencies.
+
+The Linux runner exposes a first-party platform channel backed by WebKitGTK 4.1.
+One reusable hidden render view handles Mermaid, PlantUML, OpenAPI parsing, SVG
+rasterization, and MathJax. BusyMark-owned windows are created only for the full
+interactive OpenAPI reference. Inline previews are Flutter SVG or PNG widgets,
+not embedded live browser views.
+
+The WebKit host uses an ephemeral context and an allow-listed private resource
+scheme. It disables storage, cookies, media, WebRTC, developer tools, popups,
+permissions, context menus, and unapproved navigation. Its content security
+policy blocks networking, frames, objects, forms, plugins, and remote fonts.
+Engine SVG is treated as untrusted and normalized before Flutter or Typst sees
+it. The WebKit subprocess sandbox remains enabled for ordinary Linux packages
+and the strict Snap.
+
+D2 runs its pinned Linux executable directly, never through a shell. Source,
+time, output, dimensions, and environment are bounded, and each execution uses
+a fresh temporary directory. Safe SVG remains vector output; unsupported CSS,
+embedded fonts, conflicting cascade behavior, animation, or `foreignObject`
+content is sanitized and rasterized locally.
+
+OpenAPI parsing accepts only bounded local references anchored to the canonical
+workspace. The interactive Scalar view receives bundled content with agent,
+telemetry, authentication persistence, proxying, API requests, remote fonts,
+and custom fetches disabled.
+
+## Dependencies and packaging
+
+Do not duplicate pinned versions or hashes in documentation. The authoritative
+sources are:
+
+- `tools/visualization/package.json` and `package-lock.json` for web engines and
+ transitive packages;
+- `tools/fetch_d2.sh` for the D2 release, architecture, and verified hashes;
+- `tools/fetch_typst.sh` for Typst release artifacts and hashes;
+- `linux/CMakeLists.txt` and `snap/snapcraft.yaml` for bundle layout; and
+- `.github/workflows/flutter-linux.yml` for release and Snap verification.
+
+The web bundle is built with `npm ci --ignore-scripts`. Build steps verify direct
+upstream artifacts and stage package metadata, licenses, and notices. Node.js is
+a build dependency only.
+
+## Verification
+
+Run the deterministic Dart suite with the bundled executables after building:
+
+```bash
+flutter analyze
+BUSYMARK_D2_PATH=build/linux/x64/debug/bundle/libexec/busymark/d2 \
+BUSYMARK_TYPST_PATH=build/linux/x64/debug/bundle/libexec/busymark/typst \
+ flutter test
+```
+
+Run the real WebKit and D2 corpus under X11:
+
+```bash
+xvfb-run -a -s '-screen 0 1280x1024x24' \
+ env GDK_BACKEND=x11 LIBGL_ALWAYS_SOFTWARE=1 \
+ /usr/bin/python3 -u tools/visualization_smoke.py \
+ --assets build/linux/x64/debug/visualization/web \
+ --d2 build/linux/x64/debug/d2/linux-x86_64/d2 \
+ --demo test/fixtures/markdown/basic.md \
+ --plantuml-corpus test/fixtures/visualization/plantuml-conformance.md
+```
+
+The release binary has a gated product-path smoke entry point:
+
+```bash
+verification_dir="$(mktemp -d)"
+BUSYMARK_RELEASE_SMOKE=1 \
+ build/linux/x64/release/bundle/busymark \
+ --visualization-release-smoke="$verification_dir/report.json"
+```
+
+The report must contain `"ok": true`; the same directory receives the generated
+PDF and HTML fixtures. The Linux workflow is authoritative for compositor setup,
+X11 and Wayland coverage, the strict Snap run, and required environment flags.
+
+The native host and Python corpus harness disable hardware compositing only for
+their offscreen WebKit views, before realization and after process recovery.
+GTK offscreen windows cannot provide the GL context that accelerated compositing
+requires. Normal startup does not require a WebKit environment override, and
+Flutter and visible API-reference windows retain their normal rendering settings.
+Smoke tests must not globally disable WebKit compositing; `LIBGL_ALWAYS_SOFTWARE`
+is only used to provide Mesa rendering on headless CI displays.
diff --git a/docs/development/writerside-verification.md b/docs/development/writerside-verification.md
new file mode 100644
index 00000000..cdbc1fd1
--- /dev/null
+++ b/docs/development/writerside-verification.md
@@ -0,0 +1,51 @@
+# Writerside verification
+
+The user-facing [Writerside support](../writerside-support.md) page describes the
+supported semantic surface and known limitations. This note contains the
+maintainer checks behind those claims.
+
+## Automated coverage
+
+The Writerside tests cover resolution, source preservation, anchored file
+access, instance changes, source assistance, widgets, editing, and export. A
+native Typst integration test runs when the bundled compiler is present and is
+explicitly skipped otherwise. API unit tests inject the platform parser
+interface; they do not claim to exercise the native WebKit parser.
+
+Run the focused suite with:
+
+```bash
+flutter test --no-pub test/src/writerside*_test.dart \
+ test/src/source_autocomplete_test.dart \
+ test/src/source_editor_widget_test.dart \
+ test/src/workspace_controller_test.dart \
+ test/src/markdown_export_mapper_test.dart
+```
+
+## Official-builder conformance
+
+`test/fixtures/writerside/conformance_semantics.json` is a normalized semantic
+snapshot extracted from the official Writerside builder version named in the
+user guide. BusyMark compares representative paragraphs, quotes, shortcuts,
+tooltips, table structure, and related links with that snapshot. Passing the
+fixture is not a claim of complete website-artifact parity.
+
+Run the builder comparison with:
+
+```bash
+bash tools/validate_writerside_conformance.sh
+```
+
+The script uses temporary source copies and validates both the builder report
+and normalized semantic snapshot. Set `WRITERSIDE_CONFORMANCE_OUTPUT` only when
+generated artifacts must be retained for diagnosis.
+
+Regenerate the authoring schema from an intentionally selected official XSD:
+
+```bash
+python3 tools/generate_writerside_schema.py /path/to/topic.v2.xsd
+dart format lib/src/writerside/writerside_schema_data.dart
+```
+
+The generated source records its XSD URL and SHA-256. Review both values when
+updating the supported Writerside baseline.
diff --git a/docs/history.md b/docs/history.md
new file mode 100644
index 00000000..88be4aa9
--- /dev/null
+++ b/docs/history.md
@@ -0,0 +1,105 @@
+# Clipboard History and Local History
+
+BusyMark provides two independent history tools. Clipboard History keeps copied
+material for reuse during the current application session. Local History keeps
+document revisions on this device for comparison and recovery.
+
+## Clipboard History
+
+Open **Clipboard History** from the sidebar, command palette, or an editor
+context menu. Search examines each item's complete text. Select an item and use
+**Paste** or **Paste as Plain Text**; Enter and double-click also insert the
+selected item at the latest editable selection.
+
+BusyMark collects successful copy and cut operations from its editors. Supported
+content copied in another application is added after it is successfully pasted
+into BusyMark. Refresh can inspect the current system clipboard without adding
+it to the retained list.
+
+Entries can contain Source selections, BusyMark rich fragments, supported HTML,
+plain-text alternatives, and images. Rich content retains Markdown structure
+for insertion between BusyMark editors. Supported external HTML goes through
+the same safe conversion as an ordinary paste. Image bytes are retained and are
+added to a document's assets only when you paste them.
+
+Clipboard History is memory-only and disappears when BusyMark exits. It keeps
+up to 100 entries and 64 MiB in total, evicting the oldest entries first. An
+oversized item can still be copied or pasted but is not retained. Clearing
+Clipboard History does not clear the operating-system clipboard. Collection can
+be disabled under **Settings → History** without removing existing session
+entries.
+
+Clipboard persistence after the source application exits depends on the Linux
+desktop's clipboard manager. External applications decide whether to consume
+HTML or plain text, and Writerside-specific structures use readable HTML or
+plain-text fallbacks outside BusyMark.
+
+## Local History
+
+Open **Local History** from the sidebar, command palette, an editor or document
+tab context menu, or a file-tree context menu. The revision list normally follows
+the active document.
+
+Use **Find in Local History…** to locate retained closed, renamed, deleted, and
+previously untitled documents by name, path, or revision content. Selecting a
+result temporarily inspects that document; use Back to return to the active
+document.
+
+BusyMark stores complete source text together with its encoding and line-ending
+policy. It records:
+
+- a baseline when an eligible existing document is first tracked;
+- successful explicit saves;
+- automatic checkpoints, every 60 seconds by default; and
+- protective revisions before reload, discard, restore, delete, or a known
+ external change.
+
+Continued typing does not postpone an active checkpoint window, and ordinary
+autosave does not create a revision for every write. Adjacent revisions with
+identical content are deduplicated.
+
+Select a revision to compare it with the current unsaved editor buffer, or with
+a fresh disk snapshot when the document is not open. You can copy source,
+restore the complete revision, or restore an exact changed region when the
+comparison is current and precise. A restore is one normal undoable edit and
+follows the current autosave setting.
+
+Before restoring content, BusyMark must save a protective revision of the
+current document. If Local History recording is disabled, the path is excluded,
+or storage fails, BusyMark leaves the document unchanged.
+
+For a deleted file, use **Restore to Original Location** or **Restore to New
+Location…**. An existing destination uses the normal overwrite confirmation and
+is protected before the retained source is applied. With autosave disabled, the
+disk file remains unchanged until you save.
+
+## Retention, failures, and clearing
+
+Local History defaults to 30 days and 512 MiB. Either limit can remove older
+entries sooner. Under **Settings → History**, you can change recording, the
+checkpoint interval, age and storage limits, and excluded paths. Each exclusion
+must be an absolute file or directory path; a directory excludes its descendants.
+Blank and relative entries are ignored.
+
+When a history write fails, BusyMark reports the failure and may retry the latest
+pending source during the current session. The document itself can still save,
+but an operation that requires a protective revision remains blocked until that
+revision is safely stored.
+
+Clearing one document or all Local History permanently removes only stored
+revisions. It does not delete project files, Git data, crash-recovery data, or
+clipboard content. New edits after a clear begin a new checkpoint window.
+
+## Choosing the right recovery tool
+
+- **Undo/redo** is the current document's bounded in-memory edit sequence.
+- **Crash recovery** protects recent unsaved buffers after an interrupted
+ session; it is not a revision timeline.
+- **Local History** is a timestamped, on-device source timeline. It is not
+ encrypted, a backup, or an audit log.
+- **Git history** is repository history. Local History never creates commits,
+ refs, or sidecar repositories.
+
+Clipboard and Local History content is not sent to AI providers, telemetry, or
+diagnostic logs by these features. Contributor-facing storage and transaction
+details are in [History implementation notes](development/history.md).
diff --git a/docs/html-export-verification.md b/docs/html-export-verification.md
deleted file mode 100644
index 2bfcf0c6..00000000
--- a/docs/html-export-verification.md
+++ /dev/null
@@ -1,110 +0,0 @@
-# PDF and HTML export verification — September 5, 2026
-
-Validation used Flutter 3.47.0 on Linux, bundled Typst 0.15.1, the bundled Noto
-fonts, and BusyMark’s existing local math and visualization engines. User-facing
-settings are documented in [PDF export](pdf-export.md), [HTML export](html-export.md),
-and [Writerside PDF export](writerside-pdf-export.md).
-
-## Implementation areas
-
-- `lib/src/export/export_options.dart` supplies shared content options and one
- validated, serializable option model per format. `app_settings.dart` persists
- the last confirmed values through the existing settings store.
-- `export_options_editor.dart` supplies the reusable modal editors for Markdown
- and Writerside. Existing PDF and HTML export UI entry points use those editors;
- Writerside retains its workspace-specific instance and Save/Discard/Cancel flow.
-- `typst_payload_builder.dart`, `markdown_math_export.dart`, `typst_compiler.dart`,
- and `assets/export/markdown.typ` carry PDF geometry, typography, native TOC,
- numbering, accent, and running text through payload schema 2. The semantic
- `markdown_export_mapper.dart` no longer sets PDF math metrics.
-- `html_document_writer.dart`, `html_export_styles.dart`, `html_export_assets.dart`,
- and the HTML service/rich-content preparation apply themes, typography, semantic
- TOCs, numbering, bounded custom CSS, and both resource packaging modes.
- `export_metadata_mapper.dart` provides common resolved metadata.
-- Linux and Snap build configuration stages Noto Sans, Serif, Mono and available
- Noto script fallbacks. Localization sources and generated output cover all new
- settings. Focused model, widget, payload, native PDF, HTML, math and Writerside
- tests sit beside the existing export tests.
-
-## Executed checks
-
-| Check | Result |
-| --- | --- |
-| Changed Dart sources formatted; `git diff --check`; Python browser script syntax | Passed |
-| Repository-wide `flutter analyze --no-pub` | Passed; no issues |
-| `flutter gen-l10n` and localization audit | Passed |
-| Full `flutter test --no-pub` with bundled Typst and D2 paths | **1,469 passed; no skips** |
-| Native PDF customization tests | Six profiles compiled; page geometry, embedded Noto families, body/code sizes, native outline entries/page references, heading numbering, running titles and counter placement verified |
-| Workspace refresh regression | Reproduced before the fix; all 63 controller tests pass after the fix |
-| `flutter build linux --release --no-pub` | Passed; final release bundle includes Noto fonts and licenses |
-| Release application smoke under Xvfb | Passed; MathJax 4.1.3, Mermaid, PlantUML, D2, static OpenAPI, default/custom PDFs and Markdown/Writerside HTML; zero HTML warnings |
-| Relocated browser verification | **46 passed** in Chrome 151.0.7922.137 and Firefox 154.0.1 |
-
-The new tests cover absent/malformed persisted settings, validation and geometry,
-all four dialogs’ reuse of remembered format options, dependent controls, Reset,
-cancellation, CSS validation and ordering, embedded and external assets, output
-bounds, existing-output preservation, metadata, TOC hierarchy/depth, numbering,
-custom math metrics, and unchanged Writerside resolution and publication order.
-Existing PDF, HTML, native menu, source-preservation and editor tests remain part
-of the complete suite.
-
-Full-suite verification exposed an existing filesystem-monitor race: a background
-refresh could invalidate a foreground Writerside operation. A controlled regression
-test reproduced that failure. Monitor refreshes now wait for active loading to
-finish, and stale/disposed refreshes stop before changing controller state.
-
-Historical note: at the time of this HTML-export verification, the base `pt`
-and `zh` catalogs were still partial while `pt_BR` and `zh_CN` were complete.
-That limitation has since been superseded: localization policy now requires
-both base and regional catalogs to contain the complete English message-key set.
-
-## Browser and output verification
-
-Browser checks open relocated Markdown and Writerside pages through both `file:`
-URLs and a local static HTTP server, with external networking unavailable. They
-verify image loading, CSP/style hashing, metadata, custom CSS overrides, typography,
-automatic light/dark preference, embedded resources, anchors, footnotes, navigation,
-keyboard focus, disclosure, overflow, script blocking and printing. Request
-observation recorded no unexpected external resources; local favicon 404 probes
-are not document dependencies. Representative screenshots were visually inspected.
-Print checks include disclosure bodies and every Writerside tab.
-
-Representative outputs generated by the release application:
-
-- `/tmp/busymark-custom-output/offline.html` and `offline.assets/`: default
- Markdown output with Unicode headings, nested/task lists, tables, disclosure,
- local SVG, inline/display math, three diagram engines, static API and footnotes.
-- `/tmp/busymark-custom-output/automatic-single.html`: automatic theme, embedded
- resources, 20 px serif text, customized width/accent, numbered headings and CSS.
-- `/tmp/busymark-custom-output/dark-assets.html`: dark appearance with companion
- resources using relative URLs.
-- `/tmp/busymark-custom-output/writerside/index.html`: default multipage instance
- with includes, variables, all tabs, procedures, custom filenames, hidden topics
- and work-in-progress status.
-- `/tmp/busymark-custom-output/writerside-embedded/index.html`: the same instance
- with shared customization and embedded resources in each topic page.
-- `/tmp/busymark-custom-output/visualization-smoke.pdf`: default PDF rich content.
-- `/tmp/busymark-custom-output/customized.pdf`: 240 × 320 mm pages, independent
- margins, 14 pt Noto Sans, 11 pt code, purple accent, numbered native TOC,
- title headers/footers, right-aligned page numbers and first-page suppression.
-
-Native results are in `/tmp/busymark-custom-output/report.json`. Browser results,
-screenshots and printed PDFs are under `/tmp/busymark-custom-browser-final/`;
-`browser-report.json` identifies the separate relocated root. The source fixtures
-and renderer temporary directories were removed before opening relocated copies.
-These temporary artifact paths are not export dependencies and may be cleaned up.
-
-To reproduce with a release bundle and the browser-check dependencies installed:
-
-```sh
-BUSYMARK_RELEASE_SMOKE=1 xvfb-run -a build/linux/x64/release/bundle/busymark \
- --visualization-release-smoke=/tmp/busymark-custom-output/report.json
-python3 tools/verify_html_export_browser.py /tmp/busymark-custom-output \
- --output /tmp/busymark-custom-browser-final
-```
-
-Browser and print verification ran headlessly. Physical-printer output, assistive
-technology certification, and a complete Snap package build were not performed;
-the Linux bundle and its staged Noto fonts/licenses were verified. The existing
-HTML exporter has no client-side syntax highlighter, so this change retains its
-code rendering and supplies a theme-aware code palette without another dependency.
diff --git a/docs/html-export.md b/docs/html-export.md
index 5f809383..70c848f7 100644
--- a/docs/html-export.md
+++ b/docs/html-export.md
@@ -1,7 +1,8 @@
# HTML export
-Choose **Main menu → Export as HTML…** or use the command palette. HTML has no
-default shortcut; **Ctrl+Shift+E** continues to export PDF.
+For Markdown, choose **Outline → Actions → Export** or use the command palette.
+For Writerside, choose **Topics → Actions → Export**. HTML has no default shortcut;
+**Ctrl+Shift+E** continues to export PDF.
Markdown exports the active document’s current editor text, including unsaved
and untitled content. Writerside first offers Save, Discard, or Cancel for
diff --git a/docs/local-ai.md b/docs/local-ai.md
index 7985438c..5353ce9a 100644
--- a/docs/local-ai.md
+++ b/docs/local-ai.md
@@ -1,209 +1,119 @@
# AI editing
-BusyMark provides explicit, reviewable AI editing with Local Ollama, OpenAI,
-or Google Gemini. AI is disabled by default. BusyMark never starts a request in
-the background and never applies generated content without review.
+BusyMark can refine Markdown with Local Ollama, OpenAI, or Google Gemini. AI is
+disabled by default. Every request is initiated explicitly, shows the content
+that will be shared, and produces a proposal that you must review before it can
+change the document.
## Configure a provider
-Open **Settings → AI**, then choose exactly one provider:
+Open **Settings → AI**, enable a provider, and choose a model-routing mode:
-- **Local Ollama** uses a loopback origin, normally
- `http://127.0.0.1:11434`. BusyMark permits only loopback origins, follows no
- redirects, and rejects Ollama cloud models.
+- **Local Ollama** connects only to a loopback origin, normally
+ `http://127.0.0.1:11434`. BusyMark rejects redirects and Ollama cloud models.
- **OpenAI** uses the Responses API with `store: false`.
-- **Google Gemini** uses the stable Interactions API with `store: false`.
-
-Selecting a cloud provider requires an explicit content-disclosure consent.
-The API key is stored by the operating-system credential service through
-libsecret. It is never written to BusyMark settings, a workspace, logs, or the
-application bundle. A desktop process must still read the key to make a direct
-BYOK request, so OS credential storage protects the key at rest; it does not
-make a compromised desktop process trustworthy.
-
-BusyMark uses libsecret's password API through the desktop secret service.
-Access depends on the credential service made available to the packaged
-application. BusyMark does not write API keys into settings, workspaces, logs,
-or its application bundle.
-
-Choose **Automatic model selection** to let BusyMark route the task among the
-approved models of the selected provider, or **Fixed model** to use only the
-chosen model. Routing and retries never cross provider boundaries. BusyMark
-does not silently send content from Ollama to a cloud provider or from one
-cloud provider to another.
-
-**Test connection** performs an actual bounded generation and accepts the
-provider only when the selected model returns the required test value. For
-Ollama, BusyMark also inspects `/api/show` to verify text-generation capability
-and the advertised context limit. A local model cold start is allowed up to the
-documented test deadline.
-
-Current API and privacy behavior is described by the providers in the
-[OpenAI Responses API](https://developers.openai.com/api/docs/guides/responses),
-[OpenAI API data controls](https://developers.openai.com/api/docs/guides/your-data),
-[Gemini Interactions API](https://ai.google.dev/gemini-api/docs/interactions-overview),
-and [Ollama API](https://docs.ollama.com/api/introduction) documentation.
-`store: false` disables provider conversation-state storage for these BusyMark
-requests; it is not a promise about every form of provider retention or the
-terms of a particular account.
-
-## Editing workflow
-
-AI editing is available only for Markdown files and Writerside Markdown topics
-in **Source** view. It is deliberately unavailable for Writerside XML, trees,
-configuration, variables, categories, resources, images, and unknown text
-formats. The WYSIWYG editor does not expose AI editing because flattening its
-structured blocks to plain text could lose Markdown semantics.
-
-The supported document actions are:
-
-- Rewrite
-- Shorten
-- Summarize
-- Change tone
-- Translate
-- Proofread
-- Draft
-- Explain code block
-- Improve code block
-
-Rewrite, Shorten, Change tone, Translate, and Proofread require a selection.
-Summarize expands a partial selection to complete Markdown blocks and otherwise
-summarizes the active document. Draft keeps selected notes as context and
-inserts the generated Markdown after them; without a selection it uses the
-nearest safe prose block as local context. Insertions move to a Markdown block
-boundary rather than splitting front matter, a fence, raw markup, or another
-protected construct. Code actions require the cursor or selection to be inside
-one complete fenced code block. Explain inserts prose after the fence; Improve
-proposes a replacement for that fence.
-
-Every action follows the same flow:
-
-1. BusyMark identifies the exact context and proposed replacement range.
-2. The dialog discloses the provider, model, and content category being sent.
-3. Output streams into a temporary proposal buffer, never the document.
-4. BusyMark validates the complete candidate document and shows a unified diff.
-5. The user copies, rejects, or applies the proposal.
-6. An accepted proposal becomes an ordinary editor edit with normal undo and
- save behavior.
-
-If the document or selection changes while generation is running, the proposal
-is stale and cannot be applied.
-
-## Git commit drafts
-
-The Changes sidebar can draft a Git commit message from the complete staged
-repository diff. BusyMark sends the staged patch only: it does not send the
-working tree, history, unstaged content, or repository files. The generated
-subject is limited to 72 characters and an optional body must be separated by
-a blank line. Accepting the proposal only fills the existing commit-message
-field; it never stages or commits anything.
-Immediately before Apply, BusyMark reads the complete staged patch again and
-compares its SHA-256 fingerprint with the exact patch used for generation. A
-changed index makes the proposal stale and requires a new draft.
-
-## Markdown integrity
-
-For transformations of existing Markdown, BusyMark builds the full candidate
-document and reparses it with the same GFM parser used by the editor. It rejects
-a proposal that changes protected structure or associations, including:
-
-- YAML front matter;
-- heading, list, table, and inline-Markdown structure;
-- URLs associated with links and images;
-- reference-link and footnote identifiers;
-- autolinks and heading attributes/IDs;
-- inline code with arbitrary backtick delimiters and fenced code;
-- raw HTML and Writerside markup or variables.
-
-Improve code is the narrow exception: exactly one complete fenced block may
-change, while its fence declaration and language remain fixed. The validator
-is intentionally conservative. A safe prose edit can be rejected, but a model
-response is never treated as trusted Markdown merely because it looks valid.
-
-## Budgets, retries, and privacy
-
-- Feature-specific input, total-prompt, instruction, provider output-token,
- generated-output byte, transport-byte, and absolute request-time limits are
- enforced independently. Provider-neutral prompt estimates count Unicode
- text, not UTF-8 transport bytes; providers receive output limits in tokens.
-- Large whole-document summaries use at most 16 bounded section summaries and
- a final synthesis. Other actions reject oversized input rather than silently
- sending more context.
-- Provider `429`, `408`, `409`, and transient `5xx` responses may be retried
- before any output is shown. BusyMark honors `Retry-After` and uses bounded
- exponential backoff with jitter.
-- A request is never restarted after text has streamed because restarted output
- could be duplicated.
-- At most two generations run concurrently. A newer request for the same edit
- target cancels the older request, and dialogs cancel by request ID.
-- BusyMark stores only monthly aggregate request/input/output token counts by
- provider. Prompts, responses, file paths, document names, model names, and
- credentials are not written to the usage ledger.
-
-For genuinely offline use, select Local Ollama and disable Ollama cloud support
-itself with `OLLAMA_NO_CLOUD=1` or `disable_ollama_cloud: true`, as documented in
+- **Google Gemini** uses the Interactions API with `store: false`.
+
+Cloud providers require explicit consent before BusyMark sends content. Their
+API keys are stored through the Linux system credential service (libsecret), not
+in BusyMark settings, project files, logs, or the application bundle. Credential
+storage protects a key at rest; a running desktop process still needs access to
+the key to make a request.
+
+Choose **Automatic by task** to let BusyMark select from compatible models for
+the enabled provider, or **Use selected model** to use the preferred model.
+Routing never crosses provider boundaries. Use **Test connection** to verify
+that the configured provider and model can generate text.
+
+## Refine Markdown
+
+**Refine with AI** is available for ordinary Markdown files and Writerside
+Markdown topics. It is not available for Writerside XML, project trees,
+configuration files, variables, categories, resources, images, or unknown text
+formats.
+
+You can start it in either Source or Editor view:
+
+1. Select content and choose **Refine with AI** from the editor context menu.
+ You can also use the document or Outline actions to refine an entire document
+ or a selected section.
+2. Enter a specific instruction, such as “Make this paragraph concise while
+ keeping its meaning.”
+3. Confirm the provider and model for this request.
+4. Choose **What may change** and **Context shared with AI**. These choices are
+ independent: for example, BusyMark can change one block while sharing the
+ complete document as context.
+5. Inspect **Content to change** and **Content sent to AI**, then choose
+ **Generate proposal**.
+6. Review the original and suggested content. Copy the proposal, cancel it, or
+ choose **Apply proposal**.
+
+The available change targets depend on where you opened the action:
+
+| Change target | Result |
+| --- | --- |
+| Selected content | Replaces the current selection. |
+| Insert after current block | Inserts new Markdown at a safe block boundary. |
+| Current block | Replaces the block at the selection or caret. |
+| Current section | Replaces the current heading and its section content. |
+| Complete document | Replaces the complete Markdown source. |
+
+Shared context can be none, the selection, the current block, the current
+section, or the complete document. BusyMark displays the exact context and its
+character count before sending it. A target or context choice is omitted when
+it is not available at the current location.
+
+Applying a proposal creates a normal editor edit with ordinary undo and save
+behavior. If the document changes while generation or review is in progress,
+the proposal becomes stale and BusyMark will not apply it.
+
+## Markdown safeguards
+
+BusyMark validates the complete candidate document before enabling Apply. For
+edits to existing text, it protects Markdown structure and associations such as
+front matter, headings, lists, tables, links, images, identifiers, footnotes,
+inline and fenced code, raw HTML, and Writerside markup or variables.
+
+The validator is intentionally conservative, so it can reject a reasonable
+prose edit that also changes protected syntax. An insertion after the current
+block may introduce new Markdown, but BusyMark still parses the complete result
+and places the insertion at a safe block boundary.
+
+## Git commit-message drafts
+
+In the Changes sidebar, **Draft with AI** sends the complete staged patch and
+returns a proposed commit message. It does not send unstaged content, repository
+history, or unrelated files. Applying the proposal only fills the commit-message
+field; it never stages or commits changes.
+
+BusyMark checks the staged patch again before Apply. If the index changed while
+the message was generated, the proposal is stale and must be regenerated.
+
+## Privacy and limits
+
+- Only the context displayed for the current request is sent to the selected
+ provider. BusyMark does not silently fall back to a different provider.
+- `store: false` disables provider conversation-state storage for BusyMark's
+ cloud requests. It does not replace the provider's retention terms or the
+ policies of your account.
+- Prompts, responses, file paths, document names, model names, and credentials
+ are not written to BusyMark's usage ledger. The ledger stores only monthly
+ aggregate request and token counts by provider.
+- Requests and responses have bounded size and duration. A request can be
+ retried only before generated text has been shown.
+
+For fully local use, select Local Ollama and disable cloud support in Ollama
+itself with `OLLAMA_NO_CLOUD=1` or `disable_ollama_cloud: true`, as described in
the [Ollama FAQ](https://docs.ollama.com/faq#how-can-i-disable-ollamas-cloud-features).
-## Deterministic smart editing
-
-Two related features do not invoke AI:
-
-- **Generate/update table of contents** creates a parser-derived,
- marker-delimited TOC and updates the same generated region on later runs.
-- Markdown diagnostics identify skipped heading levels, empty or vague link
- text, and empty table-header cells.
-
-Keeping these checks deterministic follows established accessibility guidance
-for [nested headings](https://www.w3.org/WAI/WCAG22/Techniques/general/G141),
-[descriptive link purpose](https://www.w3.org/WAI/WCAG22/Understanding/link-purpose-in-context.html),
-and [table structure](https://www.w3.org/WAI/WCAG22/Techniques/).
-
-## Implementation and release qualification
-
-The provider-neutral implementation lives under `lib/src/ai/`:
-
-- provider adapters map OpenAI SSE, Gemini SSE, and Ollama NDJSON into one typed
- stream;
-- the coordinator owns routing, retry, concurrency, cancellation, usage, and
- latest-write-wins behavior;
-- policy and Markdown validation run independently of the provider;
-- the first-party Linux credential host uses portal-compatible libsecret
- password operations without opening the global Secret Service collection;
-
-Deterministic tests cover arbitrary UTF-8 stream boundaries, malformed and
-incomplete events, redirects, response bounds, provider isolation, model
-fallback, cancellation, deadlines, retries and `Retry-After`, context budgets,
-credential redaction, Markdown structural invariants, source-editor apply, Git
-message validation, TOC generation, and accessibility diagnostics.
-
-Release qualification must additionally run [the AI demo](../demo/ai-editing.md)
-against each exact provider/model combination intended for release. Cloud
-qualification requires a deliberately supplied test credential; it is never
-enabled by CI secrets implicitly. Local Ollama qualification must run with
-cloud features disabled. Generated prose remains probabilistic, so release
-review evaluates structural invariants and task quality rather than exact
-golden strings.
-
-The local end-to-end corpus can be executed against an installed Ollama model:
-
-```bash
-OLLAMA_NO_CLOUD=1 ollama serve
-dart run tools/ai_ollama_qualification.dart --model
-```
-
-It performs a real generation health check and exercises all seven editing
-commands, whole-document summary, both fenced-code actions, Markdown structural
-validation, and staged-diff commit-message generation. It prints proposals for
-human quality review and exits unsuccessfully if any structural check fails.
-
-## Authoritative references
-
-- [OpenAI Responses streaming](https://developers.openai.com/api/docs/guides/streaming-responses)
-- [OpenAI models](https://developers.openai.com/api/docs/models)
-- [Gemini Interactions streaming](https://ai.google.dev/gemini-api/docs/streaming)
-- [Gemini models](https://ai.google.dev/gemini-api/docs/models)
-- [Ollama chat API](https://docs.ollama.com/api/chat)
-- [Ollama model details API](https://docs.ollama.com/api/show)
+Maintainers can find implementation invariants and qualification commands in
+[AI implementation notes](development/ai.md).
+
+## Provider references
+
+- [OpenAI Responses API](https://developers.openai.com/api/docs/guides/responses)
+- [OpenAI API data controls](https://developers.openai.com/api/docs/guides/your-data)
+- [Gemini Interactions API](https://ai.google.dev/gemini-api/docs/interactions-overview)
+- [Ollama API](https://docs.ollama.com/api/introduction)
- [Libsecret password storage](https://gnome.pages.gitlab.gnome.org/libsecret/libsecret/password-storage.html)
- [Snap Secret Portal](https://snapcraft.io/docs/how-to-guides/snap-development/use-the-secret-portal/)
diff --git a/docs/pdf-export.md b/docs/pdf-export.md
index 39b77dca..32350230 100644
--- a/docs/pdf-export.md
+++ b/docs/pdf-export.md
@@ -1,9 +1,11 @@
# PDF export
-Choose **Main menu → Export as PDF…**, use the command palette, or press
-**Ctrl+Shift+E**. Markdown exports the active editor text, including unsaved and
-untitled documents. Writerside first offers Save, Discard, or Cancel for project
-changes, then lets you select a non-library instance.
+For Markdown, choose **Outline → Actions → Export**, use the command palette,
+or press **Ctrl+Shift+E**. Markdown exports the active editor text, including
+unsaved and untitled documents. For Writerside, choose **Topics → Actions → Export**;
+BusyMark first offers Save, Discard, or Cancel for project changes, then lets
+you select a non-library instance. The shortcut and command palette route use
+the same document-versus-instance scope.
The export settings are independent of the editor and preview appearance.
Markdown and Writerside offer the same PDF controls:
@@ -39,6 +41,17 @@ Choose the destination after confirming settings. BusyMark asks before
replacing an existing file. Export can be cancelled, and a failed export keeps
the previous PDF usable.
+## Intentional blank lines
+
+Use **Insert blank line** in Editor view when a specific one-line gap is part of
+the document. BusyMark stores the gap as two explicit `
` elements, so it is
+visible in Editor and Reading views and preserved by Markdown PDF and HTML
+exports. Source view places each generated `
` on a marker-only line instead
+of appending the tags to document text.
+
+Additional empty source lines are still ordinary Markdown block separators;
+repeating them does not create additional vertical space.
+
## Offline content
PDF export works locally with BusyMark’s included PDF, math, and diagram tools.
diff --git a/docs/search-and-replace.md b/docs/search-and-replace.md
new file mode 100644
index 00000000..8fdd98d2
--- /dev/null
+++ b/docs/search-and-replace.md
@@ -0,0 +1,78 @@
+# Search and replace
+
+Press **Ctrl+F**, choose **Search**, or use the command palette to search the
+open workspace. The search controls use the same query and options for the
+active document and workspace results.
+
+## Search options
+
+- **Case sensitive** distinguishes uppercase and lowercase letters.
+- **Whole word** excludes matches that are part of a longer word.
+- **Regex** treats the query as a Dart regular expression. Invalid expressions
+ are reported instead of being searched.
+
+Zero-length regular-expression matches, such as `^`, `$`, or a lookahead that
+matches only a position, are not supported for search or replacement.
+
+## Active document
+
+In Source or Split view, the search panel shows the match count and provides
+previous and next navigation. Its replacement field supports:
+
+- replacing the current match;
+- replacing the current match and moving to the next one; and
+- replacing every match in the active document.
+
+These commands edit the current in-memory document. They do not save it
+immediately unless autosave is enabled, and they participate in normal undo and
+save behavior.
+
+Editor and Reading views show workspace search results and navigate to matching
+content, but the active-document replacement controls are available in Source
+or Split view.
+
+## Workspace search
+
+The sidebar groups matches by file. Selecting a result opens that document and
+navigates to the matching source. Workspace search covers supported text files
+already known to the open workspace, including Markdown, Writerside Markdown
+and XML topics, trees, configuration, variables, categories, `.gitignore`, and
+text resources. Images and unknown file types are excluded.
+
+For open documents, search uses the current editor buffer, including unsaved
+changes. For unopened documents, it reads the saved file. Unopened files larger
+than 1 MiB or files that cannot be read are listed as skipped. BusyMark
+initially limits the displayed results; choose **Show more results** or narrow
+the query when the result list is incomplete.
+
+## Replace in the workspace
+
+Workspace replacement is always reviewed:
+
+1. Run a workspace search and choose **Replace in Workspace**.
+2. Enter the replacement text and choose **Review replacements**.
+3. Review matches grouped by file. Clear individual matches or a whole file to
+ exclude them.
+4. Choose **Apply replacements**.
+
+With Regex enabled, replacement text supports `$1` through `$99` for numbered
+capture groups, `${name}` for named groups, `$&` for the complete match, and
+`$$` for a literal dollar sign.
+
+BusyMark rechecks every selected file and open buffer against the reviewed
+preview before changing it. Replacements in open documents update their editor
+buffers and follow normal save or autosave behavior. Replacements in unopened
+documents are written to disk while preserving their encoding and line-ending
+format; mixed line endings require you to choose LF or CRLF first.
+
+## Skipped and stale results
+
+A skipped result means BusyMark could not safely apply the reviewed change. A
+file may have changed on disk, an open buffer may have changed or closed, the
+file may be unreadable or invalid UTF-8, or the preview may have reached its
+match limit. Resolve the reported cause and run the search again.
+
+BusyMark does not apply an incomplete preview. If a concurrent change prevents
+a safe file transaction, the result identifies the affected path and, when
+necessary, the preserved displaced content. Always read the final replacement
+summary before continuing.
diff --git a/docs/visualizations.md b/docs/visualizations.md
index f083618b..6d8b659f 100644
--- a/docs/visualizations.md
+++ b/docs/visualizations.md
@@ -1,28 +1,72 @@
# Offline visualizations
-BusyMark renders Mermaid, PlantUML, D2, and fenced OpenAPI documents locally.
-The Markdown parser and serializer still treat every visualizer as an ordinary
-fenced code block. A `VisualizationDescriptor` is derived for preview and PDF
-export, so the original fence, language spelling, source span, and text remain
-authoritative.
+BusyMark renders Mermaid, PlantUML, D2, and fenced OpenAPI content locally.
+The original fenced source remains authoritative: editing and history views use
+the source, and saving preserves the fence and language spelling.
## Supported fences
-| Renderer | Fence identifiers | Preview | PDF |
+| Content | Fence identifiers | Reading and Editor views | PDF export |
| --- | --- | --- | --- |
-| Mermaid | `mermaid` | Sanitized SVG when styling is vector-safe; otherwise PNG | Vector SVG when styling is vector-safe; otherwise high-resolution PNG |
-| PlantUML | `plantuml`, `puml` | Sanitized SVG when styling is vector-safe; otherwise PNG | Vector SVG when styling is vector-safe; otherwise high-resolution PNG |
-| D2 | `d2` | Sanitized SVG when all styling is vector-safe; otherwise PNG | Vector SVG when all styling is vector-safe; otherwise high-resolution PNG |
-| OpenAPI | `openapi`, `oas`, `swagger` | Native summary and a BusyMark-owned Scalar window | Static, selectable reference content |
+| Mermaid | `mermaid` | Sanitized SVG or PNG | Vector SVG when safe; otherwise high-resolution PNG |
+| PlantUML | `plantuml`, `puml` | Sanitized SVG or PNG | Vector SVG when safe; otherwise high-resolution PNG |
+| D2 | `d2` | Sanitized SVG or PNG | Vector SVG when safe; otherwise high-resolution PNG |
+| OpenAPI | `openapi`, `oas`, `swagger` | Summary with a BusyMark-owned reference window | Static, selectable API reference |
-Identifiers are classified case-insensitively. Saving preserves the exact
-source fence. History and diff views show source rather than generated output.
-Whole-file YAML/JSON OpenAPI editing is not part of this feature.
+Fence identifiers are case-insensitive. A whole YAML or JSON file is not
+automatically treated as OpenAPI; put the specification in a recognized fence.
+
+## Diagram examples
+
+````markdown
+```mermaid
+flowchart LR
+ Source --> Preview
+```
+
+```plantuml
+@startuml
+Alice -> Bob: Hello
+@enduml
+```
+
+```d2
+source -> preview
+```
+````
+
+BusyMark renders common Mermaid diagram families and the PlantUML families
+available in the packaged MIT browser engine. D2 imports and icon or image assets
+inside a D2 fence are not supported. The bundled D2 executable is currently
+available only for Linux amd64.
+
+## OpenAPI
+
+Use YAML or JSON inside an `openapi`, `oas`, or `swagger` fence:
+
+````markdown
+```openapi
+openapi: 3.1.0
+info:
+ title: Example API
+ version: 1.0.0
+paths: {}
+```
+````
+
+BusyMark accepts OpenAPI 3.2, 3.1, and 3.0 and Swagger 2.0. The interactive
+reference receives bundled content rather than a URL and cannot issue API
+requests. Relative specification references must remain inside the open
+workspace. Remote references, absolute paths, traversal, symlink escapes,
+oversized files, and excessive or circular dependency graphs are rejected.
+
+PDF and HTML export use static API content instead of a screenshot of the
+interactive reference. Schema objects are expanded within the reference; BusyMark
+does not generate a separate website page for every schema.
## Writerside diagram forms
-Writerside Markdown and XML topics can also use semantic code blocks for
-Mermaid, PlantUML, and D2:
+Writerside Markdown and XML topics can use semantic code blocks:
```xml
flowchart LR
@@ -37,8 +81,8 @@ A -> B
a -> b
```
-Writerside's referenced-source form is supported for all three renderers. The
-path is relative to the current topic and must remain inside the open
+Writerside's referenced-source forms are supported for all three diagram
+renderers. Paths are relative to the topic and must remain inside the open
Writerside project:
````markdown
@@ -49,196 +93,30 @@ Writerside project:
{ src="../codeSnippets/flow.mmd" }
````
-Referenced files are read as strict UTF-8, size-limited, and resolved through
-BusyMark's anchored-path checks. Absolute paths, URI schemes, traversal outside
-the project, and symlink escapes are rejected. Semantic tags and the `src`
-attribute form are enabled only for Writerside projects; ordinary Markdown
-keeps them as ordinary HTML/text. These forms track the official Writerside
-documentation for [D2](https://www.jetbrains.com/help/writerside/d2-diagrams.html),
-[PlantUML](https://www.jetbrains.com/help/writerside/plantuml-diagrams.html),
-and [Mermaid](https://www.jetbrains.com/help/writerside/mermaid-diagrams.html).
-
-The automated conformance corpus is stored in:
-
-- [`test/fixtures/markdown/basic.md`](../test/fixtures/markdown/basic.md)
-- [`test/fixtures/visualization/openapi-local-reference.md`](../test/fixtures/visualization/openapi-local-reference.md)
-- [`test/fixtures/visualization/plantuml-conformance.md`](../test/fixtures/visualization/plantuml-conformance.md)
-
-## Runtime design
-
-`lib/src/visualization/` owns renderer contracts, typed results, diagnostics,
-revision cancellation, priority scheduling, memory/disk LRU caches, generated
-SVG normalization, D2 execution, and OpenAPI dependency resolution. Cache keys
-include the renderer and sanitizer versions, source, theme, preview/PDF profile,
-options, and hashes of local dependencies. The disk cache is stored below
-`$XDG_CACHE_HOME/busymark/visualizations` for both conventional packages and
-the strict Snap.
-
-The Linux runner provides a first-party Flutter platform-channel host backed by
-WebKitGTK 4.1. It uses one reusable hidden render view in an ephemeral WebKit
-context and creates BusyMark-owned views only for full Scalar references.
-Inline previews are Flutter SVG/PNG widgets, not live browser views.
-
-The host:
-
-- serves an allow-listed bundle through the private `busymark-render:` scheme;
-- uses an ephemeral data manager and rejects cookies;
-- disables local storage, databases, media, WebRTC, developer tools, popups,
- permissions, context menus, and unapproved navigation;
-- applies a CSP with no network, frames, objects, forms, plugins, or remote
- fonts;
-- recreates the hidden view after WebKit process termination; and
-- treats engine SVG as untrusted input before it reaches Flutter or Typst.
-
-The CSP permits WebAssembly evaluation for the official PlantUML/Viz.js build
-and JavaScript evaluation for Scalar's bundled schema validator. Those
-permissions are confined to the private, allow-listed, no-network harness.
-
-WebKit's subprocess sandbox remains enabled for ordinary Linux packages and
-the strict Snap. The private rendering scheme, ephemeral context, disabled
-storage, and no-network content policy apply in both package forms.
-
-## Renderer policy
-
-Mermaid uses its programmatic `render` API with automatic scanning disabled,
-strict security, HTML labels and error drawings disabled, deterministic IDs,
-bounded text/edge counts, and BusyMark light/dark themes.
-
-PlantUML uses the official MIT `@plantuml/core` TeaVM browser engine and its
-bundled Viz.js layout runtime. BusyMark's release corpus covers sequence, class,
-component, deployment, state, activity, use-case, entity relationship, mind
-map, WBS, Gantt, JSON, and YAML diagrams in WebKitGTK. Sudoku is intentionally
-absent from the MIT browser build, as documented by PlantUML.
-
-D2 uses the official Linux amd64 executable directly, never through a shell.
-Execution has bounded source, time, stdout, stderr, dimensions, and output, and
-uses a fresh temporary working directory with a minimal environment. Fenced D2
-imports and icon/image assets are disabled in the first release. BusyMark asks
-D2 only for SVG: safe CSS is inlined, executable or remote content and
-animations are removed. Vector output is accepted only when every remaining CSS
-rule can be represented without changing its meaning. Embedded fonts,
-unsupported selectors or declarations, conflicting cascade rules, and
-`` content retain their sanitized browser styling and are
-rasterized by the local WebKit host. BusyMark never silently drops styling to
-claim that an SVG is vector-safe. Raster fallback prefers 2× preview and 3× PDF
-output, then reduces the scale when necessary to stay within WebKit's 8192-pixel
-dimension and 64,000,000-pixel area limits. Stored raster metadata uses the
-host's actual ceiling-rounded pixel dimensions. The existing external-SVG
-policy is unchanged.
-
-OpenAPI uses Scalar's parser for OpenAPI 3.2, 3.1, 3.0, and Swagger 2.0,
-Scalar's official JSON bundler for local references, and the YAML parser's exact
-source locations for diagnostics. Only relative files anchored within the
-canonical workspace are accepted. Absolute, remote, traversal, symlink-escape,
-oversized, and excessive dependency graphs are rejected. The Scalar window is
-given bundled content, not a URL; Agent, telemetry, authentication persistence,
-API requests, developer tools, plugins, proxying, remote fonts, and custom
-fetches are disabled.
-
-## PDF export
-
-`MarkdownPdfExportService` renders recognized fences before mapping the export
-model. Generated assets are stored by SHA-256 under the temporary
-`generated-assets` directory and then placed by the existing Typst template.
-OpenAPI is mapped to headings, tables, paragraphs, operations, parameters,
-request bodies, responses, security schemes, and schemas instead of a Scalar
-screenshot. A renderer failure preserves the original fenced source and adds a
-warning; it does not abort the document export.
-
-## Pinned dependencies
-
-| Component | Version | Verified artifact SHA-256 |
-| --- | --- | --- |
-| Mermaid source | 11.16.1 | `0ee99b3bb82766e5d6c34b8cc768b8530ce8f1aaa13790ae368aebeef3de9d11` |
-| `@plantuml/core` | 1.2026.6 | `798f99592eb03a6446519d2becf78e6f1008d0d25c75d60b37a0f46e39e3c413` |
-| `@scalar/openapi-parser` | 0.28.14 | `993bb7ebb3480cc574665b0eac52d9cd4a817fdf5b4444894bb70e174880513d` |
-| `@scalar/api-reference` | 1.65.1 | `68b6f22ca530ac50e3cd034c5189d89cc5457c3c2d325b44e90db05c9f08c573` |
-| `@scalar/json-magic` | 0.13.0 | `f1adefc461f3594afd4ad16974820a5a88b271f7e8051045c2ac7a34eb974d33` |
-| YAML | 2.9.0 | `008fa204cb1ba700e0272ba045abbf09a6ffe63456e8146ba97cac6c2ad1ef91` |
-| D2 Linux amd64 archive | 0.7.1 | `eb172adf59f38d1e5a70ab177591356754ffaf9bebb84e0ca8b767dfb421dad7` |
-| D2 Linux amd64 executable | 0.7.1 | `48db68dfb42b76970a6769f038ec60da932adbb058257e07c50f5baaa3046016` |
-
-The web build also pins all transitive packages in
-`tools/visualization/package-lock.json` and runs `npm ci --ignore-scripts`.
-Build scripts verify the direct upstream archives before installation and copy
-package metadata, distributed licenses, and a consolidated notice into the
-application bundle. The build requires Node.js 22 or newer; the core24 Snap
-recipe uses the official `node/24/stable` build snap. Node.js and npm are build
-tools only. Runtime rendering does not require Node.js, Chromium, Java, a
-public rendering service, or a first-run download.
-
-BusyMark builds the complete Mermaid source profile with Mermaid's internal
-math rendering disabled. This keeps the existing diagram families while
-removing KaTeX from both the locked dependency graph and the shipped web
-bundle; mathematical document content is rendered only by MathJax.
-
-D2 is packaged only for Linux amd64. BusyMark must not advertise another
-architecture until the Snap platform, upstream artifact, and full corpus are
-all added and tested for it.
-
-## Verification
-
-Run the deterministic unit/widget/export suite with:
-
-```bash
-flutter analyze
-BUSYMARK_D2_PATH=build/linux/x64/debug/d2/linux-x86_64/d2 \
-BUSYMARK_TYPST_PATH=build/linux/x64/debug/bundle/libexec/busymark/typst \
- flutter test
-```
+Referenced files must be valid UTF-8 and stay within the configured size limit.
+Absolute paths, URI schemes, traversal, and symlink escapes are rejected.
+Semantic tags and `src` attributes are enabled only for Writerside projects;
+ordinary Markdown treats them as ordinary markup.
-Run the real offline WebKit/D2 conformance corpus under X11 with:
+## Export and failures
-```bash
-xvfb-run -a -s '-screen 0 1280x1024x24' \
- env WEBKIT_DISABLE_COMPOSITING_MODE=1 LIBGL_ALWAYS_SOFTWARE=1 \
- /usr/bin/python3 -u tools/visualization_smoke.py \
- --assets build/linux/x64/debug/visualization/web \
- --d2 build/linux/x64/debug/d2/linux-x86_64/d2
-```
+Generated diagrams and API references are included in PDF and offline HTML
+exports. When a renderer fails, BusyMark keeps the original fenced source as a
+readable fallback and reports a warning instead of aborting the document export.
-The release binary has a CI-only verification entry point. It is accepted by
-the native host only when `BUSYMARK_RELEASE_SMOKE=1` is set and writes a JSON
-report plus a real Typst PDF:
+Rendering does not require Java, Chromium, a public rendering service, or a
+first-run download. D2 runs as a bounded local process. Mermaid, PlantUML, and
+OpenAPI run in BusyMark's packaged, no-network WebKit environment. Generated SVG
+is sanitized before display or export, and styling that cannot be preserved
+safely as vector content is rasterized rather than silently removed.
-```bash
-BUSYMARK_RELEASE_SMOKE=1 \
- build/linux/x64/release/bundle/busymark \
- --visualization-release-smoke=/tmp/busymark-visualization-report.json
-```
+Maintainers can find architecture, dependency, and verification details in
+[Visualization implementation and verification](development/visualizations.md).
+
+## Syntax references
-The Linux workflow installs WebKitGTK 4.1 development files explicitly, builds
-the release bundle, runs all Flutter tests with the bundled D2 and Typst paths,
-and runs the real engine corpus under X11 and Wayland. It then exercises the
-actual Dart coordinator, native WebKit channel, D2 CSS and `foreignObject`
-raster paths, OpenAPI model, live WebKit process termination/recovery, and Typst
-PDF export through the release executable.
-
-The same workflow builds the final strict Snap in a clean core24 build
-environment, installs it as an unasserted confined package, and runs that
-release verification under both X11 and Wayland. Automated suites also cover
-cancellation, stale-result rejection, timeout wiring, sanitization and external
-resources, traversal and symlink escapes, circular OpenAPI references, input
-limits, both themes, cache-version invalidation, D2 raster snapshots, and a
-rasterized visual assertion of generated SVG content in the PDF. Human release
-review should still inspect the fixture documents and PDF for visual quality; it
-is not a substitute for these automated product-path checks.
-
-## Authoritative references
-
-- [Flutter Linux platform channels](https://docs.flutter.dev/platform-integration/platform-channels)
-- [WebKitGTK ephemeral contexts](https://webkitgtk.org/reference/webkit2gtk/stable/ctor.WebContext.new_ephemeral.html)
-- [WebKitGTK subprocess sandbox](https://webkitgtk.org/reference/webkit2gtk/stable/method.WebContext.set_sandbox_enabled.html)
-- [WebKitGTK web-process termination](https://webkitgtk.org/reference/webkit2gtk/stable/method.WebView.terminate_web_process.html)
-- [Mermaid programmatic usage and strict security](https://mermaid.js.org/config/usage.html)
-- [PlantUML official npm publishing and MIT build](https://github.com/plantuml/plantuml/blob/master/PUBLISHING_NPM.md)
-- [D2 SVG and PNG export behavior](https://d2lang.com/tour/exports/)
+- [Writerside D2 diagrams](https://www.jetbrains.com/help/writerside/d2-diagrams.html)
+- [Writerside PlantUML diagrams](https://www.jetbrains.com/help/writerside/plantuml-diagrams.html)
+- [Writerside Mermaid diagrams](https://www.jetbrains.com/help/writerside/mermaid-diagrams.html)
- [D2 imports](https://d2lang.com/tour/imports/)
- [D2 icons and images](https://d2lang.com/tour/icons/)
-- [Scalar OpenAPI parser](https://github.com/scalar/scalar/blob/main/packages/openapi-parser/README.md)
-- [Scalar API Reference configuration](https://scalar.com/products/api-references/configuration)
-- [Snap confinement](https://snapcraft.io/docs/explanation/security/snap-confinement/)
-- [Snapcraft GNOME extension](https://documentation.ubuntu.com/snapcraft/latest/reference/extensions/gnome-extension/)
-- [Snapcraft project-file `grade` semantics](https://documentation.ubuntu.com/snapcraft/latest/reference/project-file/snapcraft-yaml/)
-- [GitHub Ubuntu 24.04 runner image](https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md)
-- [Ubuntu 24.04 WebKitGTK 4.1 development package](https://packages.ubuntu.com/noble-updates/libwebkit2gtk-4.1-dev)
diff --git a/docs/writerside-instances.md b/docs/writerside-instances.md
index abd754aa..1b0d7d81 100644
--- a/docs/writerside-instances.md
+++ b/docs/writerside-instances.md
@@ -7,7 +7,7 @@ introduce a BusyMark-specific project file.
## Instance actions
-Open **Table of Contents**, select an instance from the visible **Instances**
+Open **Topics**, select an instance from the visible **Instances**
list, and use **TOC actions** to:
- create an instance, either empty or from selected local Markdown files;
@@ -54,14 +54,6 @@ identified, but are not expanded when only one help module is open.
The selected instance and icon colors are local BusyMark preferences. They do
not modify or add undocumented Writerside project metadata.
-## Example
-
-[`demo/writerside-instances`](../demo/writerside-instances) is an openable
-Writerside module with two output instances, a TOC library, instance groups,
-custom filters, a cross-instance topic reference, per-instance build settings,
-release/EAP statuses, hidden and work-in-progress topics, an external TOC
-link, and redirect metadata.
-
## Authoritative references
- [Instances](https://www.jetbrains.com/help/writerside/instances.html)
diff --git a/docs/writerside-pdf-export.md b/docs/writerside-pdf-export.md
index 0382cddf..1a28d03b 100644
--- a/docs/writerside-pdf-export.md
+++ b/docs/writerside-pdf-export.md
@@ -6,9 +6,9 @@ are not required.
## Export
-Open a Writerside module and select **Main menu → Export as PDF** or press
-Ctrl+Shift+E. BusyMark first asks you to save or
-discard unsaved project changes, then lets you select:
+Open a Writerside module and select **Topics → Actions → Export**, use the command
+palette, or press Ctrl+Shift+E. BusyMark first
+asks you to save or discard unsaved project changes, then lets you select:
- one non-library Writerside instance;
- a generated table of contents with heading depth 1–6 and optional heading numbering;
diff --git a/docs/writerside-support.md b/docs/writerside-support.md
index 44f34804..41eadc0e 100644
--- a/docs/writerside-support.md
+++ b/docs/writerside-support.md
@@ -1,8 +1,8 @@
# Writerside support
BusyMark's semantic baseline is Writerside **2026.08.0328**, documented by
-JetBrains on September 1, 2026. Preview and native PDF export use the same
-`WritersideDocumentResolver` and `WritersideDocumentRenderer`.
+JetBrains on September 1, 2026. Reading view and native PDF export resolve the
+same supported Writerside content.
## Resolved content
@@ -42,9 +42,7 @@ JetBrains on September 1, 2026. Preview and native PDF export use the same
| Unsupported content | Visible source fallback and source-linked diagnostic | Visible fallback with a warning, or an explicit resolution failure |
Native export resolves local topic links to unique PDF destinations, including
-non-heading IDs. Source references carry resolved text, while image/resource
-locations carry paths; asset rewriting therefore cannot turn a diagram reference
-into a rejected `file:` source. Export requires saved project changes, as before.
+non-heading IDs. Export requires project changes to be saved first.
OpenAPI schema depth controls inline expansion; this editor does not generate
website pages for schema objects. File resources in a PDF remain local links,
@@ -59,41 +57,15 @@ Usages searches all indexed topics, including those outside the selected
instance. Rename verifies every source range and stages normal undoable edits in
document tabs. Existing topic-file and instance operations remain available.
-Completion filters by syntactic context and prefix before limiting results.
-Attribute values use reference kinds and, for element IDs, the referenced topic.
-The authoring registry incorporates JetBrains' official topic XSD and documented
-release overlays such as sortable tables. Recognizing an element in this
-registry does not imply a semantic renderer: unsupported content is diagnosed.
-Build-profile validation accepts `true` and rejects the old
-`single-file` form.
+Completion uses the current syntax, prefix, reference kind, and referenced topic
+to narrow its suggestions. It incorporates JetBrains' official topic XSD and
+documented release additions such as sortable tables. A recognized completion
+does not necessarily have a semantic renderer; unsupported content remains
+visible and produces a diagnostic. Build-profile validation accepts
+`true` and rejects the old `single-file` form.
-## Verification
-
-`test/src/writerside_*_test.dart` covers resolution, source preservation, file
-bounds, instance changes, widgets, editing and export. A native Typst integration
-test runs when the bundled compiler is present; otherwise it is explicitly skipped.
-API unit tests inject the existing platform parser interface; they do not claim
-to run that native parser.
-
-`test/fixtures/writerside/conformance_semantics.json` was extracted from the
-**2026.08.0328** official builder output. The fixture's builder report had 181
-passing checks with no warnings or errors. BusyMark tests compare paragraphs,
-quotes, shortcuts, tooltip descriptions, table cell structure and See also links
-against this snapshot. This is a comparison for that fixture, not a claim of
-complete website artifact parity.
-
-```bash
-flutter test --no-pub test/src/writerside*_test.dart \
- test/src/source_autocomplete_test.dart test/src/source_editor_widget_test.dart \
- test/src/workspace_controller_test.dart test/src/markdown_export_mapper_test.dart
-bash tools/validate_writerside_conformance.sh
-```
-
-The builder check uses temporary source copies and checks both the builder report
-and normalized semantic snapshot. Set `WRITERSIDE_CONFORMANCE_OUTPUT` to retain
-its generated artifacts. Schema extraction is reproducible with
-`python3 tools/generate_writerside_schema.py /path/to/topic.v2.xsd`; the generated
-file records the XSD URL and SHA-256. Format the result with `dart format`.
+Maintainers can find the conformance fixture, schema, and test workflow in
+[Writerside verification](development/writerside-verification.md).
## Official references
diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb
index 6353995b..2c6ff704 100644
--- a/lib/l10n/app_ar.arb
+++ b/lib/l10n/app_ar.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "لا تُدعم مطابقات التعبير النمطي ذات الطول الصفري (مثل ^ أو $ أو الاستباق الذي يطابق موضعًا فقط).",
+ "workspaceSearchIncomplete": "تظهر بعض النتائج فقط. اعرض المزيد أو ضيّق نطاق البحث.",
+ "workspaceSearchShowMore": "عرض المزيد من النتائج",
+ "workspaceSearchSkippedFiles": "البحث غير مكتمل. تجاوز حجم هذه الملفات 1 MiB أو تعذّرت قراءتها:",
"@@locale": "ar",
"reportIssue": "الإبلاغ عن مشكلة",
"feedbackCategory": "الفئة",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "إدراج سطر فارغ",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "نمط النص",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "جدول المحتويات",
+ "toc": "موضوعات",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "إجراءات جدول المحتويات",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "اترك عرضًا وارتفاعًا للمحتوى لا يقلان عن 20 مم بعد الهوامش.",
"exportInvalidColor": "أدخل لونًا بصيغة #RRGGBB.",
"exportInvalidCss": "اختر ملف .css مقروءًا بترميز UTF-8 حتى 256 KiB، دون استيراد أو عناوين موارد أو HTML أو CSS قابل للتنفيذ أو يحتوي على محارف هروب.",
- "exportInvalidRange": "{field}: أدخل رقمًا بين {minimum} و{maximum}."
+ "exportInvalidRange": "{field}: أدخل رقمًا بين {minimum} و{maximum}.",
+ "clipboardHistory": "سجل النسخ واللصق",
+ "clipboardHistorySearchHint": "البحث في سجل الحافظة",
+ "clipboardHistoryDescription": "افتح سجل النسخ واللصق الخاص بالجلسة.",
+ "clipboardCurrent": "النسخة واللصق الحالية",
+ "clipboardCurrentExternal": "النسخة واللصق من تطبيق آخر",
+ "clipboardDestination": "الصق في {name}",
+ "clipboardSessionOnly": "مخزن فقط لهذه جلسة BusyMark.",
+ "clipboardNoItems": "سجل النسخ واللصق فارغ.",
+ "clipboardNoMatches": "لا توجد عناصر في سجل النسخ واللصق تطابق هذا البحث.",
+ "clipboardDisabled": "مجموعة سجل النسخ واللصق معطلة في الإعدادات.",
+ "clipboardUnavailable": "محتوى النسخة واللصق الحالي غير متاح أو غير مدعوم.",
+ "clipboardTooLarge": "هذا العنصر في النسخة واللصق كبير جدًا بحيث لا يمكن الاحتفاظ به.",
+ "clipboardPastePlainText": "الصق كنص عادي",
+ "clipboardClearAll": "مسح سجل النسخ واللصق",
+ "clipboardEntryText": "نص",
+ "clipboardEntryRichText": "نص غني",
+ "clipboardEntryImage": "صورة",
+ "clipboardOrigin": "تم نسخها من {name}",
+ "localHistory": "سجل محلي",
+ "localHistoryEllipsis": "سجل محلي…",
+ "localHistoryDescription": "اعرض النسخ والمراجعات المحلية المحفوظة والآلية للمستند الحالي.",
+ "findLocalHistoryEllipsis": "البحث في التاريخ المحلي…",
+ "findLocalHistoryDescription": "البحث في مستندات التاريخ المحلي ومحتوى الإصدارات.",
+ "localHistoryNoDocuments": "لا توجد مستندات لديها تاريخ محلي.",
+ "localHistoryNoRevisions": "هذا المستند ليس لديه إصدارات في التاريخ المحلي.",
+ "localHistoryNoMatches": "لا تتطابق أي إصدارات في التاريخ المحلي مع هذا البحث.",
+ "localHistorySearchHint": "البحث في السجل المحلي",
+ "localHistorySelectedRevision": "الإصدار المحدد",
+ "localHistoryCurrentEditor": "محتوى المحرر الحالي",
+ "localHistoryCurrentDisk": "المحتوى الحالي على القرص",
+ "localHistoryMissingFile": "الملف الأصلي مفقود.",
+ "localHistoryRestoreRevision": "استعادة الإصدار",
+ "localHistoryRestoreOriginalLocation": "استعادة إلى الموقع الأصلي",
+ "localHistoryRestoreNewLocation": "استعادة إلى موقع جديد…",
+ "localHistoryRestoreChange": "استعادة التغيير",
+ "localHistoryRestoreUnavailable": "لا يمكن استعادة هذا التغيير لأن المقارنة لم تعد حديثة.",
+ "localHistoryComparisonSimplified": "تم تبسيط هذا المقارنة الكبير. لا يمكن استعادة التغييرات الفردية.",
+ "localHistoryOlderChange": "تغيير الإصدار الأقدم: {text}",
+ "localHistoryCurrentChange": "تغيير المحتوى الحالي: {text}",
+ "localHistoryDeleted": "تم الحذف",
+ "localHistoryUntitled": "غير مُسمى",
+ "localHistoryClearDocument": "مسح سجل هذا المستند",
+ "localHistoryClearAll": "مسح السجل المحلي بالكامل",
+ "localHistoryClearDocumentTitle": "هل تريد مسح سجل المحلي؟",
+ "localHistoryClearAllTitle": "هل تريد مسح السجل المحلي؟",
+ "localHistoryClearConfirmation": "هذا يزيل بشكل دائم النسخ المحفوظة. لا يمكن التراجع عن هذه العملية.",
+ "localHistoryWarningIndexRebuilt": "تم تلف فهرس السجل المحلي وإعادة بنائه من النسخ السليمة.",
+ "localHistoryWarningUnsupportedFormat": "يستخدم هذا السجل المحلي تنسيقًا غير مدعوم {version}.",
+ "localHistoryWarningUnavailable": "السجل المحلي غير متاح: {detail}",
+ "localHistoryWarningRecordingDisabled": "تم تعطيل التسجيل أو تم استبعاد هذا المسار. قم بتفعيل التسجيل مرة أخرى قبل الاستعادة.",
+ "localHistoryWarningPathChange": "لم يتمكن السجل المحلي من تسجيل تغيير المسار: {detail}",
+ "localHistoryWarningDeletedPath": "لم يتمكن السجل المحلي من الاحتفاظ بالمسار المحذوف: {detail}",
+ "localHistoryWarningRevisionMissing": "النسخة المختارة مفقودة أو تالفة.",
+ "localHistoryWarningRevisionRead": "لا يمكن قراءة النسخة المختارة: {detail}",
+ "localHistoryWarningCapture": "لم يتمكن السجل المحلي من التقاط هذه النسخة: {detail}",
+ "localHistoryRevisionAt": "{reason}، {timestamp}",
+ "localHistoryReasonBaseline": "النسخة الأساسية",
+ "localHistoryReasonSaved": "محفوظ",
+ "localHistoryReasonAutomaticCheckpoint": "نقطة فحص تلقائية",
+ "localHistoryReasonBeforeReload": "قبل إعادة التحميل",
+ "localHistoryReasonBeforeDiscard": "قبل التخلص",
+ "localHistoryReasonBeforeRestore": "قبل الاستعادة",
+ "localHistoryReasonBeforeDelete": "قبل الحذف",
+ "localHistoryReasonExternalChange": "تغيير خارجي",
+ "settingsHistory": "سجل التغييرات",
+ "settingsClipboardHistoryTitle": "جمع سجل الحافظة",
+ "settingsClipboardHistoryDescription": "احتفظ بالمحتوى المنسوخ والمُلصق بنجاح لهذه الجلسة فقط في BusyMark.",
+ "settingsLocalHistoryTitle": "تسجيل سجل محلي",
+ "settingsLocalHistoryDescription": "حفظ إصدارات المستند على هذا الجهاز للتعافي والمقارنة.",
+ "settingsHistoryCheckpoint": "فترة التحقق التلقائي",
+ "settingsHistoryRetention": "مدة الاحتفاظ",
+ "settingsHistoryStorage": "الحد الأقصى للتخزين",
+ "settingsHistoryExcludedPaths": "مسارات مستبعدة",
+ "settingsHistoryExcludedPathsHint": "مسار مطلق واحد في كل سطر",
+ "settingsSecondsValue": "{value} ثوان",
+ "settingsDaysValue": "{value} أيام",
+ "settingsMebibytesValue": "{value} ميجابايت"
}
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 27fdcb4a..f4eac6d5 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Regex-Treffer ohne Zeichen werden nicht unterstützt (z. B. ^, $ oder Lookaheads, die nur eine Position treffen).",
+ "workspaceSearchIncomplete": "Es wird nur ein Teil der Ergebnisse angezeigt. Zeigen Sie mehr an oder grenzen Sie die Suche ein.",
+ "workspaceSearchShowMore": "Weitere Ergebnisse anzeigen",
+ "workspaceSearchSkippedFiles": "Die Suche ist unvollständig. Diese Dateien sind größer als 1 MiB oder konnten nicht gelesen werden:",
"@@locale": "de",
"reportIssue": "Problem melden",
"feedbackCategory": "Kategorie",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Leerzeile einfügen",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Textstil",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Inhaltsverzeichnis",
+ "toc": "Themen",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Inhaltsverzeichnisaktionen",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "Nach Abzug der Ränder müssen mindestens 20 mm Inhaltsbreite und -höhe bleiben.",
"exportInvalidColor": "Geben Sie eine Farbe als #RRGGBB ein.",
"exportInvalidCss": "Wählen Sie eine lesbare UTF-8-CSS-Datei bis 256 KiB ohne Importe, Ressourcen-URLs, HTML sowie ausführbares oder maskiertes CSS.",
- "exportInvalidRange": "{field}: Geben Sie eine Zahl zwischen {minimum} und {maximum} ein."
+ "exportInvalidRange": "{field}: Geben Sie eine Zahl zwischen {minimum} und {maximum} ein.",
+ "clipboardHistory": "Zwischenablage-Historie",
+ "clipboardHistorySearchHint": "Zwischenablageverlauf durchsuchen",
+ "clipboardHistoryDescription": "Öffne die Sitzungs-spezifische Zwischenablage-Historie.",
+ "clipboardCurrent": "Aktuelle Zwischenablage",
+ "clipboardCurrentExternal": "Aktuelle Zwischenablage aus einer anderen Anwendung",
+ "clipboardDestination": "Einfügen in {name}",
+ "clipboardSessionOnly": "Nur für diese BusyMark-Sitzung gespeichert.",
+ "clipboardNoItems": "Zwischenablage-Historie ist leer.",
+ "clipboardNoMatches": "Keine Zwischenablagen-Elemente passen zu dieser Suche.",
+ "clipboardDisabled": "Die Sammlung \"Zwischenablage-Historie\" ist in den Einstellungen deaktiviert.",
+ "clipboardUnavailable": "Der aktuelle Zwischenablage-Inhalt ist nicht verfügbar oder wird nicht unterstützt.",
+ "clipboardTooLarge": "Dieses Zwischenablagen-Element ist zu groß, um es zu speichern.",
+ "clipboardPastePlainText": "Einfügen als reinen Text",
+ "clipboardClearAll": "Löschen der Zwischenablage-Historie",
+ "clipboardEntryText": "Textinhalt",
+ "clipboardEntryRichText": "Reicher Text",
+ "clipboardEntryImage": "Bild",
+ "clipboardOrigin": "Kopiert aus {name}",
+ "localHistory": "Lokale Historie",
+ "localHistoryEllipsis": "Lokale Historie…",
+ "localHistoryDescription": "Zeige gespeicherte und automatische lokale Revisionen für das aktuelle Dokument.",
+ "findLocalHistoryEllipsis": "In Lokaler Historie suchen…",
+ "findLocalHistoryDescription": "Lokale Historie Dokumente und Revisionen durchsuchen.",
+ "localHistoryNoDocuments": "Es gibt noch keine Dokumente mit Lokaler Historie.",
+ "localHistoryNoRevisions": "Dieses Dokument hat keine Lokalen Historie Revisionen.",
+ "localHistoryNoMatches": "Keine Lokalen Historie Revisionen passen zu dieser Suche.",
+ "localHistorySearchHint": "Lokalen Verlauf durchsuchen",
+ "localHistorySelectedRevision": "Ausgewählte Revision",
+ "localHistoryCurrentEditor": "Aktueller Editor Inhalt",
+ "localHistoryCurrentDisk": "Aktueller Inhalt auf der Festplatte",
+ "localHistoryMissingFile": "Die Originaldatei fehlt.",
+ "localHistoryRestoreRevision": "Revision Wiederherstellen",
+ "localHistoryRestoreOriginalLocation": "Zur Originalposition Wiederherstellen",
+ "localHistoryRestoreNewLocation": "Zur neuen Position Wiederherstellen…",
+ "localHistoryRestoreChange": "Änderung Wiederherstellen",
+ "localHistoryRestoreUnavailable": "Diese Änderung kann nicht wiederhergestellt werden, da der Vergleich nicht mehr aktuell ist.",
+ "localHistoryComparisonSimplified": "Dieser große Vergleich wurde vereinfacht. Einzelne Änderungen können nicht wiederhergestellt werden.",
+ "localHistoryOlderChange": "Ältere Revision Änderung: {text}",
+ "localHistoryCurrentChange": "Aktuelle Inhalt Änderung: {text}",
+ "localHistoryDeleted": "Gelöscht",
+ "localHistoryUntitled": "Unbenannt",
+ "localHistoryClearDocument": "Löschen Sie den Verlauf dieses Dokuments",
+ "localHistoryClearAll": "Löschen Sie den gesamten lokalen Verlauf",
+ "localHistoryClearDocumentTitle": "Verlauf dieses Dokuments löschen?",
+ "localHistoryClearAllTitle": "Verlauf dieses Dokuments löschen?",
+ "localHistoryClearConfirmation": "Dies entfernt dauerhaft gespeicherte Revisionen. Diese Aktion kann nicht rückgängig gemacht werden.",
+ "localHistoryWarningIndexRebuilt": "Der lokale Verlauf wurde beschädigt und aus intakten Revisionen neu aufgebaut.",
+ "localHistoryWarningUnsupportedFormat": "Dieser lokale Verlauf verwendet das nicht unterstützte Format {version}.",
+ "localHistoryWarningUnavailable": "Lokaler Verlauf ist nicht verfügbar: {detail}",
+ "localHistoryWarningRecordingDisabled": "Aufzeichnung ist deaktiviert oder dieser Pfad ist ausgeschlossen. Aktivieren Sie die Aufzeichnung erneut, bevor Sie wiederherstellen.",
+ "localHistoryWarningPathChange": "Lokaler Verlauf konnte die Pfadänderung nicht aufzeichnen: {detail}",
+ "localHistoryWarningDeletedPath": "Lokaler Verlauf konnte den gelöschten Pfad nicht speichern: {detail}",
+ "localHistoryWarningRevisionMissing": "Die ausgewählte Revision fehlt oder ist beschädigt.",
+ "localHistoryWarningRevisionRead": "Die ausgewählte Revision kann nicht gelesen werden: {detail}",
+ "localHistoryWarningCapture": "Lokaler Verlauf konnte diese Revision nicht erfassen: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Basisversion",
+ "localHistoryReasonSaved": "Gespeichert",
+ "localHistoryReasonAutomaticCheckpoint": "Automatische Checkpoint",
+ "localHistoryReasonBeforeReload": "Vor dem Neuladen",
+ "localHistoryReasonBeforeDiscard": "Vor dem Entsorgen",
+ "localHistoryReasonBeforeRestore": "Vor der Wiederherstellung",
+ "localHistoryReasonBeforeDelete": "Vor dem Löschen",
+ "localHistoryReasonExternalChange": "Externe Änderung",
+ "settingsHistory": "Verlauf",
+ "settingsClipboardHistoryTitle": "Verlauf der Zwischenablage sammeln",
+ "settingsClipboardHistoryDescription": "Behalten Sie den Inhalt, der erfolgreich kopiert und eingefügt wurde, nur für diese BusyMark-Sitzung.",
+ "settingsLocalHistoryTitle": "Lokalen Verlauf speichern",
+ "settingsLocalHistoryDescription": "Speichern Sie Dokumentversionen auf diesem Gerät zur Wiederherstellung und zum Vergleich.",
+ "settingsHistoryCheckpoint": "Automatische Checkpoint-Intervall",
+ "settingsHistoryRetention": "Aufbewahrungsdauer",
+ "settingsHistoryStorage": "Maximale Speicherkapazität",
+ "settingsHistoryExcludedPaths": "Ausschließlich Pfade",
+ "settingsHistoryExcludedPathsHint": "Ein absoluter Pfad pro Zeile",
+ "settingsSecondsValue": "{value} Sekunden",
+ "settingsDaysValue": "{value} Tage",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index f4ba2a59..c82c96a9 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -576,6 +576,8 @@
"@link": {"description": "Link command and preview label."},
"hardLineBreak": "Hard line break",
"@hardLineBreak": {"description": "Hard line break command label."},
+ "insertBlankLine": "Insert blank line",
+ "@insertBlankLine": {"description": "Insert blank line command label."},
"textStyle": "Text style",
"@textStyle": {"description": "Text style menu tooltip."},
"paragraph": "Paragraph",
@@ -700,8 +702,8 @@
},
"files": "Files",
"@files": {"description": "Files sidebar tab label."},
- "toc": "TOC",
- "@toc": {"description": "Writerside table of contents sidebar tab label."},
+ "toc": "Topics",
+ "@toc": {"description": "Writerside Topics sidebar tab label."},
"tocActions": "TOC actions",
"@tocActions": {"description": "Tooltip for the Writerside TOC action menu button."},
"markdownUnsaved": "Markdown - unsaved",
@@ -993,6 +995,10 @@
"saveAs": "Save As",
"@saveAs": {"description": "Action that saves the current document to another path."},
"sourceSearchInvalidRegex": "Invalid regular expression",
+ "sourceSearchZeroLengthUnsupported": "Zero-length regex matches are not supported (for example ^, $, or lookaheads that match only a position).",
+ "workspaceSearchIncomplete": "Only some results are shown. Show more or narrow your search.",
+ "workspaceSearchShowMore": "Show more results",
+ "workspaceSearchSkippedFiles": "Search is incomplete. These files exceeded 1 MiB or could not be read:",
"@sourceSearchInvalidRegex": {"description": "Source search status shown when the regular expression is invalid."},
"sourceLargeFileFeaturesPaused": "Large file: highlighting and folding are paused",
"@sourceLargeFileFeaturesPaused": {"description": "Status banner shown when source highlighting and folding are disabled for a large file."},
@@ -2018,7 +2024,7 @@
"shortcutExportPdfDescription": "Export the active document or Writerside module as a PDF.",
"@shortcutExportPdfDescription": {"description": "Keyboard-shortcut description for PDF export."},
"instances": "Instances",
- "@instances": {"description": "Heading for the Writerside instances shown in the TOC sidebar."},
+ "@instances": {"description": "Heading for the Writerside instances shown in the Topics sidebar."},
"newInstance": "New instance",
"@newInstance": {"description": "Action that creates a Writerside instance."},
"newTocLibrary": "New TOC library",
@@ -2445,6 +2451,97 @@
"exportInvalidGeometry": "Leave at least 20 mm of content width and height after margins.",
"exportInvalidColor": "Enter a color as #RRGGBB.",
"exportInvalidCss": "Choose a readable UTF-8 .css file up to 256 KiB, without imports, resource URLs, HTML, or executable or escaped CSS.",
+ "clipboardHistory": "Clipboard History",
+ "clipboardHistorySearchHint": "Search clipboard history",
+ "clipboardHistoryDescription": "Open the session-local clipboard history.",
+ "clipboardCurrent": "Current clipboard",
+ "clipboardCurrentExternal": "Current clipboard from another application",
+ "clipboardDestination": "Paste into {name}",
+ "@clipboardDestination": {"placeholders": {"name": {"type": "String"}}},
+ "clipboardSessionOnly": "Stored for this BusyMark session only.",
+ "clipboardNoItems": "Clipboard history is empty.",
+ "clipboardNoMatches": "No clipboard items match this search.",
+ "clipboardDisabled": "Clipboard History collection is disabled in Settings.",
+ "clipboardUnavailable": "The current clipboard content is unavailable or unsupported.",
+ "clipboardTooLarge": "This clipboard item is too large to retain.",
+ "clipboardPastePlainText": "Paste as Plain Text",
+ "clipboardClearAll": "Clear Clipboard History",
+ "clipboardEntryText": "Text",
+ "clipboardEntryRichText": "Rich text",
+ "clipboardEntryImage": "Image",
+ "clipboardOrigin": "Copied from {name}",
+ "@clipboardOrigin": {"placeholders": {"name": {"type": "String"}}},
+ "localHistory": "Local History",
+ "localHistoryEllipsis": "Local History…",
+ "localHistoryDescription": "Show saved and automatic local revisions for the current document.",
+ "findLocalHistoryEllipsis": "Find in Local History…",
+ "findLocalHistoryDescription": "Search Local History documents and revision contents.",
+ "localHistoryNoDocuments": "No documents have Local History yet.",
+ "localHistoryNoRevisions": "This document has no Local History revisions.",
+ "localHistoryNoMatches": "No Local History revisions match this search.",
+ "localHistorySearchHint": "Search local history",
+ "localHistorySelectedRevision": "Selected revision",
+ "localHistoryCurrentEditor": "Current editor content",
+ "localHistoryCurrentDisk": "Current content on disk",
+ "localHistoryMissingFile": "The original file is missing.",
+ "localHistoryRestoreRevision": "Restore Revision",
+ "localHistoryRestoreOriginalLocation": "Restore to Original Location",
+ "localHistoryRestoreNewLocation": "Restore to New Location…",
+ "localHistoryRestoreChange": "Restore Change",
+ "localHistoryRestoreUnavailable": "This change cannot be restored because the comparison is no longer current.",
+ "localHistoryComparisonSimplified": "This large comparison is simplified. Restore individual changes is unavailable.",
+ "localHistoryOlderChange": "Older revision change: {text}",
+ "@localHistoryOlderChange": {"placeholders": {"text": {"type": "String"}}},
+ "localHistoryCurrentChange": "Current content change: {text}",
+ "@localHistoryCurrentChange": {"placeholders": {"text": {"type": "String"}}},
+ "localHistoryDeleted": "Deleted",
+ "localHistoryUntitled": "Untitled",
+ "localHistoryClearDocument": "Clear This Document’s History",
+ "localHistoryClearAll": "Clear All Local History",
+ "localHistoryClearDocumentTitle": "Clear document history?",
+ "localHistoryClearAllTitle": "Clear all Local History?",
+ "localHistoryClearConfirmation": "This permanently removes stored revisions. This action cannot be undone.",
+ "localHistoryWarningIndexRebuilt": "The Local History index was damaged and rebuilt from intact revisions.",
+ "localHistoryWarningUnsupportedFormat": "This Local History store uses unsupported format {version}.",
+ "@localHistoryWarningUnsupportedFormat": {"placeholders": {"version": {"type": "String"}}},
+ "localHistoryWarningUnavailable": "Local History is unavailable: {detail}",
+ "@localHistoryWarningUnavailable": {"placeholders": {"detail": {"type": "String"}}},
+ "localHistoryWarningRecordingDisabled": "Recording is disabled or this path is excluded. Re-enable recording before restoring.",
+ "localHistoryWarningPathChange": "Local History could not record the path change: {detail}",
+ "@localHistoryWarningPathChange": {"placeholders": {"detail": {"type": "String"}}},
+ "localHistoryWarningDeletedPath": "Local History could not retain the deleted path: {detail}",
+ "@localHistoryWarningDeletedPath": {"placeholders": {"detail": {"type": "String"}}},
+ "localHistoryWarningRevisionMissing": "The selected revision is missing or damaged.",
+ "localHistoryWarningRevisionRead": "The selected revision cannot be read: {detail}",
+ "@localHistoryWarningRevisionRead": {"placeholders": {"detail": {"type": "String"}}},
+ "localHistoryWarningCapture": "Local History could not capture this revision: {detail}",
+ "@localHistoryWarningCapture": {"placeholders": {"detail": {"type": "String"}}},
+ "localHistoryRevisionAt": "{reason} · {timestamp}",
+ "@localHistoryRevisionAt": {"placeholders": {"reason": {"type": "String"}, "timestamp": {"type": "String"}}},
+ "localHistoryReasonBaseline": "Baseline",
+ "localHistoryReasonSaved": "Saved",
+ "localHistoryReasonAutomaticCheckpoint": "Automatic checkpoint",
+ "localHistoryReasonBeforeReload": "Before reload",
+ "localHistoryReasonBeforeDiscard": "Before discard",
+ "localHistoryReasonBeforeRestore": "Before restore",
+ "localHistoryReasonBeforeDelete": "Before delete",
+ "localHistoryReasonExternalChange": "External change",
+ "settingsHistory": "History",
+ "settingsClipboardHistoryTitle": "Collect Clipboard History",
+ "settingsClipboardHistoryDescription": "Keep copied and successfully pasted content for this BusyMark session only.",
+ "settingsLocalHistoryTitle": "Record Local History",
+ "settingsLocalHistoryDescription": "Store document revisions on this device for recovery and comparison.",
+ "settingsHistoryCheckpoint": "Automatic checkpoint interval",
+ "settingsHistoryRetention": "Retention age",
+ "settingsHistoryStorage": "Maximum storage",
+ "settingsHistoryExcludedPaths": "Excluded paths",
+ "settingsHistoryExcludedPathsHint": "One absolute path per line",
+ "settingsSecondsValue": "{value} seconds",
+ "@settingsSecondsValue": {"placeholders": {"value": {"type": "int"}}},
+ "settingsDaysValue": "{value} days",
+ "@settingsDaysValue": {"placeholders": {"value": {"type": "int"}}},
+ "settingsMebibytesValue": "{value} MiB",
+ "@settingsMebibytesValue": {"placeholders": {"value": {"type": "int"}}},
"exportInvalidRange": "{field}: enter a number between {minimum} and {maximum}.",
"@exportInvalidRange": {
"description": "Invalid numeric export setting.",
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index dd6fd8bf..71ede500 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "No se admiten coincidencias de longitud cero (por ejemplo, ^, $ o anticipaciones que solo coinciden con una posición).",
+ "workspaceSearchIncomplete": "Solo se muestran algunos resultados. Muestre más o limite la búsqueda.",
+ "workspaceSearchShowMore": "Mostrar más resultados",
+ "workspaceSearchSkippedFiles": "La búsqueda está incompleta. Estos archivos superan 1 MiB o no se pudieron leer:",
"@@locale": "es",
"reportIssue": "Informar de un problema",
"feedbackCategory": "Categoría",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Insertar línea en blanco",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Estilo de texto",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Índice",
+ "toc": "Temas",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Acciones del índice",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "Deje al menos 20 mm de ancho y alto de contenido después de los márgenes.",
"exportInvalidColor": "Introduzca un color como #RRGGBB.",
"exportInvalidCss": "Elija un archivo .css UTF-8 legible de hasta 256 KiB, sin importaciones, URL de recursos, HTML ni CSS ejecutable o escapado.",
- "exportInvalidRange": "{field}: introduzca un número entre {minimum} y {maximum}."
+ "exportInvalidRange": "{field}: introduzca un número entre {minimum} y {maximum}.",
+ "clipboardHistory": "Historial de la Copia",
+ "clipboardHistorySearchHint": "Buscar en el historial del portapapeles",
+ "clipboardHistoryDescription": "Abrir el historial de la copia local de la sesión.",
+ "clipboardCurrent": "Copia actual",
+ "clipboardCurrentExternal": "Copia actual de otra aplicación",
+ "clipboardDestination": "Pegar en {name}",
+ "clipboardSessionOnly": "Almacenado solo para esta sesión de BusyMark.",
+ "clipboardNoItems": "El historial de la copia está vacío.",
+ "clipboardNoMatches": "Ningún elemento de la copia coincide con esta búsqueda.",
+ "clipboardDisabled": "La colección de Historial de la Copia está deshabilitada en la configuración.",
+ "clipboardUnavailable": "El contenido de la copia actual no está disponible o no es compatible.",
+ "clipboardTooLarge": "Este elemento de la copia es demasiado grande para conservarlo.",
+ "clipboardPastePlainText": "Pegar como Texto Plano",
+ "clipboardClearAll": "Borrar el Historial de la Copia",
+ "clipboardEntryText": "Texto",
+ "clipboardEntryRichText": "Texto enriquecido",
+ "clipboardEntryImage": "Imagen",
+ "clipboardOrigin": "Copiado de {name}",
+ "localHistory": "Historial Local",
+ "localHistoryEllipsis": "Historial Local…",
+ "localHistoryDescription": "Mostrar las revisiones locales guardadas y automáticas para el documento actual.",
+ "findLocalHistoryEllipsis": "Buscar en el Historial Local…",
+ "findLocalHistoryDescription": "Buscar documentos y contenido de revisiones en el Historial Local.",
+ "localHistoryNoDocuments": "Ningún documento tiene Historial Local.",
+ "localHistoryNoRevisions": "Este documento no tiene revisiones en el Historial Local.",
+ "localHistoryNoMatches": "Ninguna revisión del Historial Local coincide con esta búsqueda.",
+ "localHistorySearchHint": "Buscar en el historial local",
+ "localHistorySelectedRevision": "Revisión seleccionada",
+ "localHistoryCurrentEditor": "Contenido del editor actual",
+ "localHistoryCurrentDisk": "Contenido actual en el disco",
+ "localHistoryMissingFile": "El archivo original está ausente.",
+ "localHistoryRestoreRevision": "Restaurar Revisión",
+ "localHistoryRestoreOriginalLocation": "Restaurar a la Ubicación Original",
+ "localHistoryRestoreNewLocation": "Restaurar a una Nueva Ubicación…",
+ "localHistoryRestoreChange": "Restaurar Cambio",
+ "localHistoryRestoreUnavailable": "Este cambio no puede ser restaurado porque la comparación ya no es válida.",
+ "localHistoryComparisonSimplified": "Esta comparación grande se ha simplificado. Restaurar cambios individuales no está disponible.",
+ "localHistoryOlderChange": "Cambio de revisión anterior: {text}",
+ "localHistoryCurrentChange": "Cambio de contenido actual: {text}",
+ "localHistoryDeleted": "Eliminado",
+ "localHistoryUntitled": "Sin título",
+ "localHistoryClearDocument": "Borrar el historial de este documento",
+ "localHistoryClearAll": "Borrar todo el historial local",
+ "localHistoryClearDocumentTitle": "¿Borrar el historial del documento?",
+ "localHistoryClearAllTitle": "¿Borrar todo el historial local?",
+ "localHistoryClearConfirmation": "Esto elimina permanentemente las revisiones almacenadas. Esta acción no se puede deshacer.",
+ "localHistoryWarningIndexRebuilt": "El índice de historial local se dañó y se reconstruyó a partir de revisiones intactas.",
+ "localHistoryWarningUnsupportedFormat": "Esta tienda de historial local utiliza un formato no soportado {version}.",
+ "localHistoryWarningUnavailable": "El historial local no está disponible: {detail}",
+ "localHistoryWarningRecordingDisabled": "La grabación está deshabilitada o esta ruta está excluida. Re-habilite la grabación antes de restaurar.",
+ "localHistoryWarningPathChange": "El historial local no pudo registrar el cambio de ruta: {detail}",
+ "localHistoryWarningDeletedPath": "El historial local no pudo conservar la ruta eliminada: {detail}",
+ "localHistoryWarningRevisionMissing": "La revisión seleccionada está ausente o dañada.",
+ "localHistoryWarningRevisionRead": "La revisión seleccionada no se puede leer: {detail}",
+ "localHistoryWarningCapture": "El historial local no pudo capturar esta revisión: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Línea base",
+ "localHistoryReasonSaved": "Guardado",
+ "localHistoryReasonAutomaticCheckpoint": "Puntos de control automático",
+ "localHistoryReasonBeforeReload": "Antes de recargar",
+ "localHistoryReasonBeforeDiscard": "Antes de descartar",
+ "localHistoryReasonBeforeRestore": "Antes de restaurar",
+ "localHistoryReasonBeforeDelete": "Antes de eliminar",
+ "localHistoryReasonExternalChange": "Cambio externo",
+ "settingsHistory": "Historial",
+ "settingsClipboardHistoryTitle": "Recolectar Historial de Copia",
+ "settingsClipboardHistoryDescription": "Mantener el contenido copiado y pegado con éxito solo para esta sesión de BusyMark.",
+ "settingsLocalHistoryTitle": "Registrar Historial Local",
+ "settingsLocalHistoryDescription": "Almacenar revisiones del documento en este dispositivo para recuperación y comparación.",
+ "settingsHistoryCheckpoint": "Intervalo de punto de control automático",
+ "settingsHistoryRetention": "Edad de retención",
+ "settingsHistoryStorage": "Almacenamiento máximo",
+ "settingsHistoryExcludedPaths": "Rutas excluidas",
+ "settingsHistoryExcludedPathsHint": "Una ruta absoluta por línea",
+ "settingsSecondsValue": "{value} segundos",
+ "settingsDaysValue": "{value} días",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_et.arb b/lib/l10n/app_et.arb
index 775a56b5..655b304e 100644
--- a/lib/l10n/app_et.arb
+++ b/lib/l10n/app_et.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Nullpikkusega regulaaravaldiste vasteid ei toetata (näiteks ^, $ või ainult asukohta sobitavad ettevaated).",
+ "workspaceSearchIncomplete": "Kuvatakse ainult osa tulemustest. Kuva rohkem või kitsenda otsingut.",
+ "workspaceSearchShowMore": "Kuva rohkem tulemusi",
+ "workspaceSearchSkippedFiles": "Otsing pole täielik. Need failid on suuremad kui 1 MiB või neid ei saanud lugeda:",
"@@locale": "et",
"reportIssue": "Teata probleemist",
"@reportIssue": {
@@ -1065,6 +1069,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Lisa tühi rida",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Tekstilaad",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1290,9 +1298,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Sisukord",
+ "toc": "Teemad",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Sisukorra toimingud",
"@tocActions": {
@@ -3604,5 +3612,82 @@
"exportInvalidGeometry": "Jäta pärast veeriseid sisule vähemalt 20 mm laiust ja kõrgust.",
"exportInvalidColor": "Sisesta värv kujul #RRGGBB.",
"exportInvalidCss": "Vali loetav UTF-8 .css-fail kuni 256 KiB, ilma importide, ressursside URL-ide, HTML-i ning käivitatava või paojadadega CSS-ita.",
- "exportInvalidRange": "{field}: sisesta arv vahemikus {minimum} kuni {maximum}."
+ "exportInvalidRange": "{field}: sisesta arv vahemikus {minimum} kuni {maximum}.",
+ "clipboardHistory": "Kleepimiskredi ajalugu",
+ "clipboardHistorySearchHint": "Otsi lõikepuhvri ajaloost",
+ "clipboardHistoryDescription": "Ava sessiooni-spetsiifiline kleepimiskredi ajalugu.",
+ "clipboardCurrent": "Praegune kleepimiskredi",
+ "clipboardCurrentExternal": "Praegune kleepimiskredi teistest rakendusest",
+ "clipboardDestination": "Kleepi {name}",
+ "clipboardSessionOnly": "Salvestatud ainult sellele BusyMark sessioonile.",
+ "clipboardNoItems": "Kleepimiskredi ajalugu on tühi.",
+ "clipboardNoMatches": "Ei leitud kleepimiskredi elemente, mis vastavad sellele otsingule.",
+ "clipboardDisabled": "Kleepimiskredi ajalugu on peatatud seadetes.",
+ "clipboardUnavailable": "Praegune kleepimiskredi sisu pole saadaval või toetatud.",
+ "clipboardTooLarge": "See kleepimiskredi element on liiga suur, et seda säilitada.",
+ "clipboardPastePlainText": "Kleepi kui tavatekst",
+ "clipboardClearAll": "Puhasta kleepimiskredi ajalugu",
+ "clipboardEntryText": "Tekst",
+ "clipboardEntryRichText": "Rikas tekst",
+ "clipboardEntryImage": "Pilt",
+ "clipboardOrigin": "Kleepitud {name}",
+ "localHistory": "Paikne ajalugu",
+ "localHistoryEllipsis": "Paikne ajalugu…",
+ "localHistoryDescription": "Näita salvestatud ja automaatseid paikseid versioone sellele dokumendile.",
+ "findLocalHistoryEllipsis": "Leia kohalikust ajaloost…",
+ "findLocalHistoryDescription": "Otsi kohalises ajaloos ja versioonide sisu.",
+ "localHistoryNoDocuments": "Mida ei ole kohalist ajaloos.",
+ "localHistoryNoRevisions": "Selle dokumendil pole kohalist ajaloos.",
+ "localHistoryNoMatches": "Ei leidunud kohalise ajalooga versioone.",
+ "localHistorySearchHint": "Otsi kohalikust ajaloost",
+ "localHistorySelectedRevision": "Valitud versioon",
+ "localHistoryCurrentEditor": "Praegune redaktoori sisu",
+ "localHistoryCurrentDisk": "Praegune sisu faile",
+ "localHistoryMissingFile": "Algne fail puudub.",
+ "localHistoryRestoreRevision": "Taasta versioon",
+ "localHistoryRestoreOriginalLocation": "Taasta algsele asukohale",
+ "localHistoryRestoreNewLocation": "Taasta uuele asukohale…",
+ "localHistoryRestoreChange": "Taasta muudatus",
+ "localHistoryRestoreUnavailable": "Seda muudatust ei saa taastada, kuna võrdlemine pole enam kehtiv.",
+ "localHistoryComparisonSimplified": "See suur võrdlemine on lihtsustatud. Eraldi muudatuste taastamine pole võimalik.",
+ "localHistoryOlderChange": "Vana versiooni muudatus: {text}",
+ "localHistoryCurrentChange": "Praeguse versiooni muudatus: {text}",
+ "localHistoryDeleted": "Hävitatud",
+ "localHistoryUntitled": "Nimetamata",
+ "localHistoryClearDocument": "Kaustade ajaloo kustutada",
+ "localHistoryClearAll": "Kõik kohalikud ajaloo kustutada",
+ "localHistoryClearDocumentTitle": "Kas soovite kustutada dokumendi ajaloo?",
+ "localHistoryClearAllTitle": "Kas soovite kustutada kõik kohaliku aja?",
+ "localHistoryClearConfirmation": "See eemaldab püsivalt salvestatud versioonid. Seda toimingut ei saa tagasi pöörata.",
+ "localHistoryWarningIndexRebuilt": "Kohalik ajaloo indeks oli kahjustatud ja taastati tervete versioonide abil.",
+ "localHistoryWarningUnsupportedFormat": "See kohalik ajaloo salvestus kasutab tugimat format {version}.",
+ "localHistoryWarningUnavailable": "Kohalik ajaloo pole saadaval: {detail}",
+ "localHistoryWarningRecordingDisabled": "Salvestamine on välja lülitatud või see tee on eemaldatud. Lülitage salvestamine uuesti sisse enne taastamist.",
+ "localHistoryWarningPathChange": "Kohalik ajaloo ei suutnud salvestada teed: {detail}",
+ "localHistoryWarningDeletedPath": "Kohalik ajaloo ei suutnud säilitada kustutatud teed: {detail}",
+ "localHistoryWarningRevisionMissing": "Valitud versioon on kadunud või kahjustatud.",
+ "localHistoryWarningRevisionRead": "Valitud versiooni ei saa lugeda: {detail}",
+ "localHistoryWarningCapture": "Kohalik ajaloo ei suutnud seda versiooni salvestada: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Alusversioon",
+ "localHistoryReasonSaved": "Salvestatud",
+ "localHistoryReasonAutomaticCheckpoint": "Automaatne kontrollpunkt",
+ "localHistoryReasonBeforeReload": "Enne laadimist",
+ "localHistoryReasonBeforeDiscard": "Enne kõrvaldamist",
+ "localHistoryReasonBeforeRestore": "Enne taastamist",
+ "localHistoryReasonBeforeDelete": "Enne kustutamist",
+ "localHistoryReasonExternalChange": "Väline muudatus",
+ "settingsHistory": "Ajalugu",
+ "settingsClipboardHistoryTitle": "Salvesta klipi ajalugu",
+ "settingsClipboardHistoryDescription": "Hoiab salvestatud ja edukalt kopeeritud sisu ainult selle BusyMark sessiooni jaoks.",
+ "settingsLocalHistoryTitle": "Salvesta kohalik ajalugu",
+ "settingsLocalHistoryDescription": "Salvesta dokumendi versioonid selle seadmele taastamiseks ja võrdlemiseks.",
+ "settingsHistoryCheckpoint": "Automaatne kontrollpunkte intervall",
+ "settingsHistoryRetention": "Säilitamise aeg",
+ "settingsHistoryStorage": "Maksimaalne salvestamine",
+ "settingsHistoryExcludedPaths": "Exkludeeritud teed",
+ "settingsHistoryExcludedPathsHint": "Üks absoluutne tee per rida",
+ "settingsSecondsValue": "{value} sekundid",
+ "settingsDaysValue": "{value} päevad",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb
index 3bff95fe..1edfe04e 100644
--- a/lib/l10n/app_fa.arb
+++ b/lib/l10n/app_fa.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "تطبیقهای با طول صفر در عبارت منظم پشتیبانی نمیشوند (مانند ^، $ یا نگاهبهجلو که فقط با یک موقعیت تطبیق دارد).",
+ "workspaceSearchIncomplete": "فقط بخشی از نتایج نمایش داده میشود. نتایج بیشتری نمایش دهید یا جستجو را محدود کنید.",
+ "workspaceSearchShowMore": "نمایش نتایج بیشتر",
+ "workspaceSearchSkippedFiles": "جستجو کامل نیست. حجم این فایلها بیش از 1 MiB است یا خواندن آنها ممکن نبود:",
"@@locale": "fa",
"reportIssue": "گزارش مشکل",
"feedbackCategory": "دستهبندی",
@@ -978,6 +982,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "درج خط خالی",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "سبک متن",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1208,9 +1216,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "فهرست مطالب",
+ "toc": "موضوعات",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "عملیات فهرست مطالب",
"@tocActions": {
@@ -3186,5 +3194,82 @@
"exportInvalidGeometry": "پس از حاشیهها دستکم ۲۰ میلیمتر عرض و ارتفاع برای محتوا باقی بگذارید.",
"exportInvalidColor": "رنگ را بهصورت #RRGGBB وارد کنید.",
"exportInvalidCss": "یک فایل .css خوانا با UTF-8 تا ۲۵۶ KiB انتخاب کنید، بدون واردسازی، نشانی منابع، HTML یا CSS اجرایی یا دارای نویسهٔ گریز.",
- "exportInvalidRange": "{field}: عددی بین {minimum} و {maximum} وارد کنید."
+ "exportInvalidRange": "{field}: عددی بین {minimum} و {maximum} وارد کنید.",
+ "clipboardHistory": "تاریخچه کپی و پیست",
+ "clipboardHistorySearchHint": "جستجو در تاریخچه کلیپبورد",
+ "clipboardHistoryDescription": "باز کردن تاریخچه کپی و پیست مخصوص جلسه.",
+ "clipboardCurrent": "کپی و پیست فعلی",
+ "clipboardCurrentExternal": "کپی و پیست از برنامه دیگر",
+ "clipboardDestination": "کپی و پیست در {name}",
+ "clipboardSessionOnly": "فقط برای این جلسه BusyMark ذخیره می شود.",
+ "clipboardNoItems": "تاریخچه کپی و پیست خالی است.",
+ "clipboardNoMatches": "هیچ آیتم کپی و پیستی با این جستجو مطابقت ندارد.",
+ "clipboardDisabled": "مجموعه تاریخچه کپی و پیست در تنظیمات غیرفعال است.",
+ "clipboardUnavailable": "محتوای کپی و پیست فعلی در دسترس نیست یا پشتیبانی نمی شود.",
+ "clipboardTooLarge": "این آیتم کپی شده خیلی بزرگ است و نمیتوان آن را نگه داشت.",
+ "clipboardPastePlainText": "به عنوان متن ساده کپی کنید",
+ "clipboardClearAll": "تاریخچه کپی را پاک کنید",
+ "clipboardEntryText": "متن",
+ "clipboardEntryRichText": "متن غنی",
+ "clipboardEntryImage": "تصویر",
+ "clipboardOrigin": "کپی شده از {name}",
+ "localHistory": "تاریخچه محلی",
+ "localHistoryEllipsis": "تاریخچه محلی…",
+ "localHistoryDescription": "نسخههای ذخیره شده و خودکار محلی را برای سند فعلی نمایش دهید.",
+ "findLocalHistoryEllipsis": "در تاریخ محلی جستجو کنید...",
+ "findLocalHistoryDescription": "اسناد و محتوای نسخههای تاریخ محلی را جستجو کنید.",
+ "localHistoryNoDocuments": "هنوز هیچ سند دارای تاریخ محلی نیست.",
+ "localHistoryNoRevisions": "این سند دارای هیچ نسخهای از تاریخ محلی نیست.",
+ "localHistoryNoMatches": "هیچ نسخهای از تاریخ محلی با این جستجو مطابقت ندارد.",
+ "localHistorySearchHint": "جستجو در تاریخچه محلی",
+ "localHistorySelectedRevision": "نسخه انتخاب شده",
+ "localHistoryCurrentEditor": "محتوای فعلی ویرایشگر",
+ "localHistoryCurrentDisk": "محتوای فعلی روی دیسک",
+ "localHistoryMissingFile": "فایل اصلی وجود ندارد.",
+ "localHistoryRestoreRevision": "بازگرداندن نسخه",
+ "localHistoryRestoreOriginalLocation": "بازگرداندن به محل اصلی",
+ "localHistoryRestoreNewLocation": "بازگرداندن به محل جدید…",
+ "localHistoryRestoreChange": "بازگرداندن تغییر",
+ "localHistoryRestoreUnavailable": "این تغییر قابل بازگشت نیست زیرا مقایسه دیگر معتبر نیست.",
+ "localHistoryComparisonSimplified": "این مقایسه بزرگ ساده شده است. بازگرداندن تغییرات فردی امکان پذیر نیست.",
+ "localHistoryOlderChange": "تغییر در نسخه قدیمی: {text}",
+ "localHistoryCurrentChange": "تغییر در محتوا فعلی: {text}",
+ "localHistoryDeleted": "حذف شده",
+ "localHistoryUntitled": "نامگذاری نشده",
+ "localHistoryClearDocument": "پاک کردن تاریخچه این سند",
+ "localHistoryClearAll": "پاک کردن تمام تاریخچه محلی",
+ "localHistoryClearDocumentTitle": "پاک کردن تاریخچه سند؟",
+ "localHistoryClearAllTitle": "پاک کردن تمام تاریخچه محلی؟",
+ "localHistoryClearConfirmation": "این به طور دائمی تغییرات ذخیره شده را حذف می کند. این عمل قابل لغو نیست.",
+ "localHistoryWarningIndexRebuilt": "ایندکس تاریخچه محلی آسیب دیده و از تغییرات سالم بازسازی شده است.",
+ "localHistoryWarningUnsupportedFormat": "این فروشگاه تاریخچه محلی از فرمت پشتیبانی نشده {version} استفاده می کند.",
+ "localHistoryWarningUnavailable": "تاریخچه محلی در دسترس نیست: {detail}",
+ "localHistoryWarningRecordingDisabled": "ضبط غیرفعال است یا این مسیر حذف شده است. قبل از بازیابی، ضبط را دوباره فعال کنید.",
+ "localHistoryWarningPathChange": "تاریخچه محلی نمی تواند تغییر مسیر را ثبت کند: {detail}",
+ "localHistoryWarningDeletedPath": "تاریخ محلی نمیتواند مسیر حذف شده را حفظ کند: {detail}",
+ "localHistoryWarningRevisionMissing": "نسخه انتخاب شده وجود ندارد یا آسیب دیده است.",
+ "localHistoryWarningRevisionRead": "نمیتوان نسخه انتخاب شده را خواند: {detail}",
+ "localHistoryWarningCapture": "تاریخ محلی نمیتواند این نسخه را ثبت کند: {detail}",
+ "localHistoryRevisionAt": "{reason}، {timestamp}",
+ "localHistoryReasonBaseline": "خط مبنا",
+ "localHistoryReasonSaved": "ذخیره شده",
+ "localHistoryReasonAutomaticCheckpoint": "نقطه تیک کنترل خودکار",
+ "localHistoryReasonBeforeReload": "قبل از بارگذاری مجدد",
+ "localHistoryReasonBeforeDiscard": "قبل از حذف",
+ "localHistoryReasonBeforeRestore": "قبل از بازیابی",
+ "localHistoryReasonBeforeDelete": "قبل از حذف",
+ "localHistoryReasonExternalChange": "تغییرات خارجی",
+ "settingsHistory": "تاریخچه",
+ "settingsClipboardHistoryTitle": "جمع آوری تاریخچه کپی شده",
+ "settingsClipboardHistoryDescription": "محتوای کپی شده و موفق به پیوستن را فقط برای این جلسه BusyMark نگه دارید.",
+ "settingsLocalHistoryTitle": "ثبت تاریخچه محلی",
+ "settingsLocalHistoryDescription": "ذخیره ویرایش های سند در این دستگاه برای بازیابی و مقایسه.",
+ "settingsHistoryCheckpoint": "فاصله زمانی خودکار",
+ "settingsHistoryRetention": "دوره نگهداری",
+ "settingsHistoryStorage": "حداکثر فضای ذخیره سازی",
+ "settingsHistoryExcludedPaths": "مسیرهای حذف شده",
+ "settingsHistoryExcludedPathsHint": "یک مسیر مطلق در هر خط",
+ "settingsSecondsValue": "{value} ثانیه",
+ "settingsDaysValue": "{value} روز",
+ "settingsMebibytesValue": "{value} مگابایت"
}
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index fa1c4bdf..7fbda41e 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Les correspondances de longueur nulle ne sont pas prises en charge (par exemple ^, $ ou les assertions qui ne correspondent qu’à une position).",
+ "workspaceSearchIncomplete": "Seule une partie des résultats est affichée. Affichez-en davantage ou affinez la recherche.",
+ "workspaceSearchShowMore": "Afficher plus de résultats",
+ "workspaceSearchSkippedFiles": "La recherche est incomplète. Ces fichiers dépassent 1 MiB ou n’ont pas pu être lus :",
"@@locale": "fr",
"reportIssue": "Signaler un problème",
"feedbackCategory": "Catégorie",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Insérer une ligne vide",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Style de texte",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Table des matières",
+ "toc": "Sujets",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Actions sur la table des matières",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "Laissez au moins 20 mm de largeur et de hauteur de contenu après les marges.",
"exportInvalidColor": "Saisissez une couleur au format #RRGGBB.",
"exportInvalidCss": "Choisissez un fichier .css UTF-8 lisible de 256 KiB maximum, sans imports, URL de ressources, HTML ni CSS exécutable ou échappé.",
- "exportInvalidRange": "{field} : saisissez un nombre entre {minimum} et {maximum}."
+ "exportInvalidRange": "{field} : saisissez un nombre entre {minimum} et {maximum}.",
+ "clipboardHistory": "Historique de la presse-papiers",
+ "clipboardHistorySearchHint": "Rechercher dans l’historique du presse-papiers",
+ "clipboardHistoryDescription": "Ouvrir l'historique de la presse-papiers local à la session.",
+ "clipboardCurrent": "Presse-papiers actuel",
+ "clipboardCurrentExternal": "Presse-papiers actuel provenant d'une autre application",
+ "clipboardDestination": "Coller dans {name}",
+ "clipboardSessionOnly": "Stocké uniquement pour cette session BusyMark.",
+ "clipboardNoItems": "L'historique de la presse-papiers est vide.",
+ "clipboardNoMatches": "Aucun élément de presse-papiers ne correspond à cette recherche.",
+ "clipboardDisabled": "La collection d'Historique de la presse-papiers est désactivée dans les paramètres.",
+ "clipboardUnavailable": "Le contenu actuel du presse-papiers n'est pas disponible ou non pris en charge.",
+ "clipboardTooLarge": "Cet élément du presse-papiers est trop grand pour être conservé.",
+ "clipboardPastePlainText": "Coller en texte brut",
+ "clipboardClearAll": "Effacer l'historique du presse-papiers",
+ "clipboardEntryText": "Texte",
+ "clipboardEntryRichText": "Texte enrichi",
+ "clipboardEntryImage": "Image du presse-papiers",
+ "clipboardOrigin": "Copié depuis {name}",
+ "localHistory": "Historique local",
+ "localHistoryEllipsis": "Historique local…",
+ "localHistoryDescription": "Afficher les révisions locales enregistrées et automatiques pour le document actuel.",
+ "findLocalHistoryEllipsis": "Rechercher dans l'Historique Local…",
+ "findLocalHistoryDescription": "Rechercher les documents et le contenu des révisions dans l'Historique Local.",
+ "localHistoryNoDocuments": "Aucun document n'a Historique Local.",
+ "localHistoryNoRevisions": "Ce document n'a pas de révisions dans l'Historique Local.",
+ "localHistoryNoMatches": "Aucune révision n'est trouvée dans cette recherche.",
+ "localHistorySearchHint": "Rechercher dans l’historique local",
+ "localHistorySelectedRevision": "Révision sélectionnée",
+ "localHistoryCurrentEditor": "Contenu de l'éditeur actuel",
+ "localHistoryCurrentDisk": "Contenu actuel sur le disque",
+ "localHistoryMissingFile": "Le fichier original est manquant.",
+ "localHistoryRestoreRevision": "Restaurer la version",
+ "localHistoryRestoreOriginalLocation": "Restaurer à l'emplacement original",
+ "localHistoryRestoreNewLocation": "Restaurer à un nouvel emplacement…",
+ "localHistoryRestoreChange": "Restaurer la modification",
+ "localHistoryRestoreUnavailable": "Cette modification ne peut pas être restaurée car la comparaison n'est plus à jour.",
+ "localHistoryComparisonSimplified": "Cette comparaison importante est simplifiée. Restaurer des modifications individuelles n'est pas possible.",
+ "localHistoryOlderChange": "Modification de la version plus ancienne: {text}",
+ "localHistoryCurrentChange": "Modification du contenu actuel: {text}",
+ "localHistoryDeleted": "Supprimé",
+ "localHistoryUntitled": "Sans titre",
+ "localHistoryClearDocument": "Effacer l'historique de ce document",
+ "localHistoryClearAll": "Effacer l'historique local",
+ "localHistoryClearDocumentTitle": "Effacer l'historique du document ?",
+ "localHistoryClearAllTitle": "Effacer l'historique local ?",
+ "localHistoryClearConfirmation": "Cela supprime définitivement les révisions stockées. Cette action ne peut pas être annulée.",
+ "localHistoryWarningIndexRebuilt": "L'index de l'historique local a été endommagé et reconstruit à partir de révisions intactes.",
+ "localHistoryWarningUnsupportedFormat": "Ce magasin d'historique local utilise un format non pris en charge {version}.",
+ "localHistoryWarningUnavailable": "L'historique local n'est pas disponible : {detail}",
+ "localHistoryWarningRecordingDisabled": "L'enregistrement est désactivé ou ce chemin est exclu. Réactivez l'enregistrement avant de restaurer.",
+ "localHistoryWarningPathChange": "L'historique local n'a pas pu enregistrer le changement de chemin : {detail}",
+ "localHistoryWarningDeletedPath": "L'historique local n'a pas pu conserver le chemin supprimé: {detail}",
+ "localHistoryWarningRevisionMissing": "La révision sélectionnée est manquante ou corrompue.",
+ "localHistoryWarningRevisionRead": "La révision sélectionnée ne peut pas être lue: {detail}",
+ "localHistoryWarningCapture": "L'historique local n'a pas pu capturer cette révision: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Base",
+ "localHistoryReasonSaved": "Sauvegardé",
+ "localHistoryReasonAutomaticCheckpoint": "Point de contrôle automatique",
+ "localHistoryReasonBeforeReload": "Avant rechargement",
+ "localHistoryReasonBeforeDiscard": "Avant suppression",
+ "localHistoryReasonBeforeRestore": "Avant la restauration",
+ "localHistoryReasonBeforeDelete": "Avant la suppression",
+ "localHistoryReasonExternalChange": "Changement externe",
+ "settingsHistory": "Historique",
+ "settingsClipboardHistoryTitle": "Collecter l'historique du presse-papier",
+ "settingsClipboardHistoryDescription": "Conserver le contenu copié et correctement collé uniquement pour cette session BusyMark.",
+ "settingsLocalHistoryTitle": "Enregistrer l'historique local",
+ "settingsLocalHistoryDescription": "Stocker les révisions du document sur cet appareil pour la récupération et la comparaison.",
+ "settingsHistoryCheckpoint": "Intervalle de sauvegarde automatique",
+ "settingsHistoryRetention": "Période de conservation",
+ "settingsHistoryStorage": "Stockage maximal",
+ "settingsHistoryExcludedPaths": "Chemins exclus",
+ "settingsHistoryExcludedPathsHint": "Un chemin absolu par ligne",
+ "settingsSecondsValue": "{value} secondes",
+ "settingsDaysValue": "{value} jours",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb
index 397b515c..8ab7c306 100644
--- a/lib/l10n/app_hi.arb
+++ b/lib/l10n/app_hi.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "शून्य लंबाई वाले रेगेक्स मिलान समर्थित नहीं हैं (जैसे ^, $ या केवल स्थिति से मेल खाने वाले लुकअहेड)।",
+ "workspaceSearchIncomplete": "केवल कुछ परिणाम दिखाए गए हैं। अधिक परिणाम देखें या खोज को सीमित करें।",
+ "workspaceSearchShowMore": "अधिक परिणाम दिखाएँ",
+ "workspaceSearchSkippedFiles": "खोज अधूरी है। ये फ़ाइलें 1 MiB से बड़ी हैं या पढ़ी नहीं जा सकीं:",
"@@locale": "hi",
"reportIssue": "समस्या की रिपोर्ट करें",
"feedbackCategory": "श्रेणी",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "खाली पंक्ति डालें",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "टेक्स्ट शैली",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "TOC",
+ "toc": "विषय",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "विषय-सूची संबंधी कार्रवाइयाँ",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "मार्जिन के बाद सामग्री के लिए कम से कम 20 mm चौड़ाई और ऊँचाई छोड़ें।",
"exportInvalidColor": "रंग #RRGGBB के रूप में दर्ज करें।",
"exportInvalidCss": "256 KiB तक की पढ़ने योग्य UTF-8 .css फ़ाइल चुनें, जिसमें इंपोर्ट, संसाधन URL, HTML और निष्पादन योग्य या एस्केप वाला CSS न हो।",
- "exportInvalidRange": "{field}: {minimum} से {maximum} के बीच संख्या दर्ज करें।"
+ "exportInvalidRange": "{field}: {minimum} से {maximum} के बीच संख्या दर्ज करें।",
+ "clipboardHistory": "क्लिपबोर्ड इतिहास",
+ "clipboardHistorySearchHint": "क्लिपबोर्ड इतिहास खोजें",
+ "clipboardHistoryDescription": "सत्र-विशिष्ट क्लिपबोर्ड इतिहास खोलें।",
+ "clipboardCurrent": "वर्तमान क्लिपबोर्ड",
+ "clipboardCurrentExternal": "किसी अन्य एप्लिकेशन से वर्तमान क्लिपबोर्ड",
+ "clipboardDestination": "{name} में पेस्ट करें",
+ "clipboardSessionOnly": "केवल इस BusyMark सत्र के लिए संग्रहीत।",
+ "clipboardNoItems": "क्लिपबोर्ड इतिहास खाली है।",
+ "clipboardNoMatches": "कोई क्लिपबोर्ड आइटम इस खोज से मेल नहीं खाता।",
+ "clipboardDisabled": "क्लिपबोर्ड इतिहास संग्रह सेटिंग्स में अक्षम है।",
+ "clipboardUnavailable": "वर्तमान क्लिपबोर्ड सामग्री अनुपलब्ध या समर्थित नहीं है।",
+ "clipboardTooLarge": "यह क्लिपबोर्ड आइटम बहुत बड़ा है और इसे बनाए रखना संभव नहीं है।",
+ "clipboardPastePlainText": "सादे टेक्स्ट के रूप में पेस्ट करें",
+ "clipboardClearAll": "क्लिपबोर्ड इतिहास साफ़ करें",
+ "clipboardEntryText": "टेक्स्ट",
+ "clipboardEntryRichText": "रिच टेक्स्ट",
+ "clipboardEntryImage": "इमेज",
+ "clipboardOrigin": "{name} से कॉपी किया गया",
+ "localHistory": "स्थानीय इतिहास",
+ "localHistoryEllipsis": "स्थानीय इतिहास…",
+ "localHistoryDescription": "वर्तमान दस्तावेज़ के लिए सहेजे गए और स्वचालित स्थानीय संशोधनों को दिखाएं।",
+ "findLocalHistoryEllipsis": "स्थानीय इतिहास में खोजें...",
+ "findLocalHistoryDescription": "स्थानीय इतिहास में दस्तावेज़ और संशोधन सामग्री खोजें।",
+ "localHistoryNoDocuments": "अभी तक किसी भी दस्तावेज़ में स्थानीय इतिहास नहीं है।",
+ "localHistoryNoRevisions": "इस दस्तावेज़ में स्थानीय इतिहास में कोई संशोधन नहीं है।",
+ "localHistoryNoMatches": "यह खोज स्थानीय इतिहास संशोधनों से मेल नहीं खाती।",
+ "localHistorySearchHint": "स्थानीय इतिहास खोजें",
+ "localHistorySelectedRevision": "चयनित संशोधन",
+ "localHistoryCurrentEditor": "वर्तमान संपादक सामग्री",
+ "localHistoryCurrentDisk": "डिस्क पर वर्तमान सामग्री",
+ "localHistoryMissingFile": "मूल फ़ाइल गायब है।",
+ "localHistoryRestoreRevision": "पिछली स्थिति को पुनर्स्थापित करें",
+ "localHistoryRestoreOriginalLocation": "मूल स्थान पर पुनर्स्थापित करें",
+ "localHistoryRestoreNewLocation": "नए स्थान पर पुनर्स्थापित करें…",
+ "localHistoryRestoreChange": "परिवर्तन को पुनर्स्थापित करें",
+ "localHistoryRestoreUnavailable": "इस परिवर्तन को पुनर्स्थापित नहीं किया जा सकता क्योंकि तुलना अब अद्यतित नहीं है।",
+ "localHistoryComparisonSimplified": "यह बड़ा परिवर्तन सरलीकृत है। व्यक्तिगत परिवर्तनों को पुनर्स्थापित करना संभव नहीं है।",
+ "localHistoryOlderChange": "पुराने संस्करण में परिवर्तन: {text}",
+ "localHistoryCurrentChange": "वर्तमान सामग्री में परिवर्तन: {text}",
+ "localHistoryDeleted": "हटा दिया गया",
+ "localHistoryUntitled": "शीर्षकहीन",
+ "localHistoryClearDocument": "इस दस्तावेज़ का इतिहास साफ़ करें",
+ "localHistoryClearAll": "सभी स्थानीय इतिहास साफ़ करें",
+ "localHistoryClearDocumentTitle": "दस्तावेज़ का इतिहास साफ़ करना?",
+ "localHistoryClearAllTitle": "सभी स्थानीय इतिहास साफ़ करना?",
+ "localHistoryClearConfirmation": "यह स्थायी रूप से संग्रहीत संशोधनों को हटा देता है। इस कार्रवाई को पूर्ववत नहीं किया जा सकता।",
+ "localHistoryWarningIndexRebuilt": "स्थानीय इतिहास इंडेक्स क्षतिग्रस्त था और बरकरार संशोधनों से फिर से बनाया गया था।",
+ "localHistoryWarningUnsupportedFormat": "यह स्थानीय इतिहास स्टोर समर्थित प्रारूप {version} का उपयोग करता है।",
+ "localHistoryWarningUnavailable": "स्थानीय इतिहास उपलब्ध नहीं: {detail}",
+ "localHistoryWarningRecordingDisabled": "रिकॉर्डिंग अक्षम है या यह पथ बाहर रखा गया है। पुनर्स्थापित करने से पहले रिकॉर्डिंग को फिर से सक्षम करें।",
+ "localHistoryWarningPathChange": "स्थानीय इतिहास ने पथ परिवर्तन रिकॉर्ड नहीं किया: {detail}",
+ "localHistoryWarningDeletedPath": "स्थानीय इतिहास को हटाए गए पथ को सहेजने में असमर्थ: {detail}",
+ "localHistoryWarningRevisionMissing": "चयनित संशोधन गायब है या क्षतिग्रस्त है।",
+ "localHistoryWarningRevisionRead": "चयनित संशोधन को नहीं पढ़ा जा सकता: {detail}",
+ "localHistoryWarningCapture": "स्थानीय इतिहास इस संशोधन को कैप्चर नहीं कर सका: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "बेसलाइन",
+ "localHistoryReasonSaved": "सहेजा गया",
+ "localHistoryReasonAutomaticCheckpoint": "स्वचालित चेकपॉइंट",
+ "localHistoryReasonBeforeReload": "रीलोड करने से पहले",
+ "localHistoryReasonBeforeDiscard": "त्यागने से पहले",
+ "localHistoryReasonBeforeRestore": "पुनर्स्थापना से पहले",
+ "localHistoryReasonBeforeDelete": "हटाने से पहले",
+ "localHistoryReasonExternalChange": "बाहरी परिवर्तन",
+ "settingsHistory": "इतिहास",
+ "settingsClipboardHistoryTitle": "क्लिपबोर्ड इतिहास एकत्र करें",
+ "settingsClipboardHistoryDescription": "केवल इस BusyMark सत्र के लिए कॉपी किए गए और सफलतापूर्वक पेस्ट किए गए सामग्री को बनाए रखें।",
+ "settingsLocalHistoryTitle": "स्थानीय इतिहास रिकॉर्ड करें",
+ "settingsLocalHistoryDescription": "पुनर्प्राप्ति और तुलना के लिए इस डिवाइस पर दस्तावेज़ के संस्करणों को सहेजें।",
+ "settingsHistoryCheckpoint": "स्वचालित चेकपॉइंट अंतराल",
+ "settingsHistoryRetention": "प्रतिधारण आयु",
+ "settingsHistoryStorage": "अधिकतम संग्रहण",
+ "settingsHistoryExcludedPaths": "बाहर किए गए पथ",
+ "settingsHistoryExcludedPathsHint": "प्रति पंक्ति एक पूर्ण पथ",
+ "settingsSecondsValue": "{value} सेकंड",
+ "settingsDaysValue": "{value} दिन",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb
index 9ee3a64a..b2535bdc 100644
--- a/lib/l10n/app_id.arb
+++ b/lib/l10n/app_id.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Kecocokan regex dengan panjang nol tidak didukung (misalnya ^, $, atau lookahead yang hanya cocok dengan posisi).",
+ "workspaceSearchIncomplete": "Hanya sebagian hasil yang ditampilkan. Tampilkan lebih banyak atau persempit pencarian.",
+ "workspaceSearchShowMore": "Tampilkan lebih banyak hasil",
+ "workspaceSearchSkippedFiles": "Pencarian belum lengkap. Berkas ini melebihi 1 MiB atau tidak dapat dibaca:",
"@@locale": "id",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Sisipkan baris kosong",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Gaya teks",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Daftar isi",
+ "toc": "Topik",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Tindakan TOC",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "Instance",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "Instance baru",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "Sisakan lebar dan tinggi isi minimal 20 mm setelah margin.",
"exportInvalidColor": "Masukkan warna sebagai #RRGGBB.",
"exportInvalidCss": "Pilih berkas .css UTF-8 yang dapat dibaca hingga 256 KiB, tanpa impor, URL sumber daya, HTML, atau CSS yang dapat dieksekusi atau memakai escape.",
- "exportInvalidRange": "{field}: masukkan angka antara {minimum} dan {maximum}."
+ "exportInvalidRange": "{field}: masukkan angka antara {minimum} dan {maximum}.",
+ "clipboardHistory": "Sejarah Tempel",
+ "clipboardHistorySearchHint": "Cari riwayat papan klip",
+ "clipboardHistoryDescription": "Buka riwayat tempel lokal sesi.",
+ "clipboardCurrent": "Tempel saat ini",
+ "clipboardCurrentExternal": "Tempel saat ini dari aplikasi lain",
+ "clipboardDestination": "Tempel ke {name}",
+ "clipboardSessionOnly": "Hanya disimpan untuk sesi BusyMark ini.",
+ "clipboardNoItems": "Riwayat tempel kosong.",
+ "clipboardNoMatches": "Tidak ada item tempel yang cocok dengan pencarian ini.",
+ "clipboardDisabled": "Koleksi Riwayat Tempel dinonaktifkan di Pengaturan.",
+ "clipboardUnavailable": "Konten tempel saat ini tidak tersedia atau tidak didukung.",
+ "clipboardTooLarge": "Item clipboard ini terlalu besar untuk disimpan.",
+ "clipboardPastePlainText": "Tempel sebagai Teks Biasa",
+ "clipboardClearAll": "Hapus Riwayat Clipboard",
+ "clipboardEntryText": "Teks",
+ "clipboardEntryRichText": "Teks kaya",
+ "clipboardEntryImage": "Gambar",
+ "clipboardOrigin": "Disalin dari {name}",
+ "localHistory": "Riwayat Lokal",
+ "localHistoryEllipsis": "Riwayat Lokal…",
+ "localHistoryDescription": "Tampilkan revisi lokal yang disimpan dan otomatis untuk dokumen saat ini.",
+ "findLocalHistoryEllipsis": "Cari di Sejarah Lokal…",
+ "findLocalHistoryDescription": "Cari dokumen dan isi revisi dalam Sejarah Lokal.",
+ "localHistoryNoDocuments": "Tidak ada dokumen yang memiliki Sejarah Lokal.",
+ "localHistoryNoRevisions": "Dokumen ini tidak memiliki revisi Sejarah Lokal.",
+ "localHistoryNoMatches": "Tidak ada revisi Sejarah Lokal yang cocok dengan pencarian ini.",
+ "localHistorySearchHint": "Cari riwayat lokal",
+ "localHistorySelectedRevision": "Revisi yang dipilih",
+ "localHistoryCurrentEditor": "Konten editor saat ini",
+ "localHistoryCurrentDisk": "Konten saat ini di disk",
+ "localHistoryMissingFile": "File asli hilang.",
+ "localHistoryRestoreRevision": "Kembalikan Revisi",
+ "localHistoryRestoreOriginalLocation": "Kembalikan ke Lokasi Asli",
+ "localHistoryRestoreNewLocation": "Kembalikan ke Lokasi Baru…",
+ "localHistoryRestoreChange": "Kembalikan Perubahan",
+ "localHistoryRestoreUnavailable": "Perubahan ini tidak dapat dikembalikan karena perbandingan tidak lagi berlaku.",
+ "localHistoryComparisonSimplified": "Perbandingan besar ini disederhanakan. Kembalikan perubahan individual tidak tersedia.",
+ "localHistoryOlderChange": "Perubahan revisi yang lebih lama: {text}",
+ "localHistoryCurrentChange": "Perubahan konten saat ini: {text}",
+ "localHistoryDeleted": "Dihapus",
+ "localHistoryUntitled": "Tanpa Judul",
+ "localHistoryClearDocument": "Hapus Riwayat Dokumen Ini",
+ "localHistoryClearAll": "Hapus Riwayat Lokal Seluruhnya",
+ "localHistoryClearDocumentTitle": "Hapus riwayat dokumen?",
+ "localHistoryClearAllTitle": "Hapus riwayat lokal seluruhnya?",
+ "localHistoryClearConfirmation": "Ini secara permanen menghapus revisi yang tersimpan. Tindakan ini tidak dapat dibatalkan.",
+ "localHistoryWarningIndexRebuilt": "Indeks Riwayat Lokal rusak dan dibangun kembali dari revisi yang utuh.",
+ "localHistoryWarningUnsupportedFormat": "Penyimpanan Riwayat Lokal menggunakan format yang tidak didukung {version}.",
+ "localHistoryWarningUnavailable": "Riwayat Lokal tidak tersedia: {detail}",
+ "localHistoryWarningRecordingDisabled": "Perekaman dinonaktifkan atau jalur ini dikecualikan. Aktifkan kembali perekaman sebelum memulihkan.",
+ "localHistoryWarningPathChange": "Riwayat Lokal tidak dapat mencatat perubahan jalur: {detail}",
+ "localHistoryWarningDeletedPath": "Sejarah Lokal tidak dapat menyimpan jalur yang dihapus: {detail}",
+ "localHistoryWarningRevisionMissing": "Revisi yang dipilih hilang atau rusak.",
+ "localHistoryWarningRevisionRead": "Revisi yang dipilih tidak dapat dibaca: {detail}",
+ "localHistoryWarningCapture": "Sejarah Lokal tidak dapat menangkap revisi ini: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Versi dasar",
+ "localHistoryReasonSaved": "Tersimpan",
+ "localHistoryReasonAutomaticCheckpoint": "Checkpoint otomatis",
+ "localHistoryReasonBeforeReload": "Sebelum memuat ulang",
+ "localHistoryReasonBeforeDiscard": "Sebelum membuang",
+ "localHistoryReasonBeforeRestore": "Sebelum pemulihan",
+ "localHistoryReasonBeforeDelete": "Sebelum menghapus",
+ "localHistoryReasonExternalChange": "Perubahan eksternal",
+ "settingsHistory": "Riwayat",
+ "settingsClipboardHistoryTitle": "Kumpulkan Riwayat Klip",
+ "settingsClipboardHistoryDescription": "Simpan konten yang disalin dan berhasil ditempel hanya untuk sesi BusyMark ini.",
+ "settingsLocalHistoryTitle": "Rekam Riwayat Lokal",
+ "settingsLocalHistoryDescription": "Simpan revisi dokumen pada perangkat ini untuk pemulihan dan perbandingan.",
+ "settingsHistoryCheckpoint": "Interval checkpoint otomatis",
+ "settingsHistoryRetention": "Masa simpan",
+ "settingsHistoryStorage": "Penyimpanan maksimum",
+ "settingsHistoryExcludedPaths": "Jalur yang dikecualikan",
+ "settingsHistoryExcludedPathsHint": "Satu jalur absolut per baris",
+ "settingsSecondsValue": "{value} detik",
+ "settingsDaysValue": "{value} hari",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb
index 2cbee3d7..e4cab1bd 100644
--- a/lib/l10n/app_it.arb
+++ b/lib/l10n/app_it.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Le corrispondenze regex di lunghezza zero non sono supportate (ad esempio ^, $ o lookahead che corrispondono solo a una posizione).",
+ "workspaceSearchIncomplete": "È mostrata solo una parte dei risultati. Mostrane altri o restringi la ricerca.",
+ "workspaceSearchShowMore": "Mostra altri risultati",
+ "workspaceSearchSkippedFiles": "La ricerca è incompleta. Questi file superano 1 MiB o non è stato possibile leggerli:",
"@@locale": "it",
"reportIssue": "Segnala un problema",
"feedbackCategory": "Categoria",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Inserisci riga vuota",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Stile del testo",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "TOC",
+ "toc": "Argomenti",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Azioni dell'indice",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "Lascia almeno 20 mm di larghezza e altezza del contenuto dopo i margini.",
"exportInvalidColor": "Inserisci un colore come #RRGGBB.",
"exportInvalidCss": "Scegli un file .css UTF-8 leggibile fino a 256 KiB, senza importazioni, URL di risorse, HTML o CSS eseguibile o con escape.",
- "exportInvalidRange": "{field}: inserisci un numero tra {minimum} e {maximum}."
+ "exportInvalidRange": "{field}: inserisci un numero tra {minimum} e {maximum}.",
+ "clipboardHistory": "Cronologia della Copia",
+ "clipboardHistorySearchHint": "Cerca nella cronologia degli appunti",
+ "clipboardHistoryDescription": "Apri la cronologia della copia locale della sessione.",
+ "clipboardCurrent": "Copia corrente",
+ "clipboardCurrentExternal": "Copia corrente da un'altra applicazione",
+ "clipboardDestination": "Incolla in {name}",
+ "clipboardSessionOnly": "Memorizzata solo per questa sessione BusyMark.",
+ "clipboardNoItems": "La cronologia della copia è vuota.",
+ "clipboardNoMatches": "Nessun elemento della copia corrisponde a questa ricerca.",
+ "clipboardDisabled": "La collezione Cronologia della Copia è disabilitata nelle Impostazioni.",
+ "clipboardUnavailable": "Il contenuto della copia corrente non è disponibile o non è supportato.",
+ "clipboardTooLarge": "Questo elemento del segnalibro è troppo grande per essere conservato.",
+ "clipboardPastePlainText": "Incolla come testo semplice",
+ "clipboardClearAll": "Cancella la cronologia del segnalibro",
+ "clipboardEntryText": "Testo",
+ "clipboardEntryRichText": "Testo ricco",
+ "clipboardEntryImage": "Immagine",
+ "clipboardOrigin": "Copiato da {name}",
+ "localHistory": "Cronologia locale",
+ "localHistoryEllipsis": "Cronologia locale…",
+ "localHistoryDescription": "Mostra le revisioni locali salvate e automatiche per il documento corrente.",
+ "findLocalHistoryEllipsis": "Trova nella cronologia locale…",
+ "findLocalHistoryDescription": "Cerca documenti e contenuti delle revisioni nella cronologia locale.",
+ "localHistoryNoDocuments": "Nessun documento ha ancora una cronologia locale.",
+ "localHistoryNoRevisions": "Questo documento non ha revisioni nella cronologia locale.",
+ "localHistoryNoMatches": "Nessuna revisione corrisponde a questa ricerca.",
+ "localHistorySearchHint": "Cerca nella cronologia locale",
+ "localHistorySelectedRevision": "Revisione selezionata",
+ "localHistoryCurrentEditor": "Contenuto dell'editor corrente",
+ "localHistoryCurrentDisk": "Contenuto corrente sul disco",
+ "localHistoryMissingFile": "Il file originale è mancante.",
+ "localHistoryRestoreRevision": "Ripristina Versione",
+ "localHistoryRestoreOriginalLocation": "Ripristina alla Posizione Originale",
+ "localHistoryRestoreNewLocation": "Ripristina alla Nuova Posizione…",
+ "localHistoryRestoreChange": "Ripristina Modifica",
+ "localHistoryRestoreUnavailable": "Questa modifica non può essere ripristinata perché il confronto non è più valido.",
+ "localHistoryComparisonSimplified": "Questo confronto ampio è semplificato. Il ripristino di singole modifiche non è disponibile.",
+ "localHistoryOlderChange": "Modifica di una versione precedente: {text}",
+ "localHistoryCurrentChange": "Modifica del contenuto corrente: {text}",
+ "localHistoryDeleted": "Eliminato",
+ "localHistoryUntitled": "Senza Titolo",
+ "localHistoryClearDocument": "Cancella la cronologia di questo documento",
+ "localHistoryClearAll": "Cancella tutta la cronologia locale",
+ "localHistoryClearDocumentTitle": "Cancella la cronologia del documento?",
+ "localHistoryClearAllTitle": "Cancella tutta la cronologia locale?",
+ "localHistoryClearConfirmation": "Questo rimuove permanentemente le revisioni memorizzate. Questa azione non può essere annullata.",
+ "localHistoryWarningIndexRebuilt": "L'indice della cronologia locale è stato danneggiato e ricostruito da revisioni intatte.",
+ "localHistoryWarningUnsupportedFormat": "Questo archivio della cronologia locale utilizza un formato non supportato {version}.",
+ "localHistoryWarningUnavailable": "La cronologia locale non è disponibile: {detail}",
+ "localHistoryWarningRecordingDisabled": "La registrazione è disabilitata o questo percorso è escluso. Riattiva la registrazione prima di ripristinare.",
+ "localHistoryWarningPathChange": "La cronologia locale non è riuscita a registrare la modifica del percorso: {detail}",
+ "localHistoryWarningDeletedPath": "La cronologia locale non è riuscita a conservare il percorso eliminato: {detail}",
+ "localHistoryWarningRevisionMissing": "La revisione selezionata è mancante o danneggiata.",
+ "localHistoryWarningRevisionRead": "La revisione selezionata non può essere letta: {detail}",
+ "localHistoryWarningCapture": "La cronologia locale non è riuscita a catturare questa revisione: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Versione iniziale",
+ "localHistoryReasonSaved": "Salvato",
+ "localHistoryReasonAutomaticCheckpoint": "Controllo automatico",
+ "localHistoryReasonBeforeReload": "Prima del ricaricamento",
+ "localHistoryReasonBeforeDiscard": "Prima di eliminare",
+ "localHistoryReasonBeforeRestore": "Prima del ripristino",
+ "localHistoryReasonBeforeDelete": "Prima della cancellazione",
+ "localHistoryReasonExternalChange": "Modifica esterna",
+ "settingsHistory": "Cronologia",
+ "settingsClipboardHistoryTitle": "Raccogli la cronologia della clipboard",
+ "settingsClipboardHistoryDescription": "Mantieni il contenuto copiato e incollato per questa sessione BusyMark.",
+ "settingsLocalHistoryTitle": "Registra la cronologia locale",
+ "settingsLocalHistoryDescription": "Salva le revisioni del documento su questo dispositivo per il recupero e il confronto.",
+ "settingsHistoryCheckpoint": "Intervallo di checkpoint automatico",
+ "settingsHistoryRetention": "Periodo di conservazione",
+ "settingsHistoryStorage": "Massimo spazio di archiviazione",
+ "settingsHistoryExcludedPaths": "Percorsi esclusi",
+ "settingsHistoryExcludedPathsHint": "Un percorso assoluto per riga",
+ "settingsSecondsValue": "{value} secondi",
+ "settingsDaysValue": "{value} giorni",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index 9ec4fc04..c0906e97 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "長さがゼロの正規表現の一致には対応していません(^、$、位置のみに一致する先読みなど)。",
+ "workspaceSearchIncomplete": "一部の結果のみ表示しています。さらに表示するか、検索を絞り込んでください。",
+ "workspaceSearchShowMore": "さらに結果を表示",
+ "workspaceSearchSkippedFiles": "検索は未完了です。次のファイルは 1 MiB を超えているか、読み取れませんでした:",
"@@locale": "ja",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "空行を挿入",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "テキストスタイル",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "目次",
+ "toc": "トピック",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "目次の操作",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "インスタンス",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "新しいインスタンス",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "余白を除いたコンテンツの幅と高さをそれぞれ 20 mm 以上にしてください。",
"exportInvalidColor": "色を #RRGGBB 形式で入力してください。",
"exportInvalidCss": "読み取り可能な UTF-8 .css ファイル(最大 256 KiB)を選択してください。インポート、リソース URL、HTML、実行可能な CSS、エスケープを含む CSS は使用できません。",
- "exportInvalidRange": "{field}: {minimum} から {maximum} の数値を入力してください。"
+ "exportInvalidRange": "{field}: {minimum} から {maximum} の数値を入力してください。",
+ "clipboardHistory": "コピーボード履歴",
+ "clipboardHistorySearchHint": "クリップボード履歴を検索",
+ "clipboardHistoryDescription": "セッション固有のコピーボード履歴を開く。",
+ "clipboardCurrent": "現在のコピーボード",
+ "clipboardCurrentExternal": "別のアプリケーションからの現在のコピーボード",
+ "clipboardDestination": "{name} に貼り付け",
+ "clipboardSessionOnly": "このBusyMarkセッションのみに保存されます。",
+ "clipboardNoItems": "コピーボード履歴は空です。",
+ "clipboardNoMatches": "この検索に一致するコピーボードの項目はありません。",
+ "clipboardDisabled": "コピーボード履歴のコレクションは設定で無効になっています。",
+ "clipboardUnavailable": "現在のコピーボードの内容は利用できませんまたはサポートされていません。",
+ "clipboardTooLarge": "このクリップボードの項目は大きすぎて保持できません。",
+ "clipboardPastePlainText": "テキストとして貼り付け",
+ "clipboardClearAll": "クリップボードの履歴をクリア",
+ "clipboardEntryText": "テキスト",
+ "clipboardEntryRichText": "豊富なテキスト",
+ "clipboardEntryImage": "画像",
+ "clipboardOrigin": "{name} からコピー",
+ "localHistory": "ローカル履歴",
+ "localHistoryEllipsis": "ローカル履歴…",
+ "localHistoryDescription": "現在のドキュメントの保存されたおよび自動ローカルのバージョンを表示します。",
+ "findLocalHistoryEllipsis": "ローカル履歴で検索…",
+ "findLocalHistoryDescription": "ローカル履歴のドキュメントとリビジョンの内容を検索します。",
+ "localHistoryNoDocuments": "まだローカル履歴を持つドキュメントはありません。",
+ "localHistoryNoRevisions": "このドキュメントにはローカル履歴の修正はありません。",
+ "localHistoryNoMatches": "この検索に一致するローカル履歴の修正はありません。",
+ "localHistorySearchHint": "ローカル履歴を検索",
+ "localHistorySelectedRevision": "選択されたリビジョン",
+ "localHistoryCurrentEditor": "現在の編集内容",
+ "localHistoryCurrentDisk": "ディスク上の現在の内容",
+ "localHistoryMissingFile": "元のファイルが見つかりません。",
+ "localHistoryRestoreRevision": "変更を復元",
+ "localHistoryRestoreOriginalLocation": "元の位置に復元",
+ "localHistoryRestoreNewLocation": "新しい位置に復元…",
+ "localHistoryRestoreChange": "変更を復元",
+ "localHistoryRestoreUnavailable": "この変更は復元できません。比較が最新ではありません。",
+ "localHistoryComparisonSimplified": "この大きな比較は簡略化されています。個別の変更の復元はできません。",
+ "localHistoryOlderChange": "以前の変更: {text}",
+ "localHistoryCurrentChange": "現在の変更: {text}",
+ "localHistoryDeleted": "削除",
+ "localHistoryUntitled": "タイトルなし",
+ "localHistoryClearDocument": "このドキュメントの履歴をクリアする",
+ "localHistoryClearAll": "すべてのローカル履歴をクリアする",
+ "localHistoryClearDocumentTitle": "ドキュメントの履歴をクリアしますか?",
+ "localHistoryClearAllTitle": "すべてのローカル履歴をクリアしますか?",
+ "localHistoryClearConfirmation": "これにより、保存されたバージョンが完全に削除されます。この操作は取り消せません。",
+ "localHistoryWarningIndexRebuilt": "ローカル履歴のインデックスが破損し、正常なバージョンから再構築されました。",
+ "localHistoryWarningUnsupportedFormat": "このローカル履歴ストアはサポートされていない形式 {version} を使用しています。",
+ "localHistoryWarningUnavailable": "ローカル履歴は利用できません: {detail}",
+ "localHistoryWarningRecordingDisabled": "録音が無効になっているか、このパスは除外されています。復元する前に録音を再度有効にしてください。",
+ "localHistoryWarningPathChange": "ローカル履歴がパスの変更を記録できませんでした: {detail}",
+ "localHistoryWarningDeletedPath": "ローカル履歴は削除されたパスを保持できません: {detail}",
+ "localHistoryWarningRevisionMissing": "選択されたバージョンが存在しないか破損しています。",
+ "localHistoryWarningRevisionRead": "選択されたバージョンを読み取ることができません: {detail}",
+ "localHistoryWarningCapture": "ローカル履歴は、このバージョンをキャプチャできません: {detail}",
+ "localHistoryRevisionAt": "{reason}・{timestamp}",
+ "localHistoryReasonBaseline": "ベースライン",
+ "localHistoryReasonSaved": "保存",
+ "localHistoryReasonAutomaticCheckpoint": "自動チェックポイント",
+ "localHistoryReasonBeforeReload": "リロード前",
+ "localHistoryReasonBeforeDiscard": "破棄前",
+ "localHistoryReasonBeforeRestore": "復元前",
+ "localHistoryReasonBeforeDelete": "削除前",
+ "localHistoryReasonExternalChange": "外部からの変更",
+ "settingsHistory": "履歴",
+ "settingsClipboardHistoryTitle": "コピー&ペースト履歴を収集",
+ "settingsClipboardHistoryDescription": "このBusyMarkセッションでのコピー&ペーストされたコンテンツのみを保持。",
+ "settingsLocalHistoryTitle": "ローカル履歴を記録",
+ "settingsLocalHistoryDescription": "このデバイスにドキュメントのバージョンを保存し、復元および比較。",
+ "settingsHistoryCheckpoint": "自動チェックポイント間隔",
+ "settingsHistoryRetention": "保管期間",
+ "settingsHistoryStorage": "最大ストレージ",
+ "settingsHistoryExcludedPaths": "除外されたパス",
+ "settingsHistoryExcludedPathsHint": "1行に1つの絶対パス",
+ "settingsSecondsValue": "{value} 秒",
+ "settingsDaysValue": "{value} 日",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb
index 7fabd224..2d097145 100644
--- a/lib/l10n/app_ko.arb
+++ b/lib/l10n/app_ko.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "길이가 0인 정규식 일치는 지원하지 않습니다(예: ^, $, 위치에만 일치하는 전방 탐색).",
+ "workspaceSearchIncomplete": "일부 결과만 표시됩니다. 더 표시하거나 검색 범위를 좁히세요.",
+ "workspaceSearchShowMore": "결과 더 보기",
+ "workspaceSearchSkippedFiles": "검색이 완료되지 않았습니다. 다음 파일은 1 MiB를 초과하거나 읽을 수 없습니다:",
"@@locale": "ko",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "빈 줄 삽입",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "텍스트 스타일",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "목차",
+ "toc": "토픽",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "목차 작업",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "인스턴스",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "새 인스턴스",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "여백을 제외한 콘텐츠 너비와 높이를 각각 20 mm 이상 확보하세요.",
"exportInvalidColor": "#RRGGBB 형식으로 색상을 입력하세요.",
"exportInvalidCss": "가져오기, 리소스 URL, HTML, 실행 가능하거나 이스케이프된 CSS가 없는 읽기 가능한 UTF-8 .css 파일(최대 256 KiB)을 선택하세요.",
- "exportInvalidRange": "{field}: {minimum}에서 {maximum} 사이의 숫자를 입력하세요."
+ "exportInvalidRange": "{field}: {minimum}에서 {maximum} 사이의 숫자를 입력하세요.",
+ "clipboardHistory": "클립보드 기록",
+ "clipboardHistorySearchHint": "클립보드 기록 검색",
+ "clipboardHistoryDescription": "세션별 클립보드 기록을 엽니다.",
+ "clipboardCurrent": "현재 클립보드",
+ "clipboardCurrentExternal": "다른 애플리케이션의 현재 클립보드",
+ "clipboardDestination": "{name}에 붙여넣기",
+ "clipboardSessionOnly": "이 BusyMark 세션에만 저장됩니다.",
+ "clipboardNoItems": "클립보드 기록이 비어 있습니다.",
+ "clipboardNoMatches": "이 검색과 일치하는 클립보드 항목이 없습니다.",
+ "clipboardDisabled": "클립보드 기록 컬렉션이 설정에서 비활성화되었습니다.",
+ "clipboardUnavailable": "현재 클립보드 내용은 사용할 수 없거나 지원되지 않습니다.",
+ "clipboardTooLarge": "이 클립보드 항목은 저장할 수 없을 정도로 큽니다.",
+ "clipboardPastePlainText": "일반 텍스트로 붙여넣기",
+ "clipboardClearAll": "클립보드 기록 지우기",
+ "clipboardEntryText": "텍스트",
+ "clipboardEntryRichText": "풍부한 텍스트",
+ "clipboardEntryImage": "이미지",
+ "clipboardOrigin": "{name}에서 복사됨",
+ "localHistory": "로컬 기록",
+ "localHistoryEllipsis": "로컬 기록…",
+ "localHistoryDescription": "현재 문서에 저장된 자동 로컬 변경 사항을 표시합니다.",
+ "findLocalHistoryEllipsis": "로컬 히스토리에서 찾기…",
+ "findLocalHistoryDescription": "로컬 히스토리 문서 및 수정 내용 검색.",
+ "localHistoryNoDocuments": "아직 로컬 히스토리가 있는 문서는 없습니다.",
+ "localHistoryNoRevisions": "이 문서는 로컬 히스토리 수정 사항이 없습니다.",
+ "localHistoryNoMatches": "이 검색과 일치하는 로컬 히스토리 수정 사항이 없습니다.",
+ "localHistorySearchHint": "로컬 기록 검색",
+ "localHistorySelectedRevision": "선택된 수정",
+ "localHistoryCurrentEditor": "현재 편집 내용",
+ "localHistoryCurrentDisk": "디스크에 있는 현재 내용",
+ "localHistoryMissingFile": "원본 파일이 없습니다.",
+ "localHistoryRestoreRevision": "이전 버전 복원",
+ "localHistoryRestoreOriginalLocation": "원래 위치로 복원",
+ "localHistoryRestoreNewLocation": "새로운 위치로 복원…",
+ "localHistoryRestoreChange": "변경 사항 복원",
+ "localHistoryRestoreUnavailable": "이 변경 사항은 더 이상 비교가 불가능합니다.",
+ "localHistoryComparisonSimplified": "이 큰 비교는 단순화되었습니다. 개별 변경 사항 복원은 불가능합니다.",
+ "localHistoryOlderChange": "이전 버전의 변경: {text}",
+ "localHistoryCurrentChange": "현재 콘텐츠의 변경: {text}",
+ "localHistoryDeleted": "삭제됨",
+ "localHistoryUntitled": "제목 없음",
+ "localHistoryClearDocument": "이 문서의 기록 삭제",
+ "localHistoryClearAll": "모든 로컬 기록 삭제",
+ "localHistoryClearDocumentTitle": "문서 기록 삭제?",
+ "localHistoryClearAllTitle": "모든 로컬 기록 삭제?",
+ "localHistoryClearConfirmation": "이로 인해 저장된 버전이 영구적으로 삭제됩니다. 이 작업은 취소할 수 없습니다.",
+ "localHistoryWarningIndexRebuilt": "로컬 기록 인덱스가 손상되어 정상적인 버전에서 재구성되었습니다.",
+ "localHistoryWarningUnsupportedFormat": "이 로컬 기록 저장소는 지원되지 않는 형식 {version}을 사용합니다.",
+ "localHistoryWarningUnavailable": "로컬 기록은 사용할 수 없습니다: {detail}",
+ "localHistoryWarningRecordingDisabled": "녹음이 비활성화되었거나 이 경로가 제외되었습니다. 복원하기 전에 녹음을 다시 활성화하십시오.",
+ "localHistoryWarningPathChange": "로컬 기록이 경로 변경을 기록할 수 없습니다: {detail}",
+ "localHistoryWarningDeletedPath": "로컬 히스토리에서 삭제된 경로를 저장할 수 없습니다: {detail}",
+ "localHistoryWarningRevisionMissing": "선택된 버전이 누락되었거나 손상되었습니다.",
+ "localHistoryWarningRevisionRead": "선택된 버전을 읽을 수 없습니다: {detail}",
+ "localHistoryWarningCapture": "로컬 히스토리가 이 버전을 캡처할 수 없습니다: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "기본 버전",
+ "localHistoryReasonSaved": "저장됨",
+ "localHistoryReasonAutomaticCheckpoint": "자동 체크포인트",
+ "localHistoryReasonBeforeReload": "재로드 전",
+ "localHistoryReasonBeforeDiscard": "버리기 전",
+ "localHistoryReasonBeforeRestore": "복원 전",
+ "localHistoryReasonBeforeDelete": "삭제 전",
+ "localHistoryReasonExternalChange": "외부 변경",
+ "settingsHistory": "버전 기록",
+ "settingsClipboardHistoryTitle": "클립보드 기록 수집",
+ "settingsClipboardHistoryDescription": "이 BusyMark 세션에만 복사 및 성공적으로 붙여넣은 콘텐츠를 유지합니다.",
+ "settingsLocalHistoryTitle": "로컬 버전 기록",
+ "settingsLocalHistoryDescription": "복구 및 비교를 위해 이 장치에 문서 버전을 저장합니다.",
+ "settingsHistoryCheckpoint": "자동 체크포인트 간격",
+ "settingsHistoryRetention": "보관 기간",
+ "settingsHistoryStorage": "최대 저장 공간",
+ "settingsHistoryExcludedPaths": "제외된 경로",
+ "settingsHistoryExcludedPathsHint": "한 줄에 하나의 절대 경로",
+ "settingsSecondsValue": "{value} 초",
+ "settingsDaysValue": "{value} 일",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_nb.arb b/lib/l10n/app_nb.arb
index b7153c3d..2a40bf80 100644
--- a/lib/l10n/app_nb.arb
+++ b/lib/l10n/app_nb.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Regex-treff med null lengde støttes ikke (for eksempel ^, $ eller fremoversøk som bare samsvarer med en posisjon).",
+ "workspaceSearchIncomplete": "Bare noen av resultatene vises. Vis flere eller avgrens søket.",
+ "workspaceSearchShowMore": "Vis flere resultater",
+ "workspaceSearchSkippedFiles": "Søket er ufullstendig. Disse filene er større enn 1 MiB eller kunne ikke leses:",
"@@locale": "nb",
"reportIssue": "Rapporter et problem",
"feedbackCategory": "Kategori",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Sett inn tom linje",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Tekststil",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Innholdsfortegnelse",
+ "toc": "Emner",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Handlinger for innholdsfortegnelsen",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "La minst 20 mm innholdsbredde og -høyde stå igjen etter margene.",
"exportInvalidColor": "Skriv inn en farge som #RRGGBB.",
"exportInvalidCss": "Velg en lesbar UTF-8 .css-fil opptil 256 KiB uten importer, ressurs-URL-er, HTML eller kjørbar eller escaped CSS.",
- "exportInvalidRange": "{field}: skriv inn et tall mellom {minimum} og {maximum}."
+ "exportInvalidRange": "{field}: skriv inn et tall mellom {minimum} og {maximum}.",
+ "clipboardHistory": "Utklippshistorie",
+ "clipboardHistorySearchHint": "Søk i utklippstavlehistorikken",
+ "clipboardHistoryDescription": "Åpne historien for utklippet som er lokal for sesjonen.",
+ "clipboardCurrent": "Nåværende utklipp",
+ "clipboardCurrentExternal": "Nåværende utklipp fra en annen applikasjon",
+ "clipboardDestination": "Lim inn i {name}",
+ "clipboardSessionOnly": "Lagret kun for denne BusyMark-sesjonen.",
+ "clipboardNoItems": "Utklippshistorien er tom.",
+ "clipboardNoMatches": "Ingen utklippsposter samsvarer med denne søkingen.",
+ "clipboardDisabled": "Samlingen \"Utklippshistorie\" er deaktivert i Innstillinger.",
+ "clipboardUnavailable": "Innholdet i det nåværende utklippet er ikke tilgjengelig eller støttet.",
+ "clipboardTooLarge": "Denne utklippsteksten er for stor til å lagre.",
+ "clipboardPastePlainText": "Lim inn som ren tekst",
+ "clipboardClearAll": "Tøm utklippshistorie",
+ "clipboardEntryText": "Tekst",
+ "clipboardEntryRichText": "Rik tekst",
+ "clipboardEntryImage": "Bilde",
+ "clipboardOrigin": "Kopiert fra {name}",
+ "localHistory": "Lokal historie",
+ "localHistoryEllipsis": "Lokal historie…",
+ "localHistoryDescription": "Vis lagrede og automatiserte lokale versjoner for det nåværende dokumentet.",
+ "findLocalHistoryEllipsis": "Finn i Lokal Historikk…",
+ "findLocalHistoryDescription": "Søk etter dokumenter og innhold i Lokal Historikk.",
+ "localHistoryNoDocuments": "Ingen dokumenter har Lokal Historikk ennå.",
+ "localHistoryNoRevisions": "Dette dokumentet har ingen revisjoner i Lokal Historikk.",
+ "localHistoryNoMatches": "Ingen revisjoner matcher denne søken.",
+ "localHistorySearchHint": "Søk i lokal historikk",
+ "localHistorySelectedRevision": "Valgt revisjon",
+ "localHistoryCurrentEditor": "Nåværende innhold i redigereren",
+ "localHistoryCurrentDisk": "Nåværende innhold på disken",
+ "localHistoryMissingFile": "Den opprinnelige filen mangler.",
+ "localHistoryRestoreRevision": "Gjenopprett Versjon",
+ "localHistoryRestoreOriginalLocation": "Gjenopprett til Opprinnelig Sted",
+ "localHistoryRestoreNewLocation": "Gjenopprett til Nytt Sted…",
+ "localHistoryRestoreChange": "Gjenopprett Endring",
+ "localHistoryRestoreUnavailable": "Denne endringen kan ikke gjenopprettes fordi sammenligningen er ikke lenger aktuell.",
+ "localHistoryComparisonSimplified": "Denne store sammenligningen er forenklet. Gjenopprett individuelle endringer er ikke tilgjengelig.",
+ "localHistoryOlderChange": "Eldre versjonsendring: {text}",
+ "localHistoryCurrentChange": "Nåværende innholdsendring: {text}",
+ "localHistoryDeleted": "Slettet",
+ "localHistoryUntitled": "Uten tittel",
+ "localHistoryClearDocument": "Tøm historikken for dette dokumentet",
+ "localHistoryClearAll": "Tøm all lokal historikk",
+ "localHistoryClearDocumentTitle": "Tøm dokumentets historikk?",
+ "localHistoryClearAllTitle": "Tøm all lokal historikk?",
+ "localHistoryClearConfirmation": "Dette sletter permanent lagrede versjoner. Denne handlingen kan ikke angres.",
+ "localHistoryWarningIndexRebuilt": "Lokal historikk indeksen ble skadet og gjenopprettet fra intakte versjoner.",
+ "localHistoryWarningUnsupportedFormat": "Denne lokale historikk-lagringen bruker et støttet format {version}.",
+ "localHistoryWarningUnavailable": "Lokal historikk er ikke tilgjengelig: {detail}",
+ "localHistoryWarningRecordingDisabled": "Opptak er deaktivert eller denne stien er utelatt. Aktiver opptak før gjenoppretting.",
+ "localHistoryWarningPathChange": "Lokal historikk kunne ikke registrere endringen av stien: {detail}",
+ "localHistoryWarningDeletedPath": "Lokal Historikk kunne ikke lagre den slettede stien: {detail}",
+ "localHistoryWarningRevisionMissing": "Den valgte versjonen mangler eller er skadet.",
+ "localHistoryWarningRevisionRead": "Den valgte versjonen kan ikke leses: {detail}",
+ "localHistoryWarningCapture": "Lokal Historikk kunne ikke fange opp denne versjonen: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Baselinje",
+ "localHistoryReasonSaved": "Lagret",
+ "localHistoryReasonAutomaticCheckpoint": "Automatisk sjekkpunkt",
+ "localHistoryReasonBeforeReload": "Før omlasting",
+ "localHistoryReasonBeforeDiscard": "Før avvisning",
+ "localHistoryReasonBeforeRestore": "Før gjenoppbygging",
+ "localHistoryReasonBeforeDelete": "Før sletting",
+ "localHistoryReasonExternalChange": "Ekstern endring",
+ "settingsHistory": "Historikk",
+ "settingsClipboardHistoryTitle": "Samle Historikk fra utklippstavlen",
+ "settingsClipboardHistoryDescription": "Behold kopiert og vellykket limt innhold kun for denne BusyMark-sesjonen.",
+ "settingsLocalHistoryTitle": "Lag lokal historikk",
+ "settingsLocalHistoryDescription": "Lagre dokumentversjoner på denne enheten for gjenoppretting og sammenligning.",
+ "settingsHistoryCheckpoint": "Automatisk sjekkpunktintervall",
+ "settingsHistoryRetention": "Bevaringsperiode",
+ "settingsHistoryStorage": "Maks lagringsplass",
+ "settingsHistoryExcludedPaths": "Udelte stier",
+ "settingsHistoryExcludedPathsHint": "Én absolutt sti per linje",
+ "settingsSecondsValue": "{value} sekunder",
+ "settingsDaysValue": "{value} dager",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb
index 4b04b23c..ddd73fe1 100644
--- a/lib/l10n/app_nl.arb
+++ b/lib/l10n/app_nl.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Regex-overeenkomsten met lengte nul worden niet ondersteund (zoals ^, $ of lookaheads die alleen een positie vinden).",
+ "workspaceSearchIncomplete": "Slechts een deel van de resultaten wordt getoond. Toon meer of verfijn de zoekopdracht.",
+ "workspaceSearchShowMore": "Meer resultaten tonen",
+ "workspaceSearchSkippedFiles": "De zoekopdracht is onvolledig. Deze bestanden zijn groter dan 1 MiB of konden niet worden gelezen:",
"@@locale": "nl",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Lege regel invoegen",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Tekststijl",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "TOC",
+ "toc": "Onderwerpen",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "TOC-acties",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "Instanties",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "Nieuwe instantie",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "Laat na de marges minimaal 20 mm inhoudsbreedte en -hoogte over.",
"exportInvalidColor": "Voer een kleur in als #RRGGBB.",
"exportInvalidCss": "Kies een leesbaar UTF-8 .css-bestand tot 256 KiB zonder imports, bron-URL’s, HTML of uitvoerbare of ontsnapte CSS.",
- "exportInvalidRange": "{field}: voer een getal tussen {minimum} en {maximum} in."
+ "exportInvalidRange": "{field}: voer een getal tussen {minimum} en {maximum} in.",
+ "clipboardHistory": "Historie van de klembord",
+ "clipboardHistorySearchHint": "Klembordgeschiedenis doorzoeken",
+ "clipboardHistoryDescription": "Open de klembordgeschiedenis per sessie.",
+ "clipboardCurrent": "Huidig klembord",
+ "clipboardCurrentExternal": "Huidig klembord van een ander programma",
+ "clipboardDestination": "Plakken in {name}",
+ "clipboardSessionOnly": "Alleen opgeslagen voor deze BusyMark sessie.",
+ "clipboardNoItems": "De klembordgeschiedenis is leeg.",
+ "clipboardNoMatches": "Geen klemborditems komen overeen met deze zoekopdracht.",
+ "clipboardDisabled": "De collectie \"Historie van het klembord\" is uitgeschakeld in de instellingen.",
+ "clipboardUnavailable": "De inhoud van het huidige klembord is niet beschikbaar of niet ondersteund.",
+ "clipboardTooLarge": "Dit item op het klembord is te groot om te bewaren.",
+ "clipboardPastePlainText": "Plak als gewone tekst",
+ "clipboardClearAll": "Maak de geschiedenis van het klembord leeg",
+ "clipboardEntryText": "Tekst",
+ "clipboardEntryRichText": "Rijke tekst",
+ "clipboardEntryImage": "Afbeelding",
+ "clipboardOrigin": "Geplakt van {name}",
+ "localHistory": "Lokale geschiedenis",
+ "localHistoryEllipsis": "Lokale geschiedenis…",
+ "localHistoryDescription": "Toon opgeslagen en automatische lokale revisies voor het huidige document.",
+ "findLocalHistoryEllipsis": "Zoeken in Lokale Geschiedenis…",
+ "findLocalHistoryDescription": "Zoek naar documenten en inhoud van revisies in de lokale geschiedenis.",
+ "localHistoryNoDocuments": "Er zijn nog geen documenten met Lokale Geschiedenis.",
+ "localHistoryNoRevisions": "Dit document heeft geen revisies van de Lokale Geschiedenis.",
+ "localHistoryNoMatches": "Geen revisies van de Lokale Geschiedenis komen overeen met deze zoekopdracht.",
+ "localHistorySearchHint": "Lokale geschiedenis doorzoeken",
+ "localHistorySelectedRevision": "Geselecteerde revisie",
+ "localHistoryCurrentEditor": "Huidige inhoud van de editor",
+ "localHistoryCurrentDisk": "Huidige inhoud op schijf",
+ "localHistoryMissingFile": "Het originele bestand ontbreekt.",
+ "localHistoryRestoreRevision": "Herstel Versie",
+ "localHistoryRestoreOriginalLocation": "Herstel naar Originele Locatie",
+ "localHistoryRestoreNewLocation": "Herstel naar Nieuwe Locatie…",
+ "localHistoryRestoreChange": "Herstel Wijziging",
+ "localHistoryRestoreUnavailable": "Deze wijziging kan niet worden hersteld omdat de vergelijking niet meer actueel is.",
+ "localHistoryComparisonSimplified": "Deze grote vergelijking is vereenvoudigd. Het herstellen van individuele wijzigingen is niet mogelijk.",
+ "localHistoryOlderChange": "Oudere versie wijziging: {text}",
+ "localHistoryCurrentChange": "Huidige inhoud wijziging: {text}",
+ "localHistoryDeleted": "Verwijderd",
+ "localHistoryUntitled": "Onbenoemd",
+ "localHistoryClearDocument": "Verwijder de geschiedenis van dit document",
+ "localHistoryClearAll": "Verwijder de lokale geschiedenis",
+ "localHistoryClearDocumentTitle": "Verwijder de documentgeschiedenis?",
+ "localHistoryClearAllTitle": "Verwijder de lokale geschiedenis?",
+ "localHistoryClearConfirmation": "Dit verwijdert permanent opgeslagen revisies. Deze actie kan niet worden teruggedraaid.",
+ "localHistoryWarningIndexRebuilt": "De index van de lokale geschiedenis is beschadigd en opnieuw gebouwd vanuit intacte revisies.",
+ "localHistoryWarningUnsupportedFormat": "Deze lokale geschiedenisopslag gebruikt een onondersteunde {version}-formaat.",
+ "localHistoryWarningUnavailable": "De lokale geschiedenis is niet beschikbaar: {detail}",
+ "localHistoryWarningRecordingDisabled": "Opnemen is uitgeschakeld of deze pad is uitgesloten. Schakel opnemen opnieuw in voordat u herstelt.",
+ "localHistoryWarningPathChange": "De lokale geschiedenis kon de padwijziging niet opslaan: {detail}",
+ "localHistoryWarningDeletedPath": "Lokale Geschiedenis kon de verwijderde pad niet opslaan: {detail}",
+ "localHistoryWarningRevisionMissing": "De geselecteerde versie ontbreekt of is beschadigd.",
+ "localHistoryWarningRevisionRead": "De geselecteerde versie kan niet worden gelezen: {detail}",
+ "localHistoryWarningCapture": "Lokale Geschiedenis kon deze versie niet vastleggen: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Basislijn",
+ "localHistoryReasonSaved": "Bewaard",
+ "localHistoryReasonAutomaticCheckpoint": "Automatische controlepunt",
+ "localHistoryReasonBeforeReload": "Voor herladen",
+ "localHistoryReasonBeforeDiscard": "Voor verwijderen",
+ "localHistoryReasonBeforeRestore": "Voordat herstellen",
+ "localHistoryReasonBeforeDelete": "Voordat verwijderen",
+ "localHistoryReasonExternalChange": "Externe wijziging",
+ "settingsHistory": "Geschiedenis",
+ "settingsClipboardHistoryTitle": "Verzamelen van de geschiedenis van de klembord",
+ "settingsClipboardHistoryDescription": "Behoud van gekopieerd en succesvol geplakt materiaal alleen voor deze BusyMark sessie.",
+ "settingsLocalHistoryTitle": "Opnemen van lokale geschiedenis",
+ "settingsLocalHistoryDescription": "Opslaan van documentversies op dit apparaat voor herstel en vergelijking.",
+ "settingsHistoryCheckpoint": "Automatische interval voor controlepunten",
+ "settingsHistoryRetention": "Bewaarperiode",
+ "settingsHistoryStorage": "Maximale opslag",
+ "settingsHistoryExcludedPaths": "Uitsluitingen",
+ "settingsHistoryExcludedPathsHint": "Eén absolute pad per regel",
+ "settingsSecondsValue": "{value} seconden",
+ "settingsDaysValue": "{value} dagen",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb
index d6d3ae63..bb55ee60 100644
--- a/lib/l10n/app_pl.arb
+++ b/lib/l10n/app_pl.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Dopasowania wyrażeń regularnych o zerowej długości nie są obsługiwane (np. ^, $ lub asercje dopasowujące tylko pozycję).",
+ "workspaceSearchIncomplete": "Wyświetlono tylko część wyników. Pokaż więcej lub zawęź wyszukiwanie.",
+ "workspaceSearchShowMore": "Pokaż więcej wyników",
+ "workspaceSearchSkippedFiles": "Wyszukiwanie jest niepełne. Te pliki przekraczają 1 MiB lub nie można ich odczytać:",
"@@locale": "pl",
"reportIssue": "Zgłoś problem",
"feedbackCategory": "Kategoria",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Wstaw pusty wiersz",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Styl tekstu",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Spis treści",
+ "toc": "Tematy",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Działania dotyczące spisu treści",
"@tocActions": {
@@ -3170,5 +3178,82 @@
"exportInvalidGeometry": "Po odjęciu marginesów pozostaw co najmniej 20 mm szerokości i wysokości treści.",
"exportInvalidColor": "Wpisz kolor w formacie #RRGGBB.",
"exportInvalidCss": "Wybierz czytelny plik .css UTF-8 do 256 KiB, bez importów, adresów zasobów, HTML oraz wykonywalnego CSS i sekwencji ucieczki.",
- "exportInvalidRange": "{field}: wpisz liczbę między {minimum} a {maximum}."
+ "exportInvalidRange": "{field}: wpisz liczbę między {minimum} a {maximum}.",
+ "clipboardHistory": "Historia w Koszyku",
+ "clipboardHistorySearchHint": "Przeszukaj historię schowka",
+ "clipboardHistoryDescription": "Otwórz historię w koszyku lokalną dla sesji.",
+ "clipboardCurrent": "Aktualny koszyk",
+ "clipboardCurrentExternal": "Aktualny koszyk z innego programu",
+ "clipboardDestination": "Wklej do {name}",
+ "clipboardSessionOnly": "Przechowywane tylko dla tej sesji BusyMark.",
+ "clipboardNoItems": "Historia w koszyku jest pusta.",
+ "clipboardNoMatches": "Żadne elementy w koszyku nie pasują do tego wyszukiwania.",
+ "clipboardDisabled": "Zbiór \"Historia w koszyku\" wyłączony w Ustawieniach.",
+ "clipboardUnavailable": "Aktualny zawartość koszyka niedostępna lub nieobsługiwana.",
+ "clipboardTooLarge": "Ten element schowka jest zbyt duży, aby go zapisać.",
+ "clipboardPastePlainText": "Wklej jako zwykły tekst",
+ "clipboardClearAll": "Wyczyść historię schowka",
+ "clipboardEntryText": "Tekst",
+ "clipboardEntryRichText": "Bogaty tekst",
+ "clipboardEntryImage": "Obraz",
+ "clipboardOrigin": "Skopiowano z {name}",
+ "localHistory": "Lokalna historia",
+ "localHistoryEllipsis": "Lokalna historia…",
+ "localHistoryDescription": "Pokaż zapisane i automatyczne lokalne wersje dla bieżącego dokumentu.",
+ "findLocalHistoryEllipsis": "Znajdź w Lokalnej Historii…",
+ "findLocalHistoryDescription": "Wyszukaj dokumenty i zawartość wersji w Lokalnej Historii.",
+ "localHistoryNoDocuments": "Żaden dokument nie ma Lokalnej Historii.",
+ "localHistoryNoRevisions": "Ten dokument nie ma wersji w Lokalnej Historii.",
+ "localHistoryNoMatches": "Żadne wersje nie pasują do tego wyszukiwania.",
+ "localHistorySearchHint": "Przeszukaj historię lokalną",
+ "localHistorySelectedRevision": "Wybrana wersja",
+ "localHistoryCurrentEditor": "Aktualny zawartość edytora",
+ "localHistoryCurrentDisk": "Aktualna zawartość na dysku",
+ "localHistoryMissingFile": "Oryginalny plik nie istnieje.",
+ "localHistoryRestoreRevision": "Przywróć Wersję",
+ "localHistoryRestoreOriginalLocation": "Przywróć do Oryginalnej Lokalizacji",
+ "localHistoryRestoreNewLocation": "Przywróć do Nowej Lokalizacji…",
+ "localHistoryRestoreChange": "Przywróć Zmianę",
+ "localHistoryRestoreUnavailable": "Ta zmiana nie może być przywrócona, ponieważ porównanie nie jest już aktualne.",
+ "localHistoryComparisonSimplified": "To duże porównanie zostało uproszczone. Przywracanie pojedynczych zmian jest niedostępne.",
+ "localHistoryOlderChange": "Stara wersja zmiany: {text}",
+ "localHistoryCurrentChange": "Aktualna zmiana zawartości: {text}",
+ "localHistoryDeleted": "Usunięto",
+ "localHistoryUntitled": "Bez tytułu",
+ "localHistoryClearDocument": "Wyczyść historię tego dokumentu",
+ "localHistoryClearAll": "Wyczyść całą lokalną historię",
+ "localHistoryClearDocumentTitle": "Czy chcesz wyczyścić historię dokumentu?",
+ "localHistoryClearAllTitle": "Czy chcesz wyczyścić całą lokalną historię?",
+ "localHistoryClearConfirmation": "To trwale usuwa zapisane wersje. Ta akcja nie może być cofnięta.",
+ "localHistoryWarningIndexRebuilt": "Indeks Lokalnej Historii został uszkodzony i odtworzony z nieuszkodzonych wersji.",
+ "localHistoryWarningUnsupportedFormat": "Ta Lokalna Historia używa nieobsługiwanego formatu {version}.",
+ "localHistoryWarningUnavailable": "Lokalna Historia jest niedostępna: {detail}",
+ "localHistoryWarningRecordingDisabled": "Nagrywanie jest wyłączone lub ta ścieżka jest wykluczona. Włącz nagrywanie przed przywracaniem.",
+ "localHistoryWarningPathChange": "Lokalna Historia nie mogła zarejestrować zmiany ścieżki: {detail}",
+ "localHistoryWarningDeletedPath": "Lokalna historia nie mogła zachować usuniętego ścieżki: {detail}",
+ "localHistoryWarningRevisionMissing": "Wybrana wersja jest nieobecna lub uszkodzona.",
+ "localHistoryWarningRevisionRead": "Wybrana wersja nie może być odczytana: {detail}",
+ "localHistoryWarningCapture": "Lokalna historia nie mogła przechwycić tej wersji: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Podstawa",
+ "localHistoryReasonSaved": "Zapisano",
+ "localHistoryReasonAutomaticCheckpoint": "Automatyczny checkpoint",
+ "localHistoryReasonBeforeReload": "Przed ponownym załadowaniem",
+ "localHistoryReasonBeforeDiscard": "Przed wyrzuceniem",
+ "localHistoryReasonBeforeRestore": "Przed przywróceniem",
+ "localHistoryReasonBeforeDelete": "Przed usunięciem",
+ "localHistoryReasonExternalChange": "Zmiana z zewnątrz",
+ "settingsHistory": "Historia",
+ "settingsClipboardHistoryTitle": "Zapisywanie historii w schowku",
+ "settingsClipboardHistoryDescription": "Zachowaj skopiowany i pomyślnie wklejony zawartość tylko dla tej sesji BusyMark.",
+ "settingsLocalHistoryTitle": "Zapisywanie lokalnej historii",
+ "settingsLocalHistoryDescription": "Przechowywanie wersji dokumentu na tym urządzeniu w celu odzyskania i porównywania.",
+ "settingsHistoryCheckpoint": "Automatyczny interwał kontrolnego zapisu",
+ "settingsHistoryRetention": "Okres przechowywania",
+ "settingsHistoryStorage": "Maksymalna pojemność",
+ "settingsHistoryExcludedPaths": "Wykluczone ścieżki",
+ "settingsHistoryExcludedPathsHint": "Jedna absolutna ścieżka na linię",
+ "settingsSecondsValue": "{value} sekundy",
+ "settingsDaysValue": "{value} dni",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index ce9a07e6..9287c2a0 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Não são suportadas correspondências regex de comprimento zero (por exemplo, ^, $ ou antecipações que correspondem apenas a uma posição).",
+ "workspaceSearchIncomplete": "Só são apresentados alguns resultados. Mostre mais ou restrinja a pesquisa.",
+ "workspaceSearchShowMore": "Mostrar mais resultados",
+ "workspaceSearchSkippedFiles": "A pesquisa está incompleta. Estes ficheiros excedem 1 MiB ou não foi possível lê-los:",
"@@locale": "pt",
"reportIssue": "Comunicar um problema",
"feedbackCategory": "Categoria",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Inserir linha em branco",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Estilo de texto",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "TOC",
+ "toc": "Tópicos",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Ações do sumário",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "Deixe pelo menos 20 mm de largura e altura de conteúdo após as margens.",
"exportInvalidColor": "Introduza uma cor como #RRGGBB.",
"exportInvalidCss": "Escolha um ficheiro .css UTF-8 legível até 256 KiB, sem importações, URL de recursos, HTML ou CSS executável ou com escapes.",
- "exportInvalidRange": "{field}: introduza um número entre {minimum} e {maximum}."
+ "exportInvalidRange": "{field}: introduza um número entre {minimum} e {maximum}.",
+ "clipboardHistory": "Histórico da Área de Transferência",
+ "clipboardHistorySearchHint": "Pesquisar no histórico da área de transferência",
+ "clipboardHistoryDescription": "Abrir o histórico da área de transferência local da sessão.",
+ "clipboardCurrent": "Área de transferência atual",
+ "clipboardCurrentExternal": "Área de transferência de outro aplicativo",
+ "clipboardDestination": "Colar em {name}",
+ "clipboardSessionOnly": "Armazenado apenas para esta sessão BusyMark.",
+ "clipboardNoItems": "O histórico da área de transferência está vazio.",
+ "clipboardNoMatches": "Nenhum item da área de transferência corresponde a esta pesquisa.",
+ "clipboardDisabled": "A coleção de Histórico da Área de Transferência está desativada nas Configurações.",
+ "clipboardUnavailable": "O conteúdo da área de transferência atual não está disponível ou não é suportado.",
+ "clipboardTooLarge": "Este item na área de transferência é demasiado grande para manter.",
+ "clipboardPastePlainText": "Colar como Texto Simples",
+ "clipboardClearAll": "Limpar o Histórico da Área de Transferência",
+ "clipboardEntryText": "Texto",
+ "clipboardEntryRichText": "Texto formatado",
+ "clipboardEntryImage": "Imagem",
+ "clipboardOrigin": "Copiado de {name}",
+ "localHistory": "Histórico Local",
+ "localHistoryEllipsis": "Histórico Local…",
+ "localHistoryDescription": "Mostrar as revisões locais salvas e automáticas para o documento atual.",
+ "findLocalHistoryEllipsis": "Encontrar no Histórico Local…",
+ "findLocalHistoryDescription": "Procurar documentos e conteúdos de revisão no Histórico Local.",
+ "localHistoryNoDocuments": "Nenhum documento tem Histórico Local.",
+ "localHistoryNoRevisions": "Este documento não tem revisões no Histórico Local.",
+ "localHistoryNoMatches": "Nenhuma revisão corresponde à pesquisa.",
+ "localHistorySearchHint": "Pesquisar no histórico local",
+ "localHistorySelectedRevision": "Revisão selecionada",
+ "localHistoryCurrentEditor": "Conteúdo atual do editor",
+ "localHistoryCurrentDisk": "Conteúdo atual no disco",
+ "localHistoryMissingFile": "O ficheiro original está ausente.",
+ "localHistoryRestoreRevision": "Restaurar Versão",
+ "localHistoryRestoreOriginalLocation": "Restaurar para Localização Original",
+ "localHistoryRestoreNewLocation": "Restaurar para Nova Localização…",
+ "localHistoryRestoreChange": "Restaurar Alteração",
+ "localHistoryRestoreUnavailable": "Esta alteração não pode ser restaurada porque a comparação não está mais atualizada.",
+ "localHistoryComparisonSimplified": "Esta alteração grande foi simplificada. Restaurar alterações individuais não está disponível.",
+ "localHistoryOlderChange": "Alteração de versão anterior: {text}",
+ "localHistoryCurrentChange": "Alteração de conteúdo atual: {text}",
+ "localHistoryDeleted": "Excluído",
+ "localHistoryUntitled": "Sem título",
+ "localHistoryClearDocument": "Limpar o Histórico deste Documento",
+ "localHistoryClearAll": "Limpar Todo o Histórico Local",
+ "localHistoryClearDocumentTitle": "Limpar o Histórico do Documento?",
+ "localHistoryClearAllTitle": "Limpar Todo o Histórico Local?",
+ "localHistoryClearConfirmation": "Isto elimina permanentemente as versões armazenadas. Esta ação não pode ser desfeita.",
+ "localHistoryWarningIndexRebuilt": "O índice do Histórico Local foi danificado e reconstruído a partir de versões intactas.",
+ "localHistoryWarningUnsupportedFormat": "Esta loja de Histórico Local utiliza um formato não suportado {version}.",
+ "localHistoryWarningUnavailable": "O Histórico Local não está disponível: {detail}",
+ "localHistoryWarningRecordingDisabled": "A gravação está desativada ou este caminho está excluído. Reative a gravação antes de restaurar.",
+ "localHistoryWarningPathChange": "O Histórico Local não conseguiu gravar a alteração do caminho: {detail}",
+ "localHistoryWarningDeletedPath": "O Histórico Local não conseguiu reter o caminho eliminado: {detail}",
+ "localHistoryWarningRevisionMissing": "A revisão selecionada está ausente ou danificada.",
+ "localHistoryWarningRevisionRead": "A revisão selecionada não pode ser lida: {detail}",
+ "localHistoryWarningCapture": "O Histórico Local não conseguiu capturar esta revisão: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Linha Base",
+ "localHistoryReasonSaved": "Guardado",
+ "localHistoryReasonAutomaticCheckpoint": "Checkpoint automático",
+ "localHistoryReasonBeforeReload": "Antes de recarregar",
+ "localHistoryReasonBeforeDiscard": "Antes de descartar",
+ "localHistoryReasonBeforeRestore": "Antes de restaurar",
+ "localHistoryReasonBeforeDelete": "Antes de apagar",
+ "localHistoryReasonExternalChange": "Alteração externa",
+ "settingsHistory": "Histórico",
+ "settingsClipboardHistoryTitle": "Recolher Histórico da Área de Transferência",
+ "settingsClipboardHistoryDescription": "Manter o conteúdo copiado e colado com sucesso apenas para esta sessão BusyMark.",
+ "settingsLocalHistoryTitle": "Gravar Histórico Local",
+ "settingsLocalHistoryDescription": "Armazenar as versões do documento neste dispositivo para recuperação e comparação.",
+ "settingsHistoryCheckpoint": "Intervalo de ponto de controlo automático",
+ "settingsHistoryRetention": "Período de retenção",
+ "settingsHistoryStorage": "Armazenamento máximo",
+ "settingsHistoryExcludedPaths": "Caminhos excluídos",
+ "settingsHistoryExcludedPathsHint": "Um caminho absoluto por linha",
+ "settingsSecondsValue": "{value} segundos",
+ "settingsDaysValue": "{value} dias",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_pt_BR.arb b/lib/l10n/app_pt_BR.arb
index c36c96da..7edb8da6 100644
--- a/lib/l10n/app_pt_BR.arb
+++ b/lib/l10n/app_pt_BR.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Não há suporte para correspondências regex de comprimento zero (por exemplo, ^, $ ou antecipações que correspondem apenas a uma posição).",
+ "workspaceSearchIncomplete": "Apenas alguns resultados são exibidos. Mostre mais ou restrinja a pesquisa.",
+ "workspaceSearchShowMore": "Mostrar mais resultados",
+ "workspaceSearchSkippedFiles": "A pesquisa está incompleta. Estes arquivos excedem 1 MiB ou não puderam ser lidos:",
"@@locale": "pt_BR",
"reportIssue": "Comunicar um problema",
"feedbackCategory": "Categoria",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Inserir linha em branco",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Estilo de texto",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "TOC",
+ "toc": "Tópicos",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Ações do sumário",
"@tocActions": {
@@ -3144,5 +3152,82 @@
"exportInvalidGeometry": "Deixe pelo menos 20 mm de largura e altura de conteúdo após as margens.",
"exportInvalidColor": "Insira uma cor como #RRGGBB.",
"exportInvalidCss": "Escolha um arquivo .css UTF-8 legível até 256 KiB, sem importações, URL de recursos, HTML ou CSS executável ou com escapes.",
- "exportInvalidRange": "{field}: insira um número entre {minimum} e {maximum}."
+ "exportInvalidRange": "{field}: insira um número entre {minimum} e {maximum}.",
+ "clipboardHistory": "Histórico da Área de Transferência",
+ "clipboardHistorySearchHint": "Pesquisar no histórico da área de transferência",
+ "clipboardHistoryDescription": "Abrir o histórico da área de transferência local da sessão.",
+ "clipboardCurrent": "Área de transferência atual",
+ "clipboardCurrentExternal": "Área de transferência de outro aplicativo",
+ "clipboardDestination": "Colar em {name}",
+ "clipboardSessionOnly": "Armazenado apenas para esta sessão BusyMark.",
+ "clipboardNoItems": "O histórico da área de transferência está vazio.",
+ "clipboardNoMatches": "Nenhum item da área de transferência corresponde a esta pesquisa.",
+ "clipboardDisabled": "A coleção de Histórico da Área de Transferência está desativada nas Configurações.",
+ "clipboardUnavailable": "O conteúdo da área de transferência atual não está disponível ou não é suportado.",
+ "clipboardTooLarge": "Este item da área de transferência é muito grande para ser armazenado.",
+ "clipboardPastePlainText": "Colar como Texto Simples",
+ "clipboardClearAll": "Limpar o Histórico da Área de Transferência",
+ "clipboardEntryText": "Texto",
+ "clipboardEntryRichText": "Texto formatado",
+ "clipboardEntryImage": "Imagem",
+ "clipboardOrigin": "Copiado de {name}",
+ "localHistory": "Histórico Local",
+ "localHistoryEllipsis": "Histórico Local…",
+ "localHistoryDescription": "Mostrar revisões locais salvas e automáticas para o documento atual.",
+ "findLocalHistoryEllipsis": "Encontrar no Histórico Local…",
+ "findLocalHistoryDescription": "Pesquisar documentos e conteúdo de revisões no Histórico Local.",
+ "localHistoryNoDocuments": "Nenhum documento possui Histórico Local.",
+ "localHistoryNoRevisions": "Este documento não possui revisões no Histórico Local.",
+ "localHistoryNoMatches": "Nenhuma revisão no Histórico Local corresponde a esta pesquisa.",
+ "localHistorySearchHint": "Pesquisar no histórico local",
+ "localHistorySelectedRevision": "Revisão selecionada",
+ "localHistoryCurrentEditor": "Conteúdo atual do editor",
+ "localHistoryCurrentDisk": "Conteúdo atual no disco",
+ "localHistoryMissingFile": "O arquivo original está faltando.",
+ "localHistoryRestoreRevision": "Restaurar Versão",
+ "localHistoryRestoreOriginalLocation": "Restaurar para a Localização Original",
+ "localHistoryRestoreNewLocation": "Restaurar para a Nova Localização…",
+ "localHistoryRestoreChange": "Restaurar Alteração",
+ "localHistoryRestoreUnavailable": "Esta alteração não pode ser restaurada porque a comparação não está mais atualizada.",
+ "localHistoryComparisonSimplified": "Esta comparação grande foi simplificada. Restaurar alterações individuais não está disponível.",
+ "localHistoryOlderChange": "Alteração da versão anterior: {text}",
+ "localHistoryCurrentChange": "Alteração do conteúdo atual: {text}",
+ "localHistoryDeleted": "Excluído",
+ "localHistoryUntitled": "Sem Título",
+ "localHistoryClearDocument": "Limpar o Histórico deste Documento",
+ "localHistoryClearAll": "Limpar todo o Histórico Local",
+ "localHistoryClearDocumentTitle": "Limpar o histórico do documento?",
+ "localHistoryClearAllTitle": "Limpar todo o Histórico Local?",
+ "localHistoryClearConfirmation": "Isso remove permanentemente as revisões armazenadas. Esta ação não pode ser desfeita.",
+ "localHistoryWarningIndexRebuilt": "O índice do Histórico Local foi danificado e reconstruído a partir de revisões íntegras.",
+ "localHistoryWarningUnsupportedFormat": "Esta loja de Histórico Local usa o formato não suportado {version}.",
+ "localHistoryWarningUnavailable": "Histórico Local não está disponível: {detail}",
+ "localHistoryWarningRecordingDisabled": "A gravação está desativada ou este caminho está excluído. Reative a gravação antes de restaurar.",
+ "localHistoryWarningPathChange": "O Histórico Local não conseguiu gravar a alteração do caminho: {detail}",
+ "localHistoryWarningDeletedPath": "O Histórico Local não conseguiu reter o caminho excluído: {detail}",
+ "localHistoryWarningRevisionMissing": "A revisão selecionada está ausente ou corrompida.",
+ "localHistoryWarningRevisionRead": "A revisão selecionada não pode ser lida: {detail}",
+ "localHistoryWarningCapture": "O Histórico Local não conseguiu capturar esta revisão: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Linha de Base",
+ "localHistoryReasonSaved": "Salvo",
+ "localHistoryReasonAutomaticCheckpoint": "Checkpoint automático",
+ "localHistoryReasonBeforeReload": "Antes de recarregar",
+ "localHistoryReasonBeforeDiscard": "Antes de descartar",
+ "localHistoryReasonBeforeRestore": "Antes da restauração",
+ "localHistoryReasonBeforeDelete": "Antes da exclusão",
+ "localHistoryReasonExternalChange": "Mudança externa",
+ "settingsHistory": "Histórico",
+ "settingsClipboardHistoryTitle": "Coletar Histórico da Área de Transferência",
+ "settingsClipboardHistoryDescription": "Manter o conteúdo copiado e colado com sucesso apenas para esta sessão BusyMark.",
+ "settingsLocalHistoryTitle": "Gravar Histórico Local",
+ "settingsLocalHistoryDescription": "Armazenar revisões do documento neste dispositivo para recuperação e comparação.",
+ "settingsHistoryCheckpoint": "Intervalo de checkpoint automático",
+ "settingsHistoryRetention": "Tempo de retenção",
+ "settingsHistoryStorage": "Armazenamento máximo",
+ "settingsHistoryExcludedPaths": "Caminhos excluídos",
+ "settingsHistoryExcludedPathsHint": "Um caminho absoluto por linha",
+ "settingsSecondsValue": "{value} segundos",
+ "settingsDaysValue": "{value} dias",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index c175e7db..77a33d5b 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Совпадения регулярных выражений нулевой длины не поддерживаются (например, ^, $ или проверки вперёд, совпадающие только с позицией).",
+ "workspaceSearchIncomplete": "Показана только часть результатов. Покажите больше или уточните поиск.",
+ "workspaceSearchShowMore": "Показать больше результатов",
+ "workspaceSearchSkippedFiles": "Поиск неполный. Эти файлы превышают 1 MiB или не могут быть прочитаны:",
"@@locale": "ru",
"reportIssue": "Сообщить о проблеме",
"feedbackCategory": "Категория",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Вставить пустую строку",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Стиль текста",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Оглавление",
+ "toc": "Темы",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Действия с оглавлением",
"@tocActions": {
@@ -3170,5 +3178,82 @@
"exportInvalidGeometry": "После вычета полей оставьте не менее 20 мм ширины и высоты содержимого.",
"exportInvalidColor": "Введите цвет в формате #RRGGBB.",
"exportInvalidCss": "Выберите читаемый файл .css в UTF-8 до 256 КиБ без импортов, URL ресурсов, HTML, исполняемого CSS и escape-последовательностей.",
- "exportInvalidRange": "{field}: введите число от {minimum} до {maximum}."
+ "exportInvalidRange": "{field}: введите число от {minimum} до {maximum}.",
+ "clipboardHistory": "История буфера обмена",
+ "clipboardHistorySearchHint": "Поиск в истории буфера обмена",
+ "clipboardHistoryDescription": "Открыть историю буфера обмена, специфичную для сеанса.",
+ "clipboardCurrent": "Текущий буфер обмена",
+ "clipboardCurrentExternal": "Текущий буфер обмена из другого приложения",
+ "clipboardDestination": "Вставить в {name}",
+ "clipboardSessionOnly": "Сохраняется только для текущего сеанса BusyMark.",
+ "clipboardNoItems": "История буфера обмена пуста.",
+ "clipboardNoMatches": "Нет элементов буфера обмена, соответствующих этому поиску.",
+ "clipboardDisabled": "Коллекция \"История буфера обмена\" отключена в настройках.",
+ "clipboardUnavailable": "Текущее содержимое буфера обмена недоступно или не поддерживается.",
+ "clipboardTooLarge": "Этот элемент буфера обмена слишком велик для хранения.",
+ "clipboardPastePlainText": "Вставить как обычный текст",
+ "clipboardClearAll": "Очистить историю буфера обмена",
+ "clipboardEntryText": "Текст",
+ "clipboardEntryRichText": "Форматированный текст",
+ "clipboardEntryImage": "Изображение",
+ "clipboardOrigin": "Скопировано из {name}",
+ "localHistory": "Локальная история",
+ "localHistoryEllipsis": "Локальная история…",
+ "localHistoryDescription": "Отобразить сохраненные и автоматические локальные версии для текущего документа.",
+ "findLocalHistoryEllipsis": "Найти в локальной истории…",
+ "findLocalHistoryDescription": "Поиск документов и содержимого версий в локальной истории.",
+ "localHistoryNoDocuments": "Ни один документ не имеет локальной истории.",
+ "localHistoryNoRevisions": "Этот документ не имеет версий в локальной истории.",
+ "localHistoryNoMatches": "Ни одна версия не соответствует этому поиску.",
+ "localHistorySearchHint": "Поиск в локальной истории",
+ "localHistorySelectedRevision": "Выбранная версия",
+ "localHistoryCurrentEditor": "Текущее содержимое редактора",
+ "localHistoryCurrentDisk": "Текущее содержимое на диске",
+ "localHistoryMissingFile": "Оригинальный файл отсутствует.",
+ "localHistoryRestoreRevision": "Восстановить версию",
+ "localHistoryRestoreOriginalLocation": "Восстановить в исходное местоположение",
+ "localHistoryRestoreNewLocation": "Восстановить в новое местоположение…",
+ "localHistoryRestoreChange": "Восстановить изменения",
+ "localHistoryRestoreUnavailable": "Эти изменения нельзя восстановить, так как сравнение больше не актуально.",
+ "localHistoryComparisonSimplified": "Это большое сравнение упрощено. Восстановление отдельных изменений недоступно.",
+ "localHistoryOlderChange": "Изменения в более старой версии: {text}",
+ "localHistoryCurrentChange": "Изменения в текущей версии: {text}",
+ "localHistoryDeleted": "Удалено",
+ "localHistoryUntitled": "Безымянный",
+ "localHistoryClearDocument": "Очистить историю этого документа",
+ "localHistoryClearAll": "Очистить всю локальную историю",
+ "localHistoryClearDocumentTitle": "Очистить историю документа?",
+ "localHistoryClearAllTitle": "Очистить всю локальную историю?",
+ "localHistoryClearConfirmation": "Это навсегда удаляет сохраненные версии. Это действие нельзя отменить.",
+ "localHistoryWarningIndexRebuilt": "Индекс локальной истории был поврежден и восстановлен из неповрежденных версий.",
+ "localHistoryWarningUnsupportedFormat": "Эта локальная история использует неподдерживаемый формат {version}.",
+ "localHistoryWarningUnavailable": "Локальная история недоступна: {detail}",
+ "localHistoryWarningRecordingDisabled": "Запись отключена или этот путь исключен. Включите запись перед восстановлением.",
+ "localHistoryWarningPathChange": "Локальная история не смогла записать изменение пути: {detail}",
+ "localHistoryWarningDeletedPath": "Локальная история не смогла сохранить удалённый путь: {detail}",
+ "localHistoryWarningRevisionMissing": "Выбранная версия отсутствует или повреждена.",
+ "localHistoryWarningRevisionRead": "Выбранную версию невозможно прочитать: {detail}",
+ "localHistoryWarningCapture": "Локальная история не смогла захватить эту версию: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Базовая версия",
+ "localHistoryReasonSaved": "Сохранено",
+ "localHistoryReasonAutomaticCheckpoint": "Автоматическая контрольная точка",
+ "localHistoryReasonBeforeReload": "Перед перезагрузкой",
+ "localHistoryReasonBeforeDiscard": "Перед удалением",
+ "localHistoryReasonBeforeRestore": "Перед восстановлением",
+ "localHistoryReasonBeforeDelete": "Перед удалением",
+ "localHistoryReasonExternalChange": "Внешнее изменение",
+ "settingsHistory": "История",
+ "settingsClipboardHistoryTitle": "Сохранить историю буфера обмена",
+ "settingsClipboardHistoryDescription": "Сохранять только скопированный и успешно вставленный контент для текущей сессии BusyMark.",
+ "settingsLocalHistoryTitle": "Записывать локальную историю",
+ "settingsLocalHistoryDescription": "Сохранять версии документа на этом устройстве для восстановления и сравнения.",
+ "settingsHistoryCheckpoint": "Интервал автоматического сохранения",
+ "settingsHistoryRetention": "Срок хранения",
+ "settingsHistoryStorage": "Максимальное хранилище",
+ "settingsHistoryExcludedPaths": "Исключенные пути",
+ "settingsHistoryExcludedPathsHint": "Один абсолютный путь в строке",
+ "settingsSecondsValue": "{value} секунды",
+ "settingsDaysValue": "{value} дни",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb
index 069d6c18..36e0713a 100644
--- a/lib/l10n/app_tr.arb
+++ b/lib/l10n/app_tr.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Sıfır uzunluklu düzenli ifade eşleşmeleri desteklenmez (örneğin ^, $ veya yalnızca bir konumla eşleşen ileri bakışlar).",
+ "workspaceSearchIncomplete": "Sonuçların yalnızca bir kısmı gösteriliyor. Daha fazlasını gösterin veya aramayı daraltın.",
+ "workspaceSearchShowMore": "Daha fazla sonuç göster",
+ "workspaceSearchSkippedFiles": "Arama eksik. Bu dosyalar 1 MiB boyutunu aşıyor veya okunamadı:",
"@@locale": "tr",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Boş satır ekle",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Metin stili",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "İçindekiler",
+ "toc": "Konular",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "İçindekiler eylemleri",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "Örnekler",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "Yeni örnek",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "Kenar boşluklarından sonra en az 20 mm içerik genişliği ve yüksekliği bırakın.",
"exportInvalidColor": "#RRGGBB biçiminde bir renk girin.",
"exportInvalidCss": "İçe aktarım, kaynak URL’si, HTML, çalıştırılabilir veya kaçışlı CSS içermeyen, okunabilir UTF-8 .css dosyası seçin (en fazla 256 KiB).",
- "exportInvalidRange": "{field}: {minimum} ile {maximum} arasında bir sayı girin."
+ "exportInvalidRange": "{field}: {minimum} ile {maximum} arasında bir sayı girin.",
+ "clipboardHistory": "Kopyalama Geçmişi",
+ "clipboardHistorySearchHint": "Pano geçmişinde ara",
+ "clipboardHistoryDescription": "Oturuma özgü kopyalama geçmişini aç.",
+ "clipboardCurrent": "Mevcut kopyalama",
+ "clipboardCurrentExternal": "Başka bir uygulamadan mevcut kopyalama",
+ "clipboardDestination": "{name}'e yapıştır",
+ "clipboardSessionOnly": "Sadece bu BusyMark oturumu için saklanır.",
+ "clipboardNoItems": "Kopyalama geçmişi boş.",
+ "clipboardNoMatches": "Bu arama ile eşleşen kopyalama öğesi bulunamadı.",
+ "clipboardDisabled": "Kopyalama Geçmişi koleksiyonu Ayarlar'da devre dışıdır.",
+ "clipboardUnavailable": "Mevcut kopyalama içeriği mevcut değil veya desteklenmiyor.",
+ "clipboardTooLarge": "Bu kopyalanmış öğe, saklamak için çok büyüktür.",
+ "clipboardPastePlainText": "Temiz Metin Olarak Yapıştır",
+ "clipboardClearAll": "Kopyalama Geçmişini Temizle",
+ "clipboardEntryText": "Metin",
+ "clipboardEntryRichText": "Zengin Metin",
+ "clipboardEntryImage": "Resim",
+ "clipboardOrigin": "{name}'den Kopyalandı",
+ "localHistory": "Yerel Geçmiş",
+ "localHistoryEllipsis": "Yerel Geçmiş…",
+ "localHistoryDescription": "Mevcut belge için kaydedilmiş ve otomatik yerel sürümleri göster.",
+ "findLocalHistoryEllipsis": "Yerel Geçmişte Bul...",
+ "findLocalHistoryDescription": "Yerel Geçmişte belgeleri ve revizyon içeriğini arayın.",
+ "localHistoryNoDocuments": "Henüz herhangi bir belge Yerel Geçmişe sahip değil.",
+ "localHistoryNoRevisions": "Bu belge Yerel Geçmiş revizyonlarına sahip değil.",
+ "localHistoryNoMatches": "Bu arama ile eşleşen Yerel Geçmiş revizyonu yok.",
+ "localHistorySearchHint": "Yerel geçmişte ara",
+ "localHistorySelectedRevision": "Seçilen revizyon",
+ "localHistoryCurrentEditor": "Mevcut düzenleme içeriği",
+ "localHistoryCurrentDisk": "Disk üzerinde mevcut içerik",
+ "localHistoryMissingFile": "Orijinal dosya bulunmuyor.",
+ "localHistoryRestoreRevision": "Versiyonu Geri Yükle",
+ "localHistoryRestoreOriginalLocation": "Orijinal Konuma Geri Yükle",
+ "localHistoryRestoreNewLocation": "Yeni Konuma Geri Yükle…",
+ "localHistoryRestoreChange": "Değişikliği Geri Yükle",
+ "localHistoryRestoreUnavailable": "Bu değişiklik geri alınamaz çünkü karşılaştırma artık geçerli değil.",
+ "localHistoryComparisonSimplified": "Bu büyük karşılaştırma basitleştirildi. Tek tek değişiklikleri geri yüklemek mümkün değil.",
+ "localHistoryOlderChange": "Eski versiyon değişikliği: {text}",
+ "localHistoryCurrentChange": "Mevcut içerik değişikliği: {text}",
+ "localHistoryDeleted": "Silindi",
+ "localHistoryUntitled": "Başlımsız",
+ "localHistoryClearDocument": "Bu Dokümanın Geçmişini Temizle",
+ "localHistoryClearAll": "Tüm Yerel Geçmişi Temizle",
+ "localHistoryClearDocumentTitle": "Doküman Geçmişini Temizlemek İstemiyor Musunuz?",
+ "localHistoryClearAllTitle": "Tüm Yerel Geçmişi Temizlemek İstemiyor Musunuz?",
+ "localHistoryClearConfirmation": "Bu, kaydedilen tüm sürümleri kalıcı olarak kaldırır. Bu işlem geri alınamaz.",
+ "localHistoryWarningIndexRebuilt": "Yerel Geçmiş indeksi, sağlam sürümlerden yeniden oluşturuldu.",
+ "localHistoryWarningUnsupportedFormat": "Bu Yerel Geçmiş depolama, desteklenmeyen format {version} kullanır.",
+ "localHistoryWarningUnavailable": "Yerel Geçmiş kullanılabilir değil: {detail}",
+ "localHistoryWarningRecordingDisabled": "Kayıt devre dışı bırakılmış veya bu yol hariç tutulmuş. Geri yükleme yapmadan önce tekrar etkinleştirin.",
+ "localHistoryWarningPathChange": "Yerel Geçmiş, yol değişikliğini kaydetemedi: {detail}",
+ "localHistoryWarningDeletedPath": "Yerel Geçmiş, silinen yolu kaydetemedi: {detail}",
+ "localHistoryWarningRevisionMissing": "Seçilen sürüm eksik veya bozulmuş.",
+ "localHistoryWarningRevisionRead": "Seçilen sürüm okunamaz: {detail}",
+ "localHistoryWarningCapture": "Yerel Geçmiş, bu sürümü yakalamadı: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Temel Sürüm",
+ "localHistoryReasonSaved": "Kaydedildi",
+ "localHistoryReasonAutomaticCheckpoint": "Otomatik kontrol noktası",
+ "localHistoryReasonBeforeReload": "Yenilemeden önce",
+ "localHistoryReasonBeforeDiscard": "Atılmadan önce",
+ "localHistoryReasonBeforeRestore": "Restorasyon öncesi",
+ "localHistoryReasonBeforeDelete": "Silme öncesi",
+ "localHistoryReasonExternalChange": "Dış kaynak değişiklik",
+ "settingsHistory": "Geçmiş",
+ "settingsClipboardHistoryTitle": "Geçmişi Kopyala",
+ "settingsClipboardHistoryDescription": "Bu BusyMark oturumu için kopyalanan ve başarıyla yapıştırılan içeriği sakla.",
+ "settingsLocalHistoryTitle": "Yerel Geçmişi Kaydet",
+ "settingsLocalHistoryDescription": "Belgeleri kurtarma ve karşılaştırma için bu cihazda değişiklikleri sakla.",
+ "settingsHistoryCheckpoint": "Otomatik kontrol noktası aralığı",
+ "settingsHistoryRetention": "Saklama süresi",
+ "settingsHistoryStorage": "Maksimum depolama alanı",
+ "settingsHistoryExcludedPaths": "Hariç tutulan yollar",
+ "settingsHistoryExcludedPathsHint": "Bir mutlak yol her satırda",
+ "settingsSecondsValue": "{value} saniye",
+ "settingsDaysValue": "{value} gün",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb
index 52313fec..3e627dec 100644
--- a/lib/l10n/app_uk.arb
+++ b/lib/l10n/app_uk.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Збіги регулярних виразів нульової довжини не підтримуються (наприклад, ^, $ або перевірки вперед, що збігаються лише з позицією).",
+ "workspaceSearchIncomplete": "Показано лише частину результатів. Покажіть більше або звузьте пошук.",
+ "workspaceSearchShowMore": "Показати більше результатів",
+ "workspaceSearchSkippedFiles": "Пошук неповний. Ці файли перевищують 1 MiB або їх не вдалося прочитати:",
"@@locale": "uk",
"reportIssue": "Повідомити про проблему",
"feedbackCategory": "Категорія",
@@ -977,6 +981,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Вставити порожній рядок",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Стиль тексту",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1202,9 +1210,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Зміст",
+ "toc": "Теми",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Дії зі змістом",
"@tocActions": {
@@ -3170,5 +3178,82 @@
"exportInvalidGeometry": "Після віднімання полів залиште щонайменше 20 мм ширини й висоти вмісту.",
"exportInvalidColor": "Введіть колір у форматі #RRGGBB.",
"exportInvalidCss": "Виберіть читабельний файл .css у UTF-8 до 256 КіБ без імпортів, URL ресурсів, HTML, виконуваного CSS і escape-послідовностей.",
- "exportInvalidRange": "{field}: введіть число від {minimum} до {maximum}."
+ "exportInvalidRange": "{field}: введіть число від {minimum} до {maximum}.",
+ "clipboardHistory": "Історія Вирізання",
+ "clipboardHistorySearchHint": "Пошук в історії буфера обміну",
+ "clipboardHistoryDescription": "Відкрити історію вирізання, прив'язану до сесії.",
+ "clipboardCurrent": "Поточне вирізання",
+ "clipboardCurrentExternal": "Поточне вирізання з іншого додатку",
+ "clipboardDestination": "Вставити в {name}",
+ "clipboardSessionOnly": "Зберігається лише для цієї сесії BusyMark.",
+ "clipboardNoItems": "Історія вирізання порожня.",
+ "clipboardNoMatches": "Жоден елемент вирізання не відповідає цьому пошуку.",
+ "clipboardDisabled": "Збірка \"Історія Вирізання\" вимкнена в Налаштуваннях.",
+ "clipboardUnavailable": "Поточний вміст вирізання недоступний або не підтримується.",
+ "clipboardTooLarge": "Цей елемент буфера обміну занадто великий для зберігання.",
+ "clipboardPastePlainText": "Вставити як звичайний текст",
+ "clipboardClearAll": "Очистити історію буфера обміну",
+ "clipboardEntryText": "Текст",
+ "clipboardEntryRichText": "Збагачений текст",
+ "clipboardEntryImage": "Зображення",
+ "clipboardOrigin": "Скопійовано з {name}",
+ "localHistory": "Локальна історія",
+ "localHistoryEllipsis": "Локальна історія…",
+ "localHistoryDescription": "Показати збережені та автоматичні локальні версії для поточної документа.",
+ "findLocalHistoryEllipsis": "Знайти в локальній історії…",
+ "findLocalHistoryDescription": "Пошук документів та вмісту версій у локальній історії.",
+ "localHistoryNoDocuments": "Жоден документ поки не має локальної історії.",
+ "localHistoryNoRevisions": "Цей документ не має версій локальної історії.",
+ "localHistoryNoMatches": "Жодна версія локальної історії не відповідає цьому пошуку.",
+ "localHistorySearchHint": "Пошук у локальній історії",
+ "localHistorySelectedRevision": "Обрана версія",
+ "localHistoryCurrentEditor": "Поточний вміст редактора",
+ "localHistoryCurrentDisk": "Поточний вміст на диску",
+ "localHistoryMissingFile": "Оригінальний файл відсутній.",
+ "localHistoryRestoreRevision": "Відновити версію",
+ "localHistoryRestoreOriginalLocation": "Відновити до початкової локації",
+ "localHistoryRestoreNewLocation": "Відновити до нової локації…",
+ "localHistoryRestoreChange": "Відновити зміни",
+ "localHistoryRestoreUnavailable": "Ці зміни не можуть бути відновлені, оскільки порівняння більше не актуальне.",
+ "localHistoryComparisonSimplified": "Це велике порівняння спрощено. Відновлення окремих змін недоступне.",
+ "localHistoryOlderChange": "Зміна в попередній версії: {text}",
+ "localHistoryCurrentChange": "Зміна в поточному вмісті: {text}",
+ "localHistoryDeleted": "Видалено",
+ "localHistoryUntitled": "Без назви",
+ "localHistoryClearDocument": "Очистити історію цього документа",
+ "localHistoryClearAll": "Очистити всю локальну історію",
+ "localHistoryClearDocumentTitle": "Очистити історію документа?",
+ "localHistoryClearAllTitle": "Очистити всю локальну історію?",
+ "localHistoryClearConfirmation": "Це постійно видаляє збережені версії. Цю дію неможливо скасувати.",
+ "localHistoryWarningIndexRebuilt": "Індекс локальної історії пошкоджено та відновлено з цілих версій.",
+ "localHistoryWarningUnsupportedFormat": "Ця локальна історія використовує непідтримуваний формат {version}.",
+ "localHistoryWarningUnavailable": "Локальна історія недоступна: {detail}",
+ "localHistoryWarningRecordingDisabled": "Запис вимкнено або цей шлях виключено. Увімкніть запис перед відновленням.",
+ "localHistoryWarningPathChange": "Локальна історія не змогла записати зміну шляху: {detail}",
+ "localHistoryWarningDeletedPath": "Локальна історія не змогла зберегти видалений шлях: {detail}",
+ "localHistoryWarningRevisionMissing": "Вибрана версія відсутня або пошкоджена.",
+ "localHistoryWarningRevisionRead": "Вибрану версію неможливо прочитати: {detail}",
+ "localHistoryWarningCapture": "Локальна історія не змогла зберегти цю версію: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Базова версія",
+ "localHistoryReasonSaved": "Збережено",
+ "localHistoryReasonAutomaticCheckpoint": "Автоматичний контрольний пункт",
+ "localHistoryReasonBeforeReload": "Перед перезавантаженням",
+ "localHistoryReasonBeforeDiscard": "Перед видаленням",
+ "localHistoryReasonBeforeRestore": "Перед відновленням",
+ "localHistoryReasonBeforeDelete": "Перед видаленням",
+ "localHistoryReasonExternalChange": "Зовнішня зміна",
+ "settingsHistory": "Історія",
+ "settingsClipboardHistoryTitle": "Зберегти історію, скопійовану в буфер обміну",
+ "settingsClipboardHistoryDescription": "Зберігати лише скопійований та успішно вставлений вміст для цієї сесії BusyMark.",
+ "settingsLocalHistoryTitle": "Записувати локальну історію",
+ "settingsLocalHistoryDescription": "Зберігати версії документа на цьому пристрої для відновлення та порівняння.",
+ "settingsHistoryCheckpoint": "Автоматичний інтервал збереження контрольної точки",
+ "settingsHistoryRetention": "Тривалість зберігання",
+ "settingsHistoryStorage": "Максимальне місце для зберігання",
+ "settingsHistoryExcludedPaths": "Виключені шляхи",
+ "settingsHistoryExcludedPathsHint": "Один абсолютний шлях на рядок",
+ "settingsSecondsValue": "{value} секунди",
+ "settingsDaysValue": "{value} дні",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_vi.arb b/lib/l10n/app_vi.arb
index 275741df..47ccd5d4 100644
--- a/lib/l10n/app_vi.arb
+++ b/lib/l10n/app_vi.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "Không hỗ trợ kết quả khớp regex có độ dài bằng 0 (ví dụ ^, $ hoặc lookahead chỉ khớp với một vị trí).",
+ "workspaceSearchIncomplete": "Chỉ hiển thị một phần kết quả. Hiển thị thêm hoặc thu hẹp tìm kiếm.",
+ "workspaceSearchShowMore": "Hiển thị thêm kết quả",
+ "workspaceSearchSkippedFiles": "Tìm kiếm chưa đầy đủ. Các tệp này vượt quá 1 MiB hoặc không thể đọc được:",
"@@locale": "vi",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "Chèn dòng trống",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "Kiểu văn bản",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "Mục lục",
+ "toc": "Chủ đề",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "Thao tác với mục lục",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "Các thực thể",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "Thực thể mới",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "Sau khi trừ lề, hãy để nội dung rộng và cao ít nhất 20 mm.",
"exportInvalidColor": "Nhập màu theo dạng #RRGGBB.",
"exportInvalidCss": "Chọn tệp .css UTF-8 đọc được tối đa 256 KiB, không có nhập tài nguyên, URL tài nguyên, HTML, CSS thực thi hoặc ký tự thoát.",
- "exportInvalidRange": "{field}: nhập số từ {minimum} đến {maximum}."
+ "exportInvalidRange": "{field}: nhập số từ {minimum} đến {maximum}.",
+ "clipboardHistory": "Lịch sử sao chép",
+ "clipboardHistorySearchHint": "Tìm kiếm lịch sử bảng nhớ tạm",
+ "clipboardHistoryDescription": "Mở lịch sử sao chép cục bộ phiên.",
+ "clipboardCurrent": "Lịch sử sao chép hiện tại",
+ "clipboardCurrentExternal": "Lịch sử sao chép từ một ứng dụng khác",
+ "clipboardDestination": "Dán vào {name}",
+ "clipboardSessionOnly": "Chỉ lưu trong phiên BusyMark này.",
+ "clipboardNoItems": "Lịch sử sao chép trống.",
+ "clipboardNoMatches": "Không có mục sao chép nào khớp với tìm kiếm này.",
+ "clipboardDisabled": "Bộ sưu tập Lịch sử sao chép bị vô hiệu hóa trong Cài đặt.",
+ "clipboardUnavailable": "Nội dung sao chép hiện tại không khả dụng hoặc không được hỗ trợ.",
+ "clipboardTooLarge": "Mục này trong clipboard quá lớn để lưu.",
+ "clipboardPastePlainText": "Dán dưới dạng văn bản thuần túy",
+ "clipboardClearAll": "Xóa lịch sử clipboard",
+ "clipboardEntryText": "Văn bản",
+ "clipboardEntryRichText": "Văn bản phong phú",
+ "clipboardEntryImage": "Hình ảnh",
+ "clipboardOrigin": "Sao chép từ {name}",
+ "localHistory": "Lịch sử cục bộ",
+ "localHistoryEllipsis": "Lịch sử cục bộ…",
+ "localHistoryDescription": "Hiển thị các phiên bản cục bộ đã lưu và tự động cho tài liệu hiện tại.",
+ "findLocalHistoryEllipsis": "Tìm trong Lịch sử cục bộ…",
+ "findLocalHistoryDescription": "Tìm kiếm các tài liệu và nội dung phiên bản trong Lịch sử cục bộ.",
+ "localHistoryNoDocuments": "Chưa có tài liệu nào có Lịch sử cục bộ.",
+ "localHistoryNoRevisions": "Tài liệu này không có các phiên bản Lịch sử cục bộ.",
+ "localHistoryNoMatches": "Không có phiên bản nào trong Lịch sử cục bộ khớp với tìm kiếm này.",
+ "localHistorySearchHint": "Tìm kiếm lịch sử cục bộ",
+ "localHistorySelectedRevision": "Phiên bản đã chọn",
+ "localHistoryCurrentEditor": "Nội dung trình soạn thảo hiện tại",
+ "localHistoryCurrentDisk": "Nội dung hiện tại trên ổ đĩa",
+ "localHistoryMissingFile": "Tệp gốc bị thiếu.",
+ "localHistoryRestoreRevision": "Khôi phục Phiên bản",
+ "localHistoryRestoreOriginalLocation": "Khôi phục về Vị trí Ban đầu",
+ "localHistoryRestoreNewLocation": "Khôi phục về Vị trí Mới…",
+ "localHistoryRestoreChange": "Khôi phục Thay đổi",
+ "localHistoryRestoreUnavailable": "Thay đổi này không thể khôi phục vì so sánh không còn hợp lệ.",
+ "localHistoryComparisonSimplified": "So sánh này rất lớn, khôi phục thay đổi riêng không khả thi.",
+ "localHistoryOlderChange": "Thay đổi phiên bản cũ: {text}",
+ "localHistoryCurrentChange": "Thay đổi nội dung hiện tại: {text}",
+ "localHistoryDeleted": "Xóa",
+ "localHistoryUntitled": "Không có tiêu đề",
+ "localHistoryClearDocument": "Xóa Lịch Sử Của Tài Liệu Này",
+ "localHistoryClearAll": "Xóa Lịch Sử Địa Phương",
+ "localHistoryClearDocumentTitle": "Xóa lịch sử tài liệu?",
+ "localHistoryClearAllTitle": "Xóa lịch sử địa phương?",
+ "localHistoryClearConfirmation": "Điều này sẽ xóa vĩnh viễn các phiên bản đã lưu. Hành động này không thể hoàn tác.",
+ "localHistoryWarningIndexRebuilt": "Chỉ mục Lịch Sử Địa Phương bị hỏng và được xây dựng lại từ các phiên bản còn nguyên vẹn.",
+ "localHistoryWarningUnsupportedFormat": "Kho Lịch Sử Địa Phương sử dụng định dạng không được hỗ trợ {version}.",
+ "localHistoryWarningUnavailable": "Lịch Sử Địa Phương không khả dụng: {detail}",
+ "localHistoryWarningRecordingDisabled": "Ghi âm bị tắt hoặc đường dẫn này bị loại trừ. Vui lòng bật lại ghi âm trước khi khôi phục.",
+ "localHistoryWarningPathChange": "Lịch Sử Địa Phương không thể ghi lại thay đổi đường dẫn: {detail}",
+ "localHistoryWarningDeletedPath": "Lịch sử cục bộ không thể lưu trữ đường dẫn đã xóa: {detail}",
+ "localHistoryWarningRevisionMissing": "Phiên bản đã chọn bị thiếu hoặc bị hỏng.",
+ "localHistoryWarningRevisionRead": "Phiên bản đã chọn không thể đọc: {detail}",
+ "localHistoryWarningCapture": "Lịch sử cục bộ không thể ghi lại phiên bản này: {detail}",
+ "localHistoryRevisionAt": "{reason} – {timestamp}",
+ "localHistoryReasonBaseline": "Mốc cơ sở",
+ "localHistoryReasonSaved": "Lưu",
+ "localHistoryReasonAutomaticCheckpoint": "Tự động lưu",
+ "localHistoryReasonBeforeReload": "Trước khi tải lại",
+ "localHistoryReasonBeforeDiscard": "Trước khi vứt",
+ "localHistoryReasonBeforeRestore": "Trước khi khôi phục",
+ "localHistoryReasonBeforeDelete": "Trước khi xóa",
+ "localHistoryReasonExternalChange": "Thay đổi từ bên ngoài",
+ "settingsHistory": "Lịch sử",
+ "settingsClipboardHistoryTitle": "Thu thập lịch sử sao chép",
+ "settingsClipboardHistoryDescription": "Giữ nội dung đã sao chép và dán thành công chỉ cho phiên BusyMark này.",
+ "settingsLocalHistoryTitle": "Ghi lại lịch sử cục bộ",
+ "settingsLocalHistoryDescription": "Lưu các phiên bản tài liệu trên thiết bị để khôi phục và so sánh.",
+ "settingsHistoryCheckpoint": "Khoảng thời gian kiểm tra tự động",
+ "settingsHistoryRetention": "Thời gian lưu",
+ "settingsHistoryStorage": "Dung lượng tối đa",
+ "settingsHistoryExcludedPaths": "Đường dẫn bị loại trừ",
+ "settingsHistoryExcludedPathsHint": "Một đường dẫn tuyệt đối trên mỗi dòng",
+ "settingsSecondsValue": "{value} giây",
+ "settingsDaysValue": "{value} ngày",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index 80ad14f8..5929bcf8 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "不支持零长度的正则表达式匹配(例如 ^、$ 或仅匹配位置的前瞻断言)。",
+ "workspaceSearchIncomplete": "仅显示了部分结果。请显示更多结果或缩小搜索范围。",
+ "workspaceSearchShowMore": "显示更多结果",
+ "workspaceSearchSkippedFiles": "搜索不完整。以下文件超过 1 MiB 或无法读取:",
"@@locale": "zh",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "插入空行",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "文本样式",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "目录",
+ "toc": "主题",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "目录操作",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "实例",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "新建实例",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "扣除边距后,请至少保留 20 mm 的内容宽度和高度。",
"exportInvalidColor": "请输入 #RRGGBB 格式的颜色。",
"exportInvalidCss": "请选择可读取的 UTF-8 .css 文件(最大 256 KiB),不得包含导入、资源 URL、HTML、可执行或转义 CSS。",
- "exportInvalidRange": "{field}:请输入 {minimum} 到 {maximum} 之间的数字。"
+ "exportInvalidRange": "{field}:请输入 {minimum} 到 {maximum} 之间的数字。",
+ "clipboardHistory": "剪贴板历史",
+ "clipboardHistorySearchHint": "搜索剪贴板历史记录",
+ "clipboardHistoryDescription": "打开会话本地剪贴板历史。",
+ "clipboardCurrent": "当前剪贴板",
+ "clipboardCurrentExternal": "来自其他应用程序的当前剪贴板",
+ "clipboardDestination": "粘贴到 {name}",
+ "clipboardSessionOnly": "仅存储在本次 BusyMark 会话中。",
+ "clipboardNoItems": "剪贴板历史为空。",
+ "clipboardNoMatches": "没有匹配此搜索的剪贴板项目。",
+ "clipboardDisabled": "剪贴板历史集合在设置中已禁用。",
+ "clipboardUnavailable": "当前剪贴板内容不可用或不受支持。",
+ "clipboardTooLarge": "此剪贴板内容过大,无法保留。",
+ "clipboardPastePlainText": "作为纯文本粘贴",
+ "clipboardClearAll": "清除剪贴板历史",
+ "clipboardEntryText": "文本",
+ "clipboardEntryRichText": "丰富文本",
+ "clipboardEntryImage": "图片",
+ "clipboardOrigin": "复制自 {name}",
+ "localHistory": "本地历史",
+ "localHistoryEllipsis": "本地历史…",
+ "localHistoryDescription": "显示当前文档的已保存和自动本地修订。",
+ "findLocalHistoryEllipsis": "在本地历史中查找…",
+ "findLocalHistoryDescription": "搜索本地历史文档和修订内容。",
+ "localHistoryNoDocuments": "尚无文档具有本地历史。",
+ "localHistoryNoRevisions": "此文档没有本地历史修订。",
+ "localHistoryNoMatches": "没有本地历史修订与此搜索匹配。",
+ "localHistorySearchHint": "搜索本地历史记录",
+ "localHistorySelectedRevision": "已选择的修订",
+ "localHistoryCurrentEditor": "当前编辑器内容",
+ "localHistoryCurrentDisk": "磁盘上的当前内容",
+ "localHistoryMissingFile": "原始文件丢失。",
+ "localHistoryRestoreRevision": "恢复版本",
+ "localHistoryRestoreOriginalLocation": "恢复到原始位置",
+ "localHistoryRestoreNewLocation": "恢复到新位置…",
+ "localHistoryRestoreChange": "恢复更改",
+ "localHistoryRestoreUnavailable": "无法恢复此更改,因为比较已过时。",
+ "localHistoryComparisonSimplified": "此大型比较已简化。 无法恢复单个更改。",
+ "localHistoryOlderChange": "较旧的版本更改: {text}",
+ "localHistoryCurrentChange": "当前内容更改: {text}",
+ "localHistoryDeleted": "已删除",
+ "localHistoryUntitled": "未命名",
+ "localHistoryClearDocument": "清除此文档的历史",
+ "localHistoryClearAll": "清除所有本地历史",
+ "localHistoryClearDocumentTitle": "清除文档历史?",
+ "localHistoryClearAllTitle": "清除所有本地历史?",
+ "localHistoryClearConfirmation": "这将永久删除存储的修订。 此操作无法撤销。",
+ "localHistoryWarningIndexRebuilt": "本地历史索引已损坏并从完整修订中重建。",
+ "localHistoryWarningUnsupportedFormat": "此本地历史存储使用不受支持的格式 {version}。",
+ "localHistoryWarningUnavailable": "本地历史不可用: {detail}",
+ "localHistoryWarningRecordingDisabled": "录音已禁用或此路径已排除。 在恢复之前重新启用录音。",
+ "localHistoryWarningPathChange": "无法记录本地历史路径更改: {detail}",
+ "localHistoryWarningDeletedPath": "本地历史记录无法保留已删除的路径: {detail}",
+ "localHistoryWarningRevisionMissing": "所选版本丢失或损坏。",
+ "localHistoryWarningRevisionRead": "无法读取所选版本: {detail}",
+ "localHistoryWarningCapture": "本地历史记录无法捕获此版本: {detail}",
+ "localHistoryRevisionAt": "{reason}・{timestamp}",
+ "localHistoryReasonBaseline": "基线",
+ "localHistoryReasonSaved": "已保存",
+ "localHistoryReasonAutomaticCheckpoint": "自动检查点",
+ "localHistoryReasonBeforeReload": "在重新加载之前",
+ "localHistoryReasonBeforeDiscard": "在丢弃之前",
+ "localHistoryReasonBeforeRestore": "恢复前",
+ "localHistoryReasonBeforeDelete": "删除前",
+ "localHistoryReasonExternalChange": "外部修改",
+ "settingsHistory": "历史记录",
+ "settingsClipboardHistoryTitle": "收集剪贴板历史",
+ "settingsClipboardHistoryDescription": "只保留本次 BusyMark 会话中已复制并成功粘贴的内容。",
+ "settingsLocalHistoryTitle": "记录本地历史",
+ "settingsLocalHistoryDescription": "将文档修订存储在此设备上,以便恢复和比较。",
+ "settingsHistoryCheckpoint": "自动检查点间隔",
+ "settingsHistoryRetention": "保留期限",
+ "settingsHistoryStorage": "最大存储",
+ "settingsHistoryExcludedPaths": "排除路径",
+ "settingsHistoryExcludedPathsHint": "每行一个绝对路径",
+ "settingsSecondsValue": "{value} 秒",
+ "settingsDaysValue": "{value} 天",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/app_zh_CN.arb b/lib/l10n/app_zh_CN.arb
index 54c4eba7..760dfa9b 100644
--- a/lib/l10n/app_zh_CN.arb
+++ b/lib/l10n/app_zh_CN.arb
@@ -1,4 +1,8 @@
{
+ "sourceSearchZeroLengthUnsupported": "不支持零长度的正则表达式匹配(例如 ^、$ 或仅匹配位置的前瞻断言)。",
+ "workspaceSearchIncomplete": "仅显示了部分结果。请显示更多结果或缩小搜索范围。",
+ "workspaceSearchShowMore": "显示更多结果",
+ "workspaceSearchSkippedFiles": "搜索不完整。以下文件超过 1 MiB 或无法读取:",
"@@locale": "zh_CN",
"appTitle": "BusyMark",
"@appTitle": {
@@ -1137,6 +1141,10 @@
"@hardLineBreak": {
"description": "Hard line break command label."
},
+ "insertBlankLine": "插入空行",
+ "@insertBlankLine": {
+ "description": "Insert blank line command label."
+ },
"textStyle": "文本样式",
"@textStyle": {
"description": "Text style menu tooltip."
@@ -1378,9 +1386,9 @@
"@files": {
"description": "Files sidebar tab label."
},
- "toc": "目录",
+ "toc": "主题",
"@toc": {
- "description": "Writerside table of contents sidebar tab label."
+ "description": "Writerside Topics sidebar tab label."
},
"tocActions": "目录操作",
"@tocActions": {
@@ -3829,7 +3837,7 @@
},
"instances": "实例",
"@instances": {
- "description": "Heading for the Writerside instances shown in the TOC sidebar."
+ "description": "Heading for the Writerside instances shown in the Topics sidebar."
},
"newInstance": "新建实例",
"@newInstance": {
@@ -4703,5 +4711,82 @@
"exportInvalidGeometry": "扣除边距后,请至少保留 20 mm 的内容宽度和高度。",
"exportInvalidColor": "请输入 #RRGGBB 格式的颜色。",
"exportInvalidCss": "请选择可读取的 UTF-8 .css 文件(最大 256 KiB),不得包含导入、资源 URL、HTML、可执行或转义 CSS。",
- "exportInvalidRange": "{field}:请输入 {minimum} 到 {maximum} 之间的数字。"
+ "exportInvalidRange": "{field}:请输入 {minimum} 到 {maximum} 之间的数字。",
+ "clipboardHistory": "剪贴板历史",
+ "clipboardHistorySearchHint": "搜索剪贴板历史记录",
+ "clipboardHistoryDescription": "打开会话本地剪贴板历史。",
+ "clipboardCurrent": "当前剪贴板",
+ "clipboardCurrentExternal": "来自其他应用程序的当前剪贴板",
+ "clipboardDestination": "粘贴到 {name}",
+ "clipboardSessionOnly": "仅存储在本次 BusyMark 会话中。",
+ "clipboardNoItems": "剪贴板历史为空。",
+ "clipboardNoMatches": "没有匹配此搜索的剪贴板项目。",
+ "clipboardDisabled": "剪贴板历史集合在设置中已禁用。",
+ "clipboardUnavailable": "当前剪贴板内容不可用或不受支持。",
+ "clipboardTooLarge": "此剪贴板内容过大,无法保留。",
+ "clipboardPastePlainText": "作为纯文本粘贴",
+ "clipboardClearAll": "清除剪贴板历史",
+ "clipboardEntryText": "文本",
+ "clipboardEntryRichText": "丰富文本",
+ "clipboardEntryImage": "图片",
+ "clipboardOrigin": "复制自 {name}",
+ "localHistory": "本地历史",
+ "localHistoryEllipsis": "本地历史…",
+ "localHistoryDescription": "显示当前文档的已保存和自动本地修订。",
+ "findLocalHistoryEllipsis": "在本地历史中查找…",
+ "findLocalHistoryDescription": "搜索本地历史文档和修订内容。",
+ "localHistoryNoDocuments": "尚无文档具有本地历史。",
+ "localHistoryNoRevisions": "此文档没有本地历史修订。",
+ "localHistoryNoMatches": "没有本地历史修订与此搜索匹配。",
+ "localHistorySearchHint": "搜索本地历史记录",
+ "localHistorySelectedRevision": "已选择的修订",
+ "localHistoryCurrentEditor": "当前编辑器内容",
+ "localHistoryCurrentDisk": "磁盘上的当前内容",
+ "localHistoryMissingFile": "原始文件丢失。",
+ "localHistoryRestoreRevision": "恢复版本",
+ "localHistoryRestoreOriginalLocation": "恢复到原始位置",
+ "localHistoryRestoreNewLocation": "恢复到新位置…",
+ "localHistoryRestoreChange": "恢复更改",
+ "localHistoryRestoreUnavailable": "无法恢复此更改,因为比较已过时。",
+ "localHistoryComparisonSimplified": "此大型比较已简化。 无法恢复单个更改。",
+ "localHistoryOlderChange": "较旧的版本更改: {text}",
+ "localHistoryCurrentChange": "当前内容更改: {text}",
+ "localHistoryDeleted": "已删除",
+ "localHistoryUntitled": "未命名",
+ "localHistoryClearDocument": "清除此文档的历史",
+ "localHistoryClearAll": "清除所有本地历史",
+ "localHistoryClearDocumentTitle": "清除文档历史?",
+ "localHistoryClearAllTitle": "清除所有本地历史?",
+ "localHistoryClearConfirmation": "这将永久删除存储的修订。 此操作无法撤销。",
+ "localHistoryWarningIndexRebuilt": "本地历史索引已损坏并从完整修订中重建。",
+ "localHistoryWarningUnsupportedFormat": "此本地历史存储使用不受支持的格式 {version}。",
+ "localHistoryWarningUnavailable": "本地历史不可用: {detail}",
+ "localHistoryWarningRecordingDisabled": "录音已禁用或此路径已排除。 在恢复之前重新启用录音。",
+ "localHistoryWarningPathChange": "无法记录本地历史路径更改: {detail}",
+ "localHistoryWarningDeletedPath": "本地历史记录无法保留已删除的路径: {detail}",
+ "localHistoryWarningRevisionMissing": "所选版本丢失或损坏。",
+ "localHistoryWarningRevisionRead": "无法读取所选版本: {detail}",
+ "localHistoryWarningCapture": "本地历史记录无法捕获此版本: {detail}",
+ "localHistoryRevisionAt": "{reason}・{timestamp}",
+ "localHistoryReasonBaseline": "基线",
+ "localHistoryReasonSaved": "已保存",
+ "localHistoryReasonAutomaticCheckpoint": "自动检查点",
+ "localHistoryReasonBeforeReload": "在重新加载之前",
+ "localHistoryReasonBeforeDiscard": "在丢弃之前",
+ "localHistoryReasonBeforeRestore": "恢复前",
+ "localHistoryReasonBeforeDelete": "删除前",
+ "localHistoryReasonExternalChange": "外部修改",
+ "settingsHistory": "历史记录",
+ "settingsClipboardHistoryTitle": "收集剪贴板历史",
+ "settingsClipboardHistoryDescription": "只保留本次 BusyMark 会话中已复制并成功粘贴的内容。",
+ "settingsLocalHistoryTitle": "记录本地历史",
+ "settingsLocalHistoryDescription": "将文档修订存储在此设备上,以便恢复和比较。",
+ "settingsHistoryCheckpoint": "自动检查点间隔",
+ "settingsHistoryRetention": "保留期限",
+ "settingsHistoryStorage": "最大存储",
+ "settingsHistoryExcludedPaths": "排除路径",
+ "settingsHistoryExcludedPathsHint": "每行一个绝对路径",
+ "settingsSecondsValue": "{value} 秒",
+ "settingsDaysValue": "{value} 天",
+ "settingsMebibytesValue": "{value} MiB"
}
diff --git a/lib/l10n/generated/app_localizations.dart b/lib/l10n/generated/app_localizations.dart
index 1c883050..74ee4668 100644
--- a/lib/l10n/generated/app_localizations.dart
+++ b/lib/l10n/generated/app_localizations.dart
@@ -1825,6 +1825,12 @@ abstract class AppLocalizations {
/// **'Hard line break'**
String get hardLineBreak;
+ /// Insert blank line command label.
+ ///
+ /// In en, this message translates to:
+ /// **'Insert blank line'**
+ String get insertBlankLine;
+
/// Text style menu tooltip.
///
/// In en, this message translates to:
@@ -2149,10 +2155,10 @@ abstract class AppLocalizations {
/// **'Files'**
String get files;
- /// Writerside table of contents sidebar tab label.
+ /// Writerside Topics sidebar tab label.
///
/// In en, this message translates to:
- /// **'TOC'**
+ /// **'Topics'**
String get toc;
/// Tooltip for the Writerside TOC action menu button.
@@ -2857,6 +2863,30 @@ abstract class AppLocalizations {
/// **'Invalid regular expression'**
String get sourceSearchInvalidRegex;
+ /// No description provided for @sourceSearchZeroLengthUnsupported.
+ ///
+ /// In en, this message translates to:
+ /// **'Zero-length regex matches are not supported (for example ^, \$, or lookaheads that match only a position).'**
+ String get sourceSearchZeroLengthUnsupported;
+
+ /// No description provided for @workspaceSearchIncomplete.
+ ///
+ /// In en, this message translates to:
+ /// **'Only some results are shown. Show more or narrow your search.'**
+ String get workspaceSearchIncomplete;
+
+ /// No description provided for @workspaceSearchShowMore.
+ ///
+ /// In en, this message translates to:
+ /// **'Show more results'**
+ String get workspaceSearchShowMore;
+
+ /// No description provided for @workspaceSearchSkippedFiles.
+ ///
+ /// In en, this message translates to:
+ /// **'Search is incomplete. These files exceeded 1 MiB or could not be read:'**
+ String get workspaceSearchSkippedFiles;
+
/// Status banner shown when source highlighting and folding are disabled for a large file.
///
/// In en, this message translates to:
@@ -5166,7 +5196,7 @@ abstract class AppLocalizations {
/// **'Export the active document or Writerside module as a PDF.'**
String get shortcutExportPdfDescription;
- /// Heading for the Writerside instances shown in the TOC sidebar.
+ /// Heading for the Writerside instances shown in the Topics sidebar.
///
/// In en, this message translates to:
/// **'Instances'**
@@ -6557,6 +6587,468 @@ abstract class AppLocalizations {
/// **'Choose a readable UTF-8 .css file up to 256 KiB, without imports, resource URLs, HTML, or executable or escaped CSS.'**
String get exportInvalidCss;
+ /// No description provided for @clipboardHistory.
+ ///
+ /// In en, this message translates to:
+ /// **'Clipboard History'**
+ String get clipboardHistory;
+
+ /// No description provided for @clipboardHistorySearchHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Search clipboard history'**
+ String get clipboardHistorySearchHint;
+
+ /// No description provided for @clipboardHistoryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Open the session-local clipboard history.'**
+ String get clipboardHistoryDescription;
+
+ /// No description provided for @clipboardCurrent.
+ ///
+ /// In en, this message translates to:
+ /// **'Current clipboard'**
+ String get clipboardCurrent;
+
+ /// No description provided for @clipboardCurrentExternal.
+ ///
+ /// In en, this message translates to:
+ /// **'Current clipboard from another application'**
+ String get clipboardCurrentExternal;
+
+ /// No description provided for @clipboardDestination.
+ ///
+ /// In en, this message translates to:
+ /// **'Paste into {name}'**
+ String clipboardDestination(String name);
+
+ /// No description provided for @clipboardSessionOnly.
+ ///
+ /// In en, this message translates to:
+ /// **'Stored for this BusyMark session only.'**
+ String get clipboardSessionOnly;
+
+ /// No description provided for @clipboardNoItems.
+ ///
+ /// In en, this message translates to:
+ /// **'Clipboard history is empty.'**
+ String get clipboardNoItems;
+
+ /// No description provided for @clipboardNoMatches.
+ ///
+ /// In en, this message translates to:
+ /// **'No clipboard items match this search.'**
+ String get clipboardNoMatches;
+
+ /// No description provided for @clipboardDisabled.
+ ///
+ /// In en, this message translates to:
+ /// **'Clipboard History collection is disabled in Settings.'**
+ String get clipboardDisabled;
+
+ /// No description provided for @clipboardUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'The current clipboard content is unavailable or unsupported.'**
+ String get clipboardUnavailable;
+
+ /// No description provided for @clipboardTooLarge.
+ ///
+ /// In en, this message translates to:
+ /// **'This clipboard item is too large to retain.'**
+ String get clipboardTooLarge;
+
+ /// No description provided for @clipboardPastePlainText.
+ ///
+ /// In en, this message translates to:
+ /// **'Paste as Plain Text'**
+ String get clipboardPastePlainText;
+
+ /// No description provided for @clipboardClearAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear Clipboard History'**
+ String get clipboardClearAll;
+
+ /// No description provided for @clipboardEntryText.
+ ///
+ /// In en, this message translates to:
+ /// **'Text'**
+ String get clipboardEntryText;
+
+ /// No description provided for @clipboardEntryRichText.
+ ///
+ /// In en, this message translates to:
+ /// **'Rich text'**
+ String get clipboardEntryRichText;
+
+ /// No description provided for @clipboardEntryImage.
+ ///
+ /// In en, this message translates to:
+ /// **'Image'**
+ String get clipboardEntryImage;
+
+ /// No description provided for @clipboardOrigin.
+ ///
+ /// In en, this message translates to:
+ /// **'Copied from {name}'**
+ String clipboardOrigin(String name);
+
+ /// No description provided for @localHistory.
+ ///
+ /// In en, this message translates to:
+ /// **'Local History'**
+ String get localHistory;
+
+ /// No description provided for @localHistoryEllipsis.
+ ///
+ /// In en, this message translates to:
+ /// **'Local History…'**
+ String get localHistoryEllipsis;
+
+ /// No description provided for @localHistoryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Show saved and automatic local revisions for the current document.'**
+ String get localHistoryDescription;
+
+ /// No description provided for @findLocalHistoryEllipsis.
+ ///
+ /// In en, this message translates to:
+ /// **'Find in Local History…'**
+ String get findLocalHistoryEllipsis;
+
+ /// No description provided for @findLocalHistoryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Search Local History documents and revision contents.'**
+ String get findLocalHistoryDescription;
+
+ /// No description provided for @localHistoryNoDocuments.
+ ///
+ /// In en, this message translates to:
+ /// **'No documents have Local History yet.'**
+ String get localHistoryNoDocuments;
+
+ /// No description provided for @localHistoryNoRevisions.
+ ///
+ /// In en, this message translates to:
+ /// **'This document has no Local History revisions.'**
+ String get localHistoryNoRevisions;
+
+ /// No description provided for @localHistoryNoMatches.
+ ///
+ /// In en, this message translates to:
+ /// **'No Local History revisions match this search.'**
+ String get localHistoryNoMatches;
+
+ /// No description provided for @localHistorySearchHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Search local history'**
+ String get localHistorySearchHint;
+
+ /// No description provided for @localHistorySelectedRevision.
+ ///
+ /// In en, this message translates to:
+ /// **'Selected revision'**
+ String get localHistorySelectedRevision;
+
+ /// No description provided for @localHistoryCurrentEditor.
+ ///
+ /// In en, this message translates to:
+ /// **'Current editor content'**
+ String get localHistoryCurrentEditor;
+
+ /// No description provided for @localHistoryCurrentDisk.
+ ///
+ /// In en, this message translates to:
+ /// **'Current content on disk'**
+ String get localHistoryCurrentDisk;
+
+ /// No description provided for @localHistoryMissingFile.
+ ///
+ /// In en, this message translates to:
+ /// **'The original file is missing.'**
+ String get localHistoryMissingFile;
+
+ /// No description provided for @localHistoryRestoreRevision.
+ ///
+ /// In en, this message translates to:
+ /// **'Restore Revision'**
+ String get localHistoryRestoreRevision;
+
+ /// No description provided for @localHistoryRestoreOriginalLocation.
+ ///
+ /// In en, this message translates to:
+ /// **'Restore to Original Location'**
+ String get localHistoryRestoreOriginalLocation;
+
+ /// No description provided for @localHistoryRestoreNewLocation.
+ ///
+ /// In en, this message translates to:
+ /// **'Restore to New Location…'**
+ String get localHistoryRestoreNewLocation;
+
+ /// No description provided for @localHistoryRestoreChange.
+ ///
+ /// In en, this message translates to:
+ /// **'Restore Change'**
+ String get localHistoryRestoreChange;
+
+ /// No description provided for @localHistoryRestoreUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'This change cannot be restored because the comparison is no longer current.'**
+ String get localHistoryRestoreUnavailable;
+
+ /// No description provided for @localHistoryComparisonSimplified.
+ ///
+ /// In en, this message translates to:
+ /// **'This large comparison is simplified. Restore individual changes is unavailable.'**
+ String get localHistoryComparisonSimplified;
+
+ /// No description provided for @localHistoryOlderChange.
+ ///
+ /// In en, this message translates to:
+ /// **'Older revision change: {text}'**
+ String localHistoryOlderChange(String text);
+
+ /// No description provided for @localHistoryCurrentChange.
+ ///
+ /// In en, this message translates to:
+ /// **'Current content change: {text}'**
+ String localHistoryCurrentChange(String text);
+
+ /// No description provided for @localHistoryDeleted.
+ ///
+ /// In en, this message translates to:
+ /// **'Deleted'**
+ String get localHistoryDeleted;
+
+ /// No description provided for @localHistoryUntitled.
+ ///
+ /// In en, this message translates to:
+ /// **'Untitled'**
+ String get localHistoryUntitled;
+
+ /// No description provided for @localHistoryClearDocument.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear This Document’s History'**
+ String get localHistoryClearDocument;
+
+ /// No description provided for @localHistoryClearAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear All Local History'**
+ String get localHistoryClearAll;
+
+ /// No description provided for @localHistoryClearDocumentTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear document history?'**
+ String get localHistoryClearDocumentTitle;
+
+ /// No description provided for @localHistoryClearAllTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear all Local History?'**
+ String get localHistoryClearAllTitle;
+
+ /// No description provided for @localHistoryClearConfirmation.
+ ///
+ /// In en, this message translates to:
+ /// **'This permanently removes stored revisions. This action cannot be undone.'**
+ String get localHistoryClearConfirmation;
+
+ /// No description provided for @localHistoryWarningIndexRebuilt.
+ ///
+ /// In en, this message translates to:
+ /// **'The Local History index was damaged and rebuilt from intact revisions.'**
+ String get localHistoryWarningIndexRebuilt;
+
+ /// No description provided for @localHistoryWarningUnsupportedFormat.
+ ///
+ /// In en, this message translates to:
+ /// **'This Local History store uses unsupported format {version}.'**
+ String localHistoryWarningUnsupportedFormat(String version);
+
+ /// No description provided for @localHistoryWarningUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Local History is unavailable: {detail}'**
+ String localHistoryWarningUnavailable(String detail);
+
+ /// No description provided for @localHistoryWarningRecordingDisabled.
+ ///
+ /// In en, this message translates to:
+ /// **'Recording is disabled or this path is excluded. Re-enable recording before restoring.'**
+ String get localHistoryWarningRecordingDisabled;
+
+ /// No description provided for @localHistoryWarningPathChange.
+ ///
+ /// In en, this message translates to:
+ /// **'Local History could not record the path change: {detail}'**
+ String localHistoryWarningPathChange(String detail);
+
+ /// No description provided for @localHistoryWarningDeletedPath.
+ ///
+ /// In en, this message translates to:
+ /// **'Local History could not retain the deleted path: {detail}'**
+ String localHistoryWarningDeletedPath(String detail);
+
+ /// No description provided for @localHistoryWarningRevisionMissing.
+ ///
+ /// In en, this message translates to:
+ /// **'The selected revision is missing or damaged.'**
+ String get localHistoryWarningRevisionMissing;
+
+ /// No description provided for @localHistoryWarningRevisionRead.
+ ///
+ /// In en, this message translates to:
+ /// **'The selected revision cannot be read: {detail}'**
+ String localHistoryWarningRevisionRead(String detail);
+
+ /// No description provided for @localHistoryWarningCapture.
+ ///
+ /// In en, this message translates to:
+ /// **'Local History could not capture this revision: {detail}'**
+ String localHistoryWarningCapture(String detail);
+
+ /// No description provided for @localHistoryRevisionAt.
+ ///
+ /// In en, this message translates to:
+ /// **'{reason} · {timestamp}'**
+ String localHistoryRevisionAt(String reason, String timestamp);
+
+ /// No description provided for @localHistoryReasonBaseline.
+ ///
+ /// In en, this message translates to:
+ /// **'Baseline'**
+ String get localHistoryReasonBaseline;
+
+ /// No description provided for @localHistoryReasonSaved.
+ ///
+ /// In en, this message translates to:
+ /// **'Saved'**
+ String get localHistoryReasonSaved;
+
+ /// No description provided for @localHistoryReasonAutomaticCheckpoint.
+ ///
+ /// In en, this message translates to:
+ /// **'Automatic checkpoint'**
+ String get localHistoryReasonAutomaticCheckpoint;
+
+ /// No description provided for @localHistoryReasonBeforeReload.
+ ///
+ /// In en, this message translates to:
+ /// **'Before reload'**
+ String get localHistoryReasonBeforeReload;
+
+ /// No description provided for @localHistoryReasonBeforeDiscard.
+ ///
+ /// In en, this message translates to:
+ /// **'Before discard'**
+ String get localHistoryReasonBeforeDiscard;
+
+ /// No description provided for @localHistoryReasonBeforeRestore.
+ ///
+ /// In en, this message translates to:
+ /// **'Before restore'**
+ String get localHistoryReasonBeforeRestore;
+
+ /// No description provided for @localHistoryReasonBeforeDelete.
+ ///
+ /// In en, this message translates to:
+ /// **'Before delete'**
+ String get localHistoryReasonBeforeDelete;
+
+ /// No description provided for @localHistoryReasonExternalChange.
+ ///
+ /// In en, this message translates to:
+ /// **'External change'**
+ String get localHistoryReasonExternalChange;
+
+ /// No description provided for @settingsHistory.
+ ///
+ /// In en, this message translates to:
+ /// **'History'**
+ String get settingsHistory;
+
+ /// No description provided for @settingsClipboardHistoryTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Collect Clipboard History'**
+ String get settingsClipboardHistoryTitle;
+
+ /// No description provided for @settingsClipboardHistoryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Keep copied and successfully pasted content for this BusyMark session only.'**
+ String get settingsClipboardHistoryDescription;
+
+ /// No description provided for @settingsLocalHistoryTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Record Local History'**
+ String get settingsLocalHistoryTitle;
+
+ /// No description provided for @settingsLocalHistoryDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Store document revisions on this device for recovery and comparison.'**
+ String get settingsLocalHistoryDescription;
+
+ /// No description provided for @settingsHistoryCheckpoint.
+ ///
+ /// In en, this message translates to:
+ /// **'Automatic checkpoint interval'**
+ String get settingsHistoryCheckpoint;
+
+ /// No description provided for @settingsHistoryRetention.
+ ///
+ /// In en, this message translates to:
+ /// **'Retention age'**
+ String get settingsHistoryRetention;
+
+ /// No description provided for @settingsHistoryStorage.
+ ///
+ /// In en, this message translates to:
+ /// **'Maximum storage'**
+ String get settingsHistoryStorage;
+
+ /// No description provided for @settingsHistoryExcludedPaths.
+ ///
+ /// In en, this message translates to:
+ /// **'Excluded paths'**
+ String get settingsHistoryExcludedPaths;
+
+ /// No description provided for @settingsHistoryExcludedPathsHint.
+ ///
+ /// In en, this message translates to:
+ /// **'One absolute path per line'**
+ String get settingsHistoryExcludedPathsHint;
+
+ /// No description provided for @settingsSecondsValue.
+ ///
+ /// In en, this message translates to:
+ /// **'{value} seconds'**
+ String settingsSecondsValue(int value);
+
+ /// No description provided for @settingsDaysValue.
+ ///
+ /// In en, this message translates to:
+ /// **'{value} days'**
+ String settingsDaysValue(int value);
+
+ /// No description provided for @settingsMebibytesValue.
+ ///
+ /// In en, this message translates to:
+ /// **'{value} MiB'**
+ String settingsMebibytesValue(int value);
+
/// Invalid numeric export setting.
///
/// In en, this message translates to:
diff --git a/lib/l10n/generated/app_localizations_ar.dart b/lib/l10n/generated/app_localizations_ar.dart
index de018b1b..c3a7b289 100644
--- a/lib/l10n/generated/app_localizations_ar.dart
+++ b/lib/l10n/generated/app_localizations_ar.dart
@@ -935,6 +935,9 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get hardLineBreak => 'فاصل سطر صريح';
+ @override
+ String get insertBlankLine => 'إدراج سطر فارغ';
+
@override
String get textStyle => 'نمط النص';
@@ -1120,7 +1123,7 @@ class AppLocalizationsAr extends AppLocalizations {
String get files => 'ملفات';
@override
- String get toc => 'جدول المحتويات';
+ String get toc => 'موضوعات';
@override
String get tocActions => 'إجراءات جدول المحتويات';
@@ -1563,6 +1566,21 @@ class AppLocalizationsAr extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'تعبير نمطي غير صالح';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'لا تُدعم مطابقات التعبير النمطي ذات الطول الصفري (مثل ^ أو \$ أو الاستباق الذي يطابق موضعًا فقط).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'تظهر بعض النتائج فقط. اعرض المزيد أو ضيّق نطاق البحث.';
+
+ @override
+ String get workspaceSearchShowMore => 'عرض المزيد من النتائج';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'البحث غير مكتمل. تجاوز حجم هذه الملفات 1 MiB أو تعذّرت قراءتها:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'ملف كبير: تم إيقاف التمييز والطي مؤقتًا';
@@ -3927,6 +3945,281 @@ class AppLocalizationsAr extends AppLocalizations {
String get exportInvalidCss =>
'اختر ملف .css مقروءًا بترميز UTF-8 حتى 256 KiB، دون استيراد أو عناوين موارد أو HTML أو CSS قابل للتنفيذ أو يحتوي على محارف هروب.';
+ @override
+ String get clipboardHistory => 'سجل النسخ واللصق';
+
+ @override
+ String get clipboardHistorySearchHint => 'البحث في سجل الحافظة';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'افتح سجل النسخ واللصق الخاص بالجلسة.';
+
+ @override
+ String get clipboardCurrent => 'النسخة واللصق الحالية';
+
+ @override
+ String get clipboardCurrentExternal => 'النسخة واللصق من تطبيق آخر';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'الصق في $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'مخزن فقط لهذه جلسة BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'سجل النسخ واللصق فارغ.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'لا توجد عناصر في سجل النسخ واللصق تطابق هذا البحث.';
+
+ @override
+ String get clipboardDisabled => 'مجموعة سجل النسخ واللصق معطلة في الإعدادات.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'محتوى النسخة واللصق الحالي غير متاح أو غير مدعوم.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'هذا العنصر في النسخة واللصق كبير جدًا بحيث لا يمكن الاحتفاظ به.';
+
+ @override
+ String get clipboardPastePlainText => 'الصق كنص عادي';
+
+ @override
+ String get clipboardClearAll => 'مسح سجل النسخ واللصق';
+
+ @override
+ String get clipboardEntryText => 'نص';
+
+ @override
+ String get clipboardEntryRichText => 'نص غني';
+
+ @override
+ String get clipboardEntryImage => 'صورة';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'تم نسخها من $name';
+ }
+
+ @override
+ String get localHistory => 'سجل محلي';
+
+ @override
+ String get localHistoryEllipsis => 'سجل محلي…';
+
+ @override
+ String get localHistoryDescription =>
+ 'اعرض النسخ والمراجعات المحلية المحفوظة والآلية للمستند الحالي.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'البحث في التاريخ المحلي…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'البحث في مستندات التاريخ المحلي ومحتوى الإصدارات.';
+
+ @override
+ String get localHistoryNoDocuments => 'لا توجد مستندات لديها تاريخ محلي.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'هذا المستند ليس لديه إصدارات في التاريخ المحلي.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'لا تتطابق أي إصدارات في التاريخ المحلي مع هذا البحث.';
+
+ @override
+ String get localHistorySearchHint => 'البحث في السجل المحلي';
+
+ @override
+ String get localHistorySelectedRevision => 'الإصدار المحدد';
+
+ @override
+ String get localHistoryCurrentEditor => 'محتوى المحرر الحالي';
+
+ @override
+ String get localHistoryCurrentDisk => 'المحتوى الحالي على القرص';
+
+ @override
+ String get localHistoryMissingFile => 'الملف الأصلي مفقود.';
+
+ @override
+ String get localHistoryRestoreRevision => 'استعادة الإصدار';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => 'استعادة إلى الموقع الأصلي';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'استعادة إلى موقع جديد…';
+
+ @override
+ String get localHistoryRestoreChange => 'استعادة التغيير';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'لا يمكن استعادة هذا التغيير لأن المقارنة لم تعد حديثة.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'تم تبسيط هذا المقارنة الكبير. لا يمكن استعادة التغييرات الفردية.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'تغيير الإصدار الأقدم: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'تغيير المحتوى الحالي: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'تم الحذف';
+
+ @override
+ String get localHistoryUntitled => 'غير مُسمى';
+
+ @override
+ String get localHistoryClearDocument => 'مسح سجل هذا المستند';
+
+ @override
+ String get localHistoryClearAll => 'مسح السجل المحلي بالكامل';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'هل تريد مسح سجل المحلي؟';
+
+ @override
+ String get localHistoryClearAllTitle => 'هل تريد مسح السجل المحلي؟';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'هذا يزيل بشكل دائم النسخ المحفوظة. لا يمكن التراجع عن هذه العملية.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'تم تلف فهرس السجل المحلي وإعادة بنائه من النسخ السليمة.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'يستخدم هذا السجل المحلي تنسيقًا غير مدعوم $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'السجل المحلي غير متاح: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'تم تعطيل التسجيل أو تم استبعاد هذا المسار. قم بتفعيل التسجيل مرة أخرى قبل الاستعادة.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'لم يتمكن السجل المحلي من تسجيل تغيير المسار: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'لم يتمكن السجل المحلي من الاحتفاظ بالمسار المحذوف: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'النسخة المختارة مفقودة أو تالفة.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'لا يمكن قراءة النسخة المختارة: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'لم يتمكن السجل المحلي من التقاط هذه النسخة: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason، $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'النسخة الأساسية';
+
+ @override
+ String get localHistoryReasonSaved => 'محفوظ';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'نقطة فحص تلقائية';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'قبل إعادة التحميل';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'قبل التخلص';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'قبل الاستعادة';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'قبل الحذف';
+
+ @override
+ String get localHistoryReasonExternalChange => 'تغيير خارجي';
+
+ @override
+ String get settingsHistory => 'سجل التغييرات';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'جمع سجل الحافظة';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'احتفظ بالمحتوى المنسوخ والمُلصق بنجاح لهذه الجلسة فقط في BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'تسجيل سجل محلي';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'حفظ إصدارات المستند على هذا الجهاز للتعافي والمقارنة.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'فترة التحقق التلقائي';
+
+ @override
+ String get settingsHistoryRetention => 'مدة الاحتفاظ';
+
+ @override
+ String get settingsHistoryStorage => 'الحد الأقصى للتخزين';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'مسارات مستبعدة';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'مسار مطلق واحد في كل سطر';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value ثوان';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value أيام';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value ميجابايت';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: أدخل رقمًا بين $minimum و$maximum.';
diff --git a/lib/l10n/generated/app_localizations_de.dart b/lib/l10n/generated/app_localizations_de.dart
index 77dcf063..18d2b9b1 100644
--- a/lib/l10n/generated/app_localizations_de.dart
+++ b/lib/l10n/generated/app_localizations_de.dart
@@ -955,6 +955,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get hardLineBreak => 'Harter Zeilenumbruch';
+ @override
+ String get insertBlankLine => 'Leerzeile einfügen';
+
@override
String get textStyle => 'Textstil';
@@ -1137,7 +1140,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get files => 'Dateien';
@override
- String get toc => 'Inhaltsverzeichnis';
+ String get toc => 'Themen';
@override
String get tocActions => 'Inhaltsverzeichnisaktionen';
@@ -1577,6 +1580,21 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Ungültiger regulärer Ausdruck';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Regex-Treffer ohne Zeichen werden nicht unterstützt (z. B. ^, \$ oder Lookaheads, die nur eine Position treffen).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Es wird nur ein Teil der Ergebnisse angezeigt. Zeigen Sie mehr an oder grenzen Sie die Suche ein.';
+
+ @override
+ String get workspaceSearchShowMore => 'Weitere Ergebnisse anzeigen';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Die Suche ist unvollständig. Diese Dateien sind größer als 1 MiB oder konnten nicht gelesen werden:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Große Datei: Hervorhebung und Faltung sind pausiert';
@@ -3922,6 +3940,290 @@ class AppLocalizationsDe extends AppLocalizations {
String get exportInvalidCss =>
'Wählen Sie eine lesbare UTF-8-CSS-Datei bis 256 KiB ohne Importe, Ressourcen-URLs, HTML sowie ausführbares oder maskiertes CSS.';
+ @override
+ String get clipboardHistory => 'Zwischenablage-Historie';
+
+ @override
+ String get clipboardHistorySearchHint => 'Zwischenablageverlauf durchsuchen';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Öffne die Sitzungs-spezifische Zwischenablage-Historie.';
+
+ @override
+ String get clipboardCurrent => 'Aktuelle Zwischenablage';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Aktuelle Zwischenablage aus einer anderen Anwendung';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Einfügen in $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Nur für diese BusyMark-Sitzung gespeichert.';
+
+ @override
+ String get clipboardNoItems => 'Zwischenablage-Historie ist leer.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Keine Zwischenablagen-Elemente passen zu dieser Suche.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Die Sammlung \"Zwischenablage-Historie\" ist in den Einstellungen deaktiviert.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Der aktuelle Zwischenablage-Inhalt ist nicht verfügbar oder wird nicht unterstützt.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Dieses Zwischenablagen-Element ist zu groß, um es zu speichern.';
+
+ @override
+ String get clipboardPastePlainText => 'Einfügen als reinen Text';
+
+ @override
+ String get clipboardClearAll => 'Löschen der Zwischenablage-Historie';
+
+ @override
+ String get clipboardEntryText => 'Textinhalt';
+
+ @override
+ String get clipboardEntryRichText => 'Reicher Text';
+
+ @override
+ String get clipboardEntryImage => 'Bild';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Kopiert aus $name';
+ }
+
+ @override
+ String get localHistory => 'Lokale Historie';
+
+ @override
+ String get localHistoryEllipsis => 'Lokale Historie…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Zeige gespeicherte und automatische lokale Revisionen für das aktuelle Dokument.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'In Lokaler Historie suchen…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Lokale Historie Dokumente und Revisionen durchsuchen.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Es gibt noch keine Dokumente mit Lokaler Historie.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Dieses Dokument hat keine Lokalen Historie Revisionen.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Keine Lokalen Historie Revisionen passen zu dieser Suche.';
+
+ @override
+ String get localHistorySearchHint => 'Lokalen Verlauf durchsuchen';
+
+ @override
+ String get localHistorySelectedRevision => 'Ausgewählte Revision';
+
+ @override
+ String get localHistoryCurrentEditor => 'Aktueller Editor Inhalt';
+
+ @override
+ String get localHistoryCurrentDisk => 'Aktueller Inhalt auf der Festplatte';
+
+ @override
+ String get localHistoryMissingFile => 'Die Originaldatei fehlt.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Revision Wiederherstellen';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Zur Originalposition Wiederherstellen';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Zur neuen Position Wiederherstellen…';
+
+ @override
+ String get localHistoryRestoreChange => 'Änderung Wiederherstellen';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Diese Änderung kann nicht wiederhergestellt werden, da der Vergleich nicht mehr aktuell ist.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Dieser große Vergleich wurde vereinfacht. Einzelne Änderungen können nicht wiederhergestellt werden.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Ältere Revision Änderung: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Aktuelle Inhalt Änderung: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Gelöscht';
+
+ @override
+ String get localHistoryUntitled => 'Unbenannt';
+
+ @override
+ String get localHistoryClearDocument =>
+ 'Löschen Sie den Verlauf dieses Dokuments';
+
+ @override
+ String get localHistoryClearAll => 'Löschen Sie den gesamten lokalen Verlauf';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Verlauf dieses Dokuments löschen?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Verlauf dieses Dokuments löschen?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Dies entfernt dauerhaft gespeicherte Revisionen. Diese Aktion kann nicht rückgängig gemacht werden.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Der lokale Verlauf wurde beschädigt und aus intakten Revisionen neu aufgebaut.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Dieser lokale Verlauf verwendet das nicht unterstützte Format $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Lokaler Verlauf ist nicht verfügbar: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Aufzeichnung ist deaktiviert oder dieser Pfad ist ausgeschlossen. Aktivieren Sie die Aufzeichnung erneut, bevor Sie wiederherstellen.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Lokaler Verlauf konnte die Pfadänderung nicht aufzeichnen: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Lokaler Verlauf konnte den gelöschten Pfad nicht speichern: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Die ausgewählte Revision fehlt oder ist beschädigt.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Die ausgewählte Revision kann nicht gelesen werden: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Lokaler Verlauf konnte diese Revision nicht erfassen: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Basisversion';
+
+ @override
+ String get localHistoryReasonSaved => 'Gespeichert';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Automatische Checkpoint';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Vor dem Neuladen';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Vor dem Entsorgen';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Vor der Wiederherstellung';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Vor dem Löschen';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Externe Änderung';
+
+ @override
+ String get settingsHistory => 'Verlauf';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Verlauf der Zwischenablage sammeln';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Behalten Sie den Inhalt, der erfolgreich kopiert und eingefügt wurde, nur für diese BusyMark-Sitzung.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Lokalen Verlauf speichern';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Speichern Sie Dokumentversionen auf diesem Gerät zur Wiederherstellung und zum Vergleich.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Automatische Checkpoint-Intervall';
+
+ @override
+ String get settingsHistoryRetention => 'Aufbewahrungsdauer';
+
+ @override
+ String get settingsHistoryStorage => 'Maximale Speicherkapazität';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Ausschließlich Pfade';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Ein absoluter Pfad pro Zeile';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value Sekunden';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value Tage';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: Geben Sie eine Zahl zwischen $minimum und $maximum ein.';
diff --git a/lib/l10n/generated/app_localizations_en.dart b/lib/l10n/generated/app_localizations_en.dart
index 73313181..5382e3d7 100644
--- a/lib/l10n/generated/app_localizations_en.dart
+++ b/lib/l10n/generated/app_localizations_en.dart
@@ -936,6 +936,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get hardLineBreak => 'Hard line break';
+ @override
+ String get insertBlankLine => 'Insert blank line';
+
@override
String get textStyle => 'Text style';
@@ -1118,7 +1121,7 @@ class AppLocalizationsEn extends AppLocalizations {
String get files => 'Files';
@override
- String get toc => 'TOC';
+ String get toc => 'Topics';
@override
String get tocActions => 'TOC actions';
@@ -1557,6 +1560,21 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Invalid regular expression';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Zero-length regex matches are not supported (for example ^, \$, or lookaheads that match only a position).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Only some results are shown. Show more or narrow your search.';
+
+ @override
+ String get workspaceSearchShowMore => 'Show more results';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Search is incomplete. These files exceeded 1 MiB or could not be read:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Large file: highlighting and folding are paused';
@@ -3916,6 +3934,282 @@ class AppLocalizationsEn extends AppLocalizations {
String get exportInvalidCss =>
'Choose a readable UTF-8 .css file up to 256 KiB, without imports, resource URLs, HTML, or executable or escaped CSS.';
+ @override
+ String get clipboardHistory => 'Clipboard History';
+
+ @override
+ String get clipboardHistorySearchHint => 'Search clipboard history';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Open the session-local clipboard history.';
+
+ @override
+ String get clipboardCurrent => 'Current clipboard';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Current clipboard from another application';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Paste into $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'Stored for this BusyMark session only.';
+
+ @override
+ String get clipboardNoItems => 'Clipboard history is empty.';
+
+ @override
+ String get clipboardNoMatches => 'No clipboard items match this search.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Clipboard History collection is disabled in Settings.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'The current clipboard content is unavailable or unsupported.';
+
+ @override
+ String get clipboardTooLarge => 'This clipboard item is too large to retain.';
+
+ @override
+ String get clipboardPastePlainText => 'Paste as Plain Text';
+
+ @override
+ String get clipboardClearAll => 'Clear Clipboard History';
+
+ @override
+ String get clipboardEntryText => 'Text';
+
+ @override
+ String get clipboardEntryRichText => 'Rich text';
+
+ @override
+ String get clipboardEntryImage => 'Image';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Copied from $name';
+ }
+
+ @override
+ String get localHistory => 'Local History';
+
+ @override
+ String get localHistoryEllipsis => 'Local History…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Show saved and automatic local revisions for the current document.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Find in Local History…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Search Local History documents and revision contents.';
+
+ @override
+ String get localHistoryNoDocuments => 'No documents have Local History yet.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'This document has no Local History revisions.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'No Local History revisions match this search.';
+
+ @override
+ String get localHistorySearchHint => 'Search local history';
+
+ @override
+ String get localHistorySelectedRevision => 'Selected revision';
+
+ @override
+ String get localHistoryCurrentEditor => 'Current editor content';
+
+ @override
+ String get localHistoryCurrentDisk => 'Current content on disk';
+
+ @override
+ String get localHistoryMissingFile => 'The original file is missing.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Restore Revision';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Restore to Original Location';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Restore to New Location…';
+
+ @override
+ String get localHistoryRestoreChange => 'Restore Change';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'This change cannot be restored because the comparison is no longer current.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'This large comparison is simplified. Restore individual changes is unavailable.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Older revision change: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Current content change: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Deleted';
+
+ @override
+ String get localHistoryUntitled => 'Untitled';
+
+ @override
+ String get localHistoryClearDocument => 'Clear This Document’s History';
+
+ @override
+ String get localHistoryClearAll => 'Clear All Local History';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'Clear document history?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Clear all Local History?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'This permanently removes stored revisions. This action cannot be undone.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'The Local History index was damaged and rebuilt from intact revisions.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'This Local History store uses unsupported format $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Local History is unavailable: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Recording is disabled or this path is excluded. Re-enable recording before restoring.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Local History could not record the path change: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Local History could not retain the deleted path: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'The selected revision is missing or damaged.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'The selected revision cannot be read: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Local History could not capture this revision: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason · $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Baseline';
+
+ @override
+ String get localHistoryReasonSaved => 'Saved';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Automatic checkpoint';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Before reload';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Before discard';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Before restore';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Before delete';
+
+ @override
+ String get localHistoryReasonExternalChange => 'External change';
+
+ @override
+ String get settingsHistory => 'History';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Collect Clipboard History';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Keep copied and successfully pasted content for this BusyMark session only.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Record Local History';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Store document revisions on this device for recovery and comparison.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Automatic checkpoint interval';
+
+ @override
+ String get settingsHistoryRetention => 'Retention age';
+
+ @override
+ String get settingsHistoryStorage => 'Maximum storage';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Excluded paths';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'One absolute path per line';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value seconds';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value days';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: enter a number between $minimum and $maximum.';
diff --git a/lib/l10n/generated/app_localizations_es.dart b/lib/l10n/generated/app_localizations_es.dart
index ac44a06e..7f22ea0a 100644
--- a/lib/l10n/generated/app_localizations_es.dart
+++ b/lib/l10n/generated/app_localizations_es.dart
@@ -953,6 +953,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get hardLineBreak => 'Salto de línea forzado';
+ @override
+ String get insertBlankLine => 'Insertar línea en blanco';
+
@override
String get textStyle => 'Estilo de texto';
@@ -1137,7 +1140,7 @@ class AppLocalizationsEs extends AppLocalizations {
String get files => 'Archivos';
@override
- String get toc => 'Índice';
+ String get toc => 'Temas';
@override
String get tocActions => 'Acciones del índice';
@@ -1574,6 +1577,21 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Expresión regular no válida';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'No se admiten coincidencias de longitud cero (por ejemplo, ^, \$ o anticipaciones que solo coinciden con una posición).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Solo se muestran algunos resultados. Muestre más o limite la búsqueda.';
+
+ @override
+ String get workspaceSearchShowMore => 'Mostrar más resultados';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'La búsqueda está incompleta. Estos archivos superan 1 MiB o no se pudieron leer:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Archivo grande: el resaltado y el plegado están en pausa';
@@ -3925,6 +3943,291 @@ class AppLocalizationsEs extends AppLocalizations {
String get exportInvalidCss =>
'Elija un archivo .css UTF-8 legible de hasta 256 KiB, sin importaciones, URL de recursos, HTML ni CSS ejecutable o escapado.';
+ @override
+ String get clipboardHistory => 'Historial de la Copia';
+
+ @override
+ String get clipboardHistorySearchHint =>
+ 'Buscar en el historial del portapapeles';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Abrir el historial de la copia local de la sesión.';
+
+ @override
+ String get clipboardCurrent => 'Copia actual';
+
+ @override
+ String get clipboardCurrentExternal => 'Copia actual de otra aplicación';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Pegar en $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Almacenado solo para esta sesión de BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'El historial de la copia está vacío.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Ningún elemento de la copia coincide con esta búsqueda.';
+
+ @override
+ String get clipboardDisabled =>
+ 'La colección de Historial de la Copia está deshabilitada en la configuración.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'El contenido de la copia actual no está disponible o no es compatible.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Este elemento de la copia es demasiado grande para conservarlo.';
+
+ @override
+ String get clipboardPastePlainText => 'Pegar como Texto Plano';
+
+ @override
+ String get clipboardClearAll => 'Borrar el Historial de la Copia';
+
+ @override
+ String get clipboardEntryText => 'Texto';
+
+ @override
+ String get clipboardEntryRichText => 'Texto enriquecido';
+
+ @override
+ String get clipboardEntryImage => 'Imagen';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Copiado de $name';
+ }
+
+ @override
+ String get localHistory => 'Historial Local';
+
+ @override
+ String get localHistoryEllipsis => 'Historial Local…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Mostrar las revisiones locales guardadas y automáticas para el documento actual.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Buscar en el Historial Local…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Buscar documentos y contenido de revisiones en el Historial Local.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Ningún documento tiene Historial Local.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Este documento no tiene revisiones en el Historial Local.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Ninguna revisión del Historial Local coincide con esta búsqueda.';
+
+ @override
+ String get localHistorySearchHint => 'Buscar en el historial local';
+
+ @override
+ String get localHistorySelectedRevision => 'Revisión seleccionada';
+
+ @override
+ String get localHistoryCurrentEditor => 'Contenido del editor actual';
+
+ @override
+ String get localHistoryCurrentDisk => 'Contenido actual en el disco';
+
+ @override
+ String get localHistoryMissingFile => 'El archivo original está ausente.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Restaurar Revisión';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Restaurar a la Ubicación Original';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Restaurar a una Nueva Ubicación…';
+
+ @override
+ String get localHistoryRestoreChange => 'Restaurar Cambio';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Este cambio no puede ser restaurado porque la comparación ya no es válida.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Esta comparación grande se ha simplificado. Restaurar cambios individuales no está disponible.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Cambio de revisión anterior: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Cambio de contenido actual: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Eliminado';
+
+ @override
+ String get localHistoryUntitled => 'Sin título';
+
+ @override
+ String get localHistoryClearDocument =>
+ 'Borrar el historial de este documento';
+
+ @override
+ String get localHistoryClearAll => 'Borrar todo el historial local';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ '¿Borrar el historial del documento?';
+
+ @override
+ String get localHistoryClearAllTitle => '¿Borrar todo el historial local?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Esto elimina permanentemente las revisiones almacenadas. Esta acción no se puede deshacer.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'El índice de historial local se dañó y se reconstruyó a partir de revisiones intactas.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Esta tienda de historial local utiliza un formato no soportado $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'El historial local no está disponible: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'La grabación está deshabilitada o esta ruta está excluida. Re-habilite la grabación antes de restaurar.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'El historial local no pudo registrar el cambio de ruta: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'El historial local no pudo conservar la ruta eliminada: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'La revisión seleccionada está ausente o dañada.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'La revisión seleccionada no se puede leer: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'El historial local no pudo capturar esta revisión: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Línea base';
+
+ @override
+ String get localHistoryReasonSaved => 'Guardado';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Puntos de control automático';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Antes de recargar';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Antes de descartar';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Antes de restaurar';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Antes de eliminar';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Cambio externo';
+
+ @override
+ String get settingsHistory => 'Historial';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Recolectar Historial de Copia';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Mantener el contenido copiado y pegado con éxito solo para esta sesión de BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Registrar Historial Local';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Almacenar revisiones del documento en este dispositivo para recuperación y comparación.';
+
+ @override
+ String get settingsHistoryCheckpoint =>
+ 'Intervalo de punto de control automático';
+
+ @override
+ String get settingsHistoryRetention => 'Edad de retención';
+
+ @override
+ String get settingsHistoryStorage => 'Almacenamiento máximo';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Rutas excluidas';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Una ruta absoluta por línea';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value segundos';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value días';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: introduzca un número entre $minimum y $maximum.';
diff --git a/lib/l10n/generated/app_localizations_et.dart b/lib/l10n/generated/app_localizations_et.dart
index d1109651..6cbb1541 100644
--- a/lib/l10n/generated/app_localizations_et.dart
+++ b/lib/l10n/generated/app_localizations_et.dart
@@ -940,6 +940,9 @@ class AppLocalizationsEt extends AppLocalizations {
@override
String get hardLineBreak => 'Sundreavahetus';
+ @override
+ String get insertBlankLine => 'Lisa tühi rida';
+
@override
String get textStyle => 'Tekstilaad';
@@ -1122,7 +1125,7 @@ class AppLocalizationsEt extends AppLocalizations {
String get files => 'Failid';
@override
- String get toc => 'Sisukord';
+ String get toc => 'Teemad';
@override
String get tocActions => 'Sisukorra toimingud';
@@ -1554,6 +1557,21 @@ class AppLocalizationsEt extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Vigane regulaaravaldis';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Nullpikkusega regulaaravaldiste vasteid ei toetata (näiteks ^, \$ või ainult asukohta sobitavad ettevaated).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Kuvatakse ainult osa tulemustest. Kuva rohkem või kitsenda otsingut.';
+
+ @override
+ String get workspaceSearchShowMore => 'Kuva rohkem tulemusi';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Otsing pole täielik. Need failid on suuremad kui 1 MiB või neid ei saanud lugeda:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Suur fail: esiletõstmine ja voltimine on peatatud';
@@ -3887,6 +3905,287 @@ class AppLocalizationsEt extends AppLocalizations {
String get exportInvalidCss =>
'Vali loetav UTF-8 .css-fail kuni 256 KiB, ilma importide, ressursside URL-ide, HTML-i ning käivitatava või paojadadega CSS-ita.';
+ @override
+ String get clipboardHistory => 'Kleepimiskredi ajalugu';
+
+ @override
+ String get clipboardHistorySearchHint => 'Otsi lõikepuhvri ajaloost';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Ava sessiooni-spetsiifiline kleepimiskredi ajalugu.';
+
+ @override
+ String get clipboardCurrent => 'Praegune kleepimiskredi';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Praegune kleepimiskredi teistest rakendusest';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Kleepi $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Salvestatud ainult sellele BusyMark sessioonile.';
+
+ @override
+ String get clipboardNoItems => 'Kleepimiskredi ajalugu on tühi.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Ei leitud kleepimiskredi elemente, mis vastavad sellele otsingule.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Kleepimiskredi ajalugu on peatatud seadetes.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Praegune kleepimiskredi sisu pole saadaval või toetatud.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'See kleepimiskredi element on liiga suur, et seda säilitada.';
+
+ @override
+ String get clipboardPastePlainText => 'Kleepi kui tavatekst';
+
+ @override
+ String get clipboardClearAll => 'Puhasta kleepimiskredi ajalugu';
+
+ @override
+ String get clipboardEntryText => 'Tekst';
+
+ @override
+ String get clipboardEntryRichText => 'Rikas tekst';
+
+ @override
+ String get clipboardEntryImage => 'Pilt';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Kleepitud $name';
+ }
+
+ @override
+ String get localHistory => 'Paikne ajalugu';
+
+ @override
+ String get localHistoryEllipsis => 'Paikne ajalugu…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Näita salvestatud ja automaatseid paikseid versioone sellele dokumendile.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Leia kohalikust ajaloost…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Otsi kohalises ajaloos ja versioonide sisu.';
+
+ @override
+ String get localHistoryNoDocuments => 'Mida ei ole kohalist ajaloos.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Selle dokumendil pole kohalist ajaloos.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Ei leidunud kohalise ajalooga versioone.';
+
+ @override
+ String get localHistorySearchHint => 'Otsi kohalikust ajaloost';
+
+ @override
+ String get localHistorySelectedRevision => 'Valitud versioon';
+
+ @override
+ String get localHistoryCurrentEditor => 'Praegune redaktoori sisu';
+
+ @override
+ String get localHistoryCurrentDisk => 'Praegune sisu faile';
+
+ @override
+ String get localHistoryMissingFile => 'Algne fail puudub.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Taasta versioon';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => 'Taasta algsele asukohale';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Taasta uuele asukohale…';
+
+ @override
+ String get localHistoryRestoreChange => 'Taasta muudatus';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Seda muudatust ei saa taastada, kuna võrdlemine pole enam kehtiv.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'See suur võrdlemine on lihtsustatud. Eraldi muudatuste taastamine pole võimalik.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Vana versiooni muudatus: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Praeguse versiooni muudatus: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Hävitatud';
+
+ @override
+ String get localHistoryUntitled => 'Nimetamata';
+
+ @override
+ String get localHistoryClearDocument => 'Kaustade ajaloo kustutada';
+
+ @override
+ String get localHistoryClearAll => 'Kõik kohalikud ajaloo kustutada';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Kas soovite kustutada dokumendi ajaloo?';
+
+ @override
+ String get localHistoryClearAllTitle =>
+ 'Kas soovite kustutada kõik kohaliku aja?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'See eemaldab püsivalt salvestatud versioonid. Seda toimingut ei saa tagasi pöörata.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Kohalik ajaloo indeks oli kahjustatud ja taastati tervete versioonide abil.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'See kohalik ajaloo salvestus kasutab tugimat format $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Kohalik ajaloo pole saadaval: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Salvestamine on välja lülitatud või see tee on eemaldatud. Lülitage salvestamine uuesti sisse enne taastamist.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Kohalik ajaloo ei suutnud salvestada teed: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Kohalik ajaloo ei suutnud säilitada kustutatud teed: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Valitud versioon on kadunud või kahjustatud.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Valitud versiooni ei saa lugeda: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Kohalik ajaloo ei suutnud seda versiooni salvestada: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Alusversioon';
+
+ @override
+ String get localHistoryReasonSaved => 'Salvestatud';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Automaatne kontrollpunkt';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Enne laadimist';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Enne kõrvaldamist';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Enne taastamist';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Enne kustutamist';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Väline muudatus';
+
+ @override
+ String get settingsHistory => 'Ajalugu';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Salvesta klipi ajalugu';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Hoiab salvestatud ja edukalt kopeeritud sisu ainult selle BusyMark sessiooni jaoks.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Salvesta kohalik ajalugu';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Salvesta dokumendi versioonid selle seadmele taastamiseks ja võrdlemiseks.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Automaatne kontrollpunkte intervall';
+
+ @override
+ String get settingsHistoryRetention => 'Säilitamise aeg';
+
+ @override
+ String get settingsHistoryStorage => 'Maksimaalne salvestamine';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Exkludeeritud teed';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Üks absoluutne tee per rida';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value sekundid';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value päevad';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: sisesta arv vahemikus $minimum kuni $maximum.';
diff --git a/lib/l10n/generated/app_localizations_fa.dart b/lib/l10n/generated/app_localizations_fa.dart
index afb09527..403d1538 100644
--- a/lib/l10n/generated/app_localizations_fa.dart
+++ b/lib/l10n/generated/app_localizations_fa.dart
@@ -936,6 +936,9 @@ class AppLocalizationsFa extends AppLocalizations {
@override
String get hardLineBreak => 'شکست خط اجباری';
+ @override
+ String get insertBlankLine => 'درج خط خالی';
+
@override
String get textStyle => 'سبک متن';
@@ -1142,7 +1145,7 @@ class AppLocalizationsFa extends AppLocalizations {
String get files => 'فایلها';
@override
- String get toc => 'فهرست مطالب';
+ String get toc => 'موضوعات';
@override
String get tocActions => 'عملیات فهرست مطالب';
@@ -1592,6 +1595,21 @@ class AppLocalizationsFa extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'عبارت منظم نامعتبر است';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'تطبیقهای با طول صفر در عبارت منظم پشتیبانی نمیشوند (مانند ^، \$ یا نگاهبهجلو که فقط با یک موقعیت تطبیق دارد).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'فقط بخشی از نتایج نمایش داده میشود. نتایج بیشتری نمایش دهید یا جستجو را محدود کنید.';
+
+ @override
+ String get workspaceSearchShowMore => 'نمایش نتایج بیشتر';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'جستجو کامل نیست. حجم این فایلها بیش از 1 MiB است یا خواندن آنها ممکن نبود:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'فایل بزرگ: برجستهسازی و جمعکردن موقتاً متوقف شدهاند';
@@ -3933,6 +3951,282 @@ class AppLocalizationsFa extends AppLocalizations {
String get exportInvalidCss =>
'یک فایل .css خوانا با UTF-8 تا ۲۵۶ KiB انتخاب کنید، بدون واردسازی، نشانی منابع، HTML یا CSS اجرایی یا دارای نویسهٔ گریز.';
+ @override
+ String get clipboardHistory => 'تاریخچه کپی و پیست';
+
+ @override
+ String get clipboardHistorySearchHint => 'جستجو در تاریخچه کلیپبورد';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'باز کردن تاریخچه کپی و پیست مخصوص جلسه.';
+
+ @override
+ String get clipboardCurrent => 'کپی و پیست فعلی';
+
+ @override
+ String get clipboardCurrentExternal => 'کپی و پیست از برنامه دیگر';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'کپی و پیست در $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'فقط برای این جلسه BusyMark ذخیره می شود.';
+
+ @override
+ String get clipboardNoItems => 'تاریخچه کپی و پیست خالی است.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'هیچ آیتم کپی و پیستی با این جستجو مطابقت ندارد.';
+
+ @override
+ String get clipboardDisabled =>
+ 'مجموعه تاریخچه کپی و پیست در تنظیمات غیرفعال است.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'محتوای کپی و پیست فعلی در دسترس نیست یا پشتیبانی نمی شود.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'این آیتم کپی شده خیلی بزرگ است و نمیتوان آن را نگه داشت.';
+
+ @override
+ String get clipboardPastePlainText => 'به عنوان متن ساده کپی کنید';
+
+ @override
+ String get clipboardClearAll => 'تاریخچه کپی را پاک کنید';
+
+ @override
+ String get clipboardEntryText => 'متن';
+
+ @override
+ String get clipboardEntryRichText => 'متن غنی';
+
+ @override
+ String get clipboardEntryImage => 'تصویر';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'کپی شده از $name';
+ }
+
+ @override
+ String get localHistory => 'تاریخچه محلی';
+
+ @override
+ String get localHistoryEllipsis => 'تاریخچه محلی…';
+
+ @override
+ String get localHistoryDescription =>
+ 'نسخههای ذخیره شده و خودکار محلی را برای سند فعلی نمایش دهید.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'در تاریخ محلی جستجو کنید...';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'اسناد و محتوای نسخههای تاریخ محلی را جستجو کنید.';
+
+ @override
+ String get localHistoryNoDocuments => 'هنوز هیچ سند دارای تاریخ محلی نیست.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'این سند دارای هیچ نسخهای از تاریخ محلی نیست.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'هیچ نسخهای از تاریخ محلی با این جستجو مطابقت ندارد.';
+
+ @override
+ String get localHistorySearchHint => 'جستجو در تاریخچه محلی';
+
+ @override
+ String get localHistorySelectedRevision => 'نسخه انتخاب شده';
+
+ @override
+ String get localHistoryCurrentEditor => 'محتوای فعلی ویرایشگر';
+
+ @override
+ String get localHistoryCurrentDisk => 'محتوای فعلی روی دیسک';
+
+ @override
+ String get localHistoryMissingFile => 'فایل اصلی وجود ندارد.';
+
+ @override
+ String get localHistoryRestoreRevision => 'بازگرداندن نسخه';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => 'بازگرداندن به محل اصلی';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'بازگرداندن به محل جدید…';
+
+ @override
+ String get localHistoryRestoreChange => 'بازگرداندن تغییر';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'این تغییر قابل بازگشت نیست زیرا مقایسه دیگر معتبر نیست.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'این مقایسه بزرگ ساده شده است. بازگرداندن تغییرات فردی امکان پذیر نیست.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'تغییر در نسخه قدیمی: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'تغییر در محتوا فعلی: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'حذف شده';
+
+ @override
+ String get localHistoryUntitled => 'نامگذاری نشده';
+
+ @override
+ String get localHistoryClearDocument => 'پاک کردن تاریخچه این سند';
+
+ @override
+ String get localHistoryClearAll => 'پاک کردن تمام تاریخچه محلی';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'پاک کردن تاریخچه سند؟';
+
+ @override
+ String get localHistoryClearAllTitle => 'پاک کردن تمام تاریخچه محلی؟';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'این به طور دائمی تغییرات ذخیره شده را حذف می کند. این عمل قابل لغو نیست.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'ایندکس تاریخچه محلی آسیب دیده و از تغییرات سالم بازسازی شده است.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'این فروشگاه تاریخچه محلی از فرمت پشتیبانی نشده $version استفاده می کند.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'تاریخچه محلی در دسترس نیست: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'ضبط غیرفعال است یا این مسیر حذف شده است. قبل از بازیابی، ضبط را دوباره فعال کنید.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'تاریخچه محلی نمی تواند تغییر مسیر را ثبت کند: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'تاریخ محلی نمیتواند مسیر حذف شده را حفظ کند: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'نسخه انتخاب شده وجود ندارد یا آسیب دیده است.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'نمیتوان نسخه انتخاب شده را خواند: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'تاریخ محلی نمیتواند این نسخه را ثبت کند: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason، $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'خط مبنا';
+
+ @override
+ String get localHistoryReasonSaved => 'ذخیره شده';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'نقطه تیک کنترل خودکار';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'قبل از بارگذاری مجدد';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'قبل از حذف';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'قبل از بازیابی';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'قبل از حذف';
+
+ @override
+ String get localHistoryReasonExternalChange => 'تغییرات خارجی';
+
+ @override
+ String get settingsHistory => 'تاریخچه';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'جمع آوری تاریخچه کپی شده';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'محتوای کپی شده و موفق به پیوستن را فقط برای این جلسه BusyMark نگه دارید.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'ثبت تاریخچه محلی';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'ذخیره ویرایش های سند در این دستگاه برای بازیابی و مقایسه.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'فاصله زمانی خودکار';
+
+ @override
+ String get settingsHistoryRetention => 'دوره نگهداری';
+
+ @override
+ String get settingsHistoryStorage => 'حداکثر فضای ذخیره سازی';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'مسیرهای حذف شده';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'یک مسیر مطلق در هر خط';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value ثانیه';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value روز';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value مگابایت';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: عددی بین $minimum و $maximum وارد کنید.';
diff --git a/lib/l10n/generated/app_localizations_fr.dart b/lib/l10n/generated/app_localizations_fr.dart
index 2b210940..0a349c0a 100644
--- a/lib/l10n/generated/app_localizations_fr.dart
+++ b/lib/l10n/generated/app_localizations_fr.dart
@@ -952,6 +952,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get hardLineBreak => 'Saut de ligne forcé';
+ @override
+ String get insertBlankLine => 'Insérer une ligne vide';
+
@override
String get textStyle => 'Style de texte';
@@ -1135,7 +1138,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get files => 'Fichiers';
@override
- String get toc => 'Table des matières';
+ String get toc => 'Sujets';
@override
String get tocActions => 'Actions sur la table des matières';
@@ -1577,6 +1580,21 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Expression régulière non valide';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Les correspondances de longueur nulle ne sont pas prises en charge (par exemple ^, \$ ou les assertions qui ne correspondent qu’à une position).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Seule une partie des résultats est affichée. Affichez-en davantage ou affinez la recherche.';
+
+ @override
+ String get workspaceSearchShowMore => 'Afficher plus de résultats';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'La recherche est incomplète. Ces fichiers dépassent 1 MiB ou n’ont pas pu être lus :';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Fichier volumineux : la coloration et le repliage sont suspendus';
@@ -3922,6 +3940,292 @@ class AppLocalizationsFr extends AppLocalizations {
String get exportInvalidCss =>
'Choisissez un fichier .css UTF-8 lisible de 256 KiB maximum, sans imports, URL de ressources, HTML ni CSS exécutable ou échappé.';
+ @override
+ String get clipboardHistory => 'Historique de la presse-papiers';
+
+ @override
+ String get clipboardHistorySearchHint =>
+ 'Rechercher dans l’historique du presse-papiers';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Ouvrir l\'historique de la presse-papiers local à la session.';
+
+ @override
+ String get clipboardCurrent => 'Presse-papiers actuel';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Presse-papiers actuel provenant d\'une autre application';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Coller dans $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Stocké uniquement pour cette session BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'L\'historique de la presse-papiers est vide.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Aucun élément de presse-papiers ne correspond à cette recherche.';
+
+ @override
+ String get clipboardDisabled =>
+ 'La collection d\'Historique de la presse-papiers est désactivée dans les paramètres.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Le contenu actuel du presse-papiers n\'est pas disponible ou non pris en charge.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Cet élément du presse-papiers est trop grand pour être conservé.';
+
+ @override
+ String get clipboardPastePlainText => 'Coller en texte brut';
+
+ @override
+ String get clipboardClearAll => 'Effacer l\'historique du presse-papiers';
+
+ @override
+ String get clipboardEntryText => 'Texte';
+
+ @override
+ String get clipboardEntryRichText => 'Texte enrichi';
+
+ @override
+ String get clipboardEntryImage => 'Image du presse-papiers';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Copié depuis $name';
+ }
+
+ @override
+ String get localHistory => 'Historique local';
+
+ @override
+ String get localHistoryEllipsis => 'Historique local…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Afficher les révisions locales enregistrées et automatiques pour le document actuel.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Rechercher dans l\'Historique Local…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Rechercher les documents et le contenu des révisions dans l\'Historique Local.';
+
+ @override
+ String get localHistoryNoDocuments => 'Aucun document n\'a Historique Local.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Ce document n\'a pas de révisions dans l\'Historique Local.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Aucune révision n\'est trouvée dans cette recherche.';
+
+ @override
+ String get localHistorySearchHint => 'Rechercher dans l’historique local';
+
+ @override
+ String get localHistorySelectedRevision => 'Révision sélectionnée';
+
+ @override
+ String get localHistoryCurrentEditor => 'Contenu de l\'éditeur actuel';
+
+ @override
+ String get localHistoryCurrentDisk => 'Contenu actuel sur le disque';
+
+ @override
+ String get localHistoryMissingFile => 'Le fichier original est manquant.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Restaurer la version';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Restaurer à l\'emplacement original';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Restaurer à un nouvel emplacement…';
+
+ @override
+ String get localHistoryRestoreChange => 'Restaurer la modification';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Cette modification ne peut pas être restaurée car la comparaison n\'est plus à jour.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Cette comparaison importante est simplifiée. Restaurer des modifications individuelles n\'est pas possible.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Modification de la version plus ancienne: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Modification du contenu actuel: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Supprimé';
+
+ @override
+ String get localHistoryUntitled => 'Sans titre';
+
+ @override
+ String get localHistoryClearDocument =>
+ 'Effacer l\'historique de ce document';
+
+ @override
+ String get localHistoryClearAll => 'Effacer l\'historique local';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Effacer l\'historique du document ?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Effacer l\'historique local ?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Cela supprime définitivement les révisions stockées. Cette action ne peut pas être annulée.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'L\'index de l\'historique local a été endommagé et reconstruit à partir de révisions intactes.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Ce magasin d\'historique local utilise un format non pris en charge $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'L\'historique local n\'est pas disponible : $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'L\'enregistrement est désactivé ou ce chemin est exclu. Réactivez l\'enregistrement avant de restaurer.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'L\'historique local n\'a pas pu enregistrer le changement de chemin : $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'L\'historique local n\'a pas pu conserver le chemin supprimé: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'La révision sélectionnée est manquante ou corrompue.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'La révision sélectionnée ne peut pas être lue: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'L\'historique local n\'a pas pu capturer cette révision: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Base';
+
+ @override
+ String get localHistoryReasonSaved => 'Sauvegardé';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Point de contrôle automatique';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Avant rechargement';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Avant suppression';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Avant la restauration';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Avant la suppression';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Changement externe';
+
+ @override
+ String get settingsHistory => 'Historique';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Collecter l\'historique du presse-papier';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Conserver le contenu copié et correctement collé uniquement pour cette session BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Enregistrer l\'historique local';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Stocker les révisions du document sur cet appareil pour la récupération et la comparaison.';
+
+ @override
+ String get settingsHistoryCheckpoint =>
+ 'Intervalle de sauvegarde automatique';
+
+ @override
+ String get settingsHistoryRetention => 'Période de conservation';
+
+ @override
+ String get settingsHistoryStorage => 'Stockage maximal';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Chemins exclus';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Un chemin absolu par ligne';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value secondes';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value jours';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field : saisissez un nombre entre $minimum et $maximum.';
diff --git a/lib/l10n/generated/app_localizations_hi.dart b/lib/l10n/generated/app_localizations_hi.dart
index 8c532cfc..8a41f375 100644
--- a/lib/l10n/generated/app_localizations_hi.dart
+++ b/lib/l10n/generated/app_localizations_hi.dart
@@ -935,6 +935,9 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get hardLineBreak => 'हार्ड लाइन ब्रेक';
+ @override
+ String get insertBlankLine => 'खाली पंक्ति डालें';
+
@override
String get textStyle => 'टेक्स्ट शैली';
@@ -1117,7 +1120,7 @@ class AppLocalizationsHi extends AppLocalizations {
String get files => 'फ़ाइलें';
@override
- String get toc => 'TOC';
+ String get toc => 'विषय';
@override
String get tocActions => 'विषय-सूची संबंधी कार्रवाइयाँ';
@@ -1551,6 +1554,21 @@ class AppLocalizationsHi extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'अमान्य रेगुलर एक्सप्रेशन';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'शून्य लंबाई वाले रेगेक्स मिलान समर्थित नहीं हैं (जैसे ^, \$ या केवल स्थिति से मेल खाने वाले लुकअहेड)।';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'केवल कुछ परिणाम दिखाए गए हैं। अधिक परिणाम देखें या खोज को सीमित करें।';
+
+ @override
+ String get workspaceSearchShowMore => 'अधिक परिणाम दिखाएँ';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'खोज अधूरी है। ये फ़ाइलें 1 MiB से बड़ी हैं या पढ़ी नहीं जा सकीं:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'बड़ी फ़ाइल: हाइलाइटिंग और फ़ोल्डिंग अस्थायी रूप से रुकी हुई हैं';
@@ -3881,6 +3899,285 @@ class AppLocalizationsHi extends AppLocalizations {
String get exportInvalidCss =>
'256 KiB तक की पढ़ने योग्य UTF-8 .css फ़ाइल चुनें, जिसमें इंपोर्ट, संसाधन URL, HTML और निष्पादन योग्य या एस्केप वाला CSS न हो।';
+ @override
+ String get clipboardHistory => 'क्लिपबोर्ड इतिहास';
+
+ @override
+ String get clipboardHistorySearchHint => 'क्लिपबोर्ड इतिहास खोजें';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'सत्र-विशिष्ट क्लिपबोर्ड इतिहास खोलें।';
+
+ @override
+ String get clipboardCurrent => 'वर्तमान क्लिपबोर्ड';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'किसी अन्य एप्लिकेशन से वर्तमान क्लिपबोर्ड';
+
+ @override
+ String clipboardDestination(String name) {
+ return '$name में पेस्ट करें';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'केवल इस BusyMark सत्र के लिए संग्रहीत।';
+
+ @override
+ String get clipboardNoItems => 'क्लिपबोर्ड इतिहास खाली है।';
+
+ @override
+ String get clipboardNoMatches =>
+ 'कोई क्लिपबोर्ड आइटम इस खोज से मेल नहीं खाता।';
+
+ @override
+ String get clipboardDisabled =>
+ 'क्लिपबोर्ड इतिहास संग्रह सेटिंग्स में अक्षम है।';
+
+ @override
+ String get clipboardUnavailable =>
+ 'वर्तमान क्लिपबोर्ड सामग्री अनुपलब्ध या समर्थित नहीं है।';
+
+ @override
+ String get clipboardTooLarge =>
+ 'यह क्लिपबोर्ड आइटम बहुत बड़ा है और इसे बनाए रखना संभव नहीं है।';
+
+ @override
+ String get clipboardPastePlainText => 'सादे टेक्स्ट के रूप में पेस्ट करें';
+
+ @override
+ String get clipboardClearAll => 'क्लिपबोर्ड इतिहास साफ़ करें';
+
+ @override
+ String get clipboardEntryText => 'टेक्स्ट';
+
+ @override
+ String get clipboardEntryRichText => 'रिच टेक्स्ट';
+
+ @override
+ String get clipboardEntryImage => 'इमेज';
+
+ @override
+ String clipboardOrigin(String name) {
+ return '$name से कॉपी किया गया';
+ }
+
+ @override
+ String get localHistory => 'स्थानीय इतिहास';
+
+ @override
+ String get localHistoryEllipsis => 'स्थानीय इतिहास…';
+
+ @override
+ String get localHistoryDescription =>
+ 'वर्तमान दस्तावेज़ के लिए सहेजे गए और स्वचालित स्थानीय संशोधनों को दिखाएं।';
+
+ @override
+ String get findLocalHistoryEllipsis => 'स्थानीय इतिहास में खोजें...';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'स्थानीय इतिहास में दस्तावेज़ और संशोधन सामग्री खोजें।';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'अभी तक किसी भी दस्तावेज़ में स्थानीय इतिहास नहीं है।';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'इस दस्तावेज़ में स्थानीय इतिहास में कोई संशोधन नहीं है।';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'यह खोज स्थानीय इतिहास संशोधनों से मेल नहीं खाती।';
+
+ @override
+ String get localHistorySearchHint => 'स्थानीय इतिहास खोजें';
+
+ @override
+ String get localHistorySelectedRevision => 'चयनित संशोधन';
+
+ @override
+ String get localHistoryCurrentEditor => 'वर्तमान संपादक सामग्री';
+
+ @override
+ String get localHistoryCurrentDisk => 'डिस्क पर वर्तमान सामग्री';
+
+ @override
+ String get localHistoryMissingFile => 'मूल फ़ाइल गायब है।';
+
+ @override
+ String get localHistoryRestoreRevision => 'पिछली स्थिति को पुनर्स्थापित करें';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'मूल स्थान पर पुनर्स्थापित करें';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'नए स्थान पर पुनर्स्थापित करें…';
+
+ @override
+ String get localHistoryRestoreChange => 'परिवर्तन को पुनर्स्थापित करें';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'इस परिवर्तन को पुनर्स्थापित नहीं किया जा सकता क्योंकि तुलना अब अद्यतित नहीं है।';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'यह बड़ा परिवर्तन सरलीकृत है। व्यक्तिगत परिवर्तनों को पुनर्स्थापित करना संभव नहीं है।';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'पुराने संस्करण में परिवर्तन: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'वर्तमान सामग्री में परिवर्तन: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'हटा दिया गया';
+
+ @override
+ String get localHistoryUntitled => 'शीर्षकहीन';
+
+ @override
+ String get localHistoryClearDocument => 'इस दस्तावेज़ का इतिहास साफ़ करें';
+
+ @override
+ String get localHistoryClearAll => 'सभी स्थानीय इतिहास साफ़ करें';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'दस्तावेज़ का इतिहास साफ़ करना?';
+
+ @override
+ String get localHistoryClearAllTitle => 'सभी स्थानीय इतिहास साफ़ करना?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'यह स्थायी रूप से संग्रहीत संशोधनों को हटा देता है। इस कार्रवाई को पूर्ववत नहीं किया जा सकता।';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'स्थानीय इतिहास इंडेक्स क्षतिग्रस्त था और बरकरार संशोधनों से फिर से बनाया गया था।';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'यह स्थानीय इतिहास स्टोर समर्थित प्रारूप $version का उपयोग करता है।';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'स्थानीय इतिहास उपलब्ध नहीं: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'रिकॉर्डिंग अक्षम है या यह पथ बाहर रखा गया है। पुनर्स्थापित करने से पहले रिकॉर्डिंग को फिर से सक्षम करें।';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'स्थानीय इतिहास ने पथ परिवर्तन रिकॉर्ड नहीं किया: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'स्थानीय इतिहास को हटाए गए पथ को सहेजने में असमर्थ: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'चयनित संशोधन गायब है या क्षतिग्रस्त है।';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'चयनित संशोधन को नहीं पढ़ा जा सकता: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'स्थानीय इतिहास इस संशोधन को कैप्चर नहीं कर सका: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'बेसलाइन';
+
+ @override
+ String get localHistoryReasonSaved => 'सहेजा गया';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'स्वचालित चेकपॉइंट';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'रीलोड करने से पहले';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'त्यागने से पहले';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'पुनर्स्थापना से पहले';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'हटाने से पहले';
+
+ @override
+ String get localHistoryReasonExternalChange => 'बाहरी परिवर्तन';
+
+ @override
+ String get settingsHistory => 'इतिहास';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'क्लिपबोर्ड इतिहास एकत्र करें';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'केवल इस BusyMark सत्र के लिए कॉपी किए गए और सफलतापूर्वक पेस्ट किए गए सामग्री को बनाए रखें।';
+
+ @override
+ String get settingsLocalHistoryTitle => 'स्थानीय इतिहास रिकॉर्ड करें';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'पुनर्प्राप्ति और तुलना के लिए इस डिवाइस पर दस्तावेज़ के संस्करणों को सहेजें।';
+
+ @override
+ String get settingsHistoryCheckpoint => 'स्वचालित चेकपॉइंट अंतराल';
+
+ @override
+ String get settingsHistoryRetention => 'प्रतिधारण आयु';
+
+ @override
+ String get settingsHistoryStorage => 'अधिकतम संग्रहण';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'बाहर किए गए पथ';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'प्रति पंक्ति एक पूर्ण पथ';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value सेकंड';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value दिन';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: $minimum से $maximum के बीच संख्या दर्ज करें।';
diff --git a/lib/l10n/generated/app_localizations_id.dart b/lib/l10n/generated/app_localizations_id.dart
index 95ac534c..926dbe68 100644
--- a/lib/l10n/generated/app_localizations_id.dart
+++ b/lib/l10n/generated/app_localizations_id.dart
@@ -941,6 +941,9 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get hardLineBreak => 'Pemisah baris keras';
+ @override
+ String get insertBlankLine => 'Sisipkan baris kosong';
+
@override
String get textStyle => 'Gaya teks';
@@ -1124,7 +1127,7 @@ class AppLocalizationsId extends AppLocalizations {
String get files => 'File';
@override
- String get toc => 'Daftar isi';
+ String get toc => 'Topik';
@override
String get tocActions => 'Tindakan TOC';
@@ -1557,6 +1560,21 @@ class AppLocalizationsId extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Ekspresi reguler tidak valid';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Kecocokan regex dengan panjang nol tidak didukung (misalnya ^, \$, atau lookahead yang hanya cocok dengan posisi).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Hanya sebagian hasil yang ditampilkan. Tampilkan lebih banyak atau persempit pencarian.';
+
+ @override
+ String get workspaceSearchShowMore => 'Tampilkan lebih banyak hasil';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Pencarian belum lengkap. Berkas ini melebihi 1 MiB atau tidak dapat dibaca:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'File besar: penyorotan dan pelipatan dijeda';
@@ -3896,6 +3914,282 @@ class AppLocalizationsId extends AppLocalizations {
String get exportInvalidCss =>
'Pilih berkas .css UTF-8 yang dapat dibaca hingga 256 KiB, tanpa impor, URL sumber daya, HTML, atau CSS yang dapat dieksekusi atau memakai escape.';
+ @override
+ String get clipboardHistory => 'Sejarah Tempel';
+
+ @override
+ String get clipboardHistorySearchHint => 'Cari riwayat papan klip';
+
+ @override
+ String get clipboardHistoryDescription => 'Buka riwayat tempel lokal sesi.';
+
+ @override
+ String get clipboardCurrent => 'Tempel saat ini';
+
+ @override
+ String get clipboardCurrentExternal => 'Tempel saat ini dari aplikasi lain';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Tempel ke $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'Hanya disimpan untuk sesi BusyMark ini.';
+
+ @override
+ String get clipboardNoItems => 'Riwayat tempel kosong.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Tidak ada item tempel yang cocok dengan pencarian ini.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Koleksi Riwayat Tempel dinonaktifkan di Pengaturan.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Konten tempel saat ini tidak tersedia atau tidak didukung.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Item clipboard ini terlalu besar untuk disimpan.';
+
+ @override
+ String get clipboardPastePlainText => 'Tempel sebagai Teks Biasa';
+
+ @override
+ String get clipboardClearAll => 'Hapus Riwayat Clipboard';
+
+ @override
+ String get clipboardEntryText => 'Teks';
+
+ @override
+ String get clipboardEntryRichText => 'Teks kaya';
+
+ @override
+ String get clipboardEntryImage => 'Gambar';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Disalin dari $name';
+ }
+
+ @override
+ String get localHistory => 'Riwayat Lokal';
+
+ @override
+ String get localHistoryEllipsis => 'Riwayat Lokal…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Tampilkan revisi lokal yang disimpan dan otomatis untuk dokumen saat ini.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Cari di Sejarah Lokal…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Cari dokumen dan isi revisi dalam Sejarah Lokal.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Tidak ada dokumen yang memiliki Sejarah Lokal.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Dokumen ini tidak memiliki revisi Sejarah Lokal.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Tidak ada revisi Sejarah Lokal yang cocok dengan pencarian ini.';
+
+ @override
+ String get localHistorySearchHint => 'Cari riwayat lokal';
+
+ @override
+ String get localHistorySelectedRevision => 'Revisi yang dipilih';
+
+ @override
+ String get localHistoryCurrentEditor => 'Konten editor saat ini';
+
+ @override
+ String get localHistoryCurrentDisk => 'Konten saat ini di disk';
+
+ @override
+ String get localHistoryMissingFile => 'File asli hilang.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Kembalikan Revisi';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => 'Kembalikan ke Lokasi Asli';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Kembalikan ke Lokasi Baru…';
+
+ @override
+ String get localHistoryRestoreChange => 'Kembalikan Perubahan';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Perubahan ini tidak dapat dikembalikan karena perbandingan tidak lagi berlaku.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Perbandingan besar ini disederhanakan. Kembalikan perubahan individual tidak tersedia.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Perubahan revisi yang lebih lama: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Perubahan konten saat ini: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Dihapus';
+
+ @override
+ String get localHistoryUntitled => 'Tanpa Judul';
+
+ @override
+ String get localHistoryClearDocument => 'Hapus Riwayat Dokumen Ini';
+
+ @override
+ String get localHistoryClearAll => 'Hapus Riwayat Lokal Seluruhnya';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'Hapus riwayat dokumen?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Hapus riwayat lokal seluruhnya?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Ini secara permanen menghapus revisi yang tersimpan. Tindakan ini tidak dapat dibatalkan.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Indeks Riwayat Lokal rusak dan dibangun kembali dari revisi yang utuh.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Penyimpanan Riwayat Lokal menggunakan format yang tidak didukung $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Riwayat Lokal tidak tersedia: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Perekaman dinonaktifkan atau jalur ini dikecualikan. Aktifkan kembali perekaman sebelum memulihkan.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Riwayat Lokal tidak dapat mencatat perubahan jalur: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Sejarah Lokal tidak dapat menyimpan jalur yang dihapus: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Revisi yang dipilih hilang atau rusak.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Revisi yang dipilih tidak dapat dibaca: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Sejarah Lokal tidak dapat menangkap revisi ini: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Versi dasar';
+
+ @override
+ String get localHistoryReasonSaved => 'Tersimpan';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Checkpoint otomatis';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Sebelum memuat ulang';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Sebelum membuang';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Sebelum pemulihan';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Sebelum menghapus';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Perubahan eksternal';
+
+ @override
+ String get settingsHistory => 'Riwayat';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Kumpulkan Riwayat Klip';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Simpan konten yang disalin dan berhasil ditempel hanya untuk sesi BusyMark ini.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Rekam Riwayat Lokal';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Simpan revisi dokumen pada perangkat ini untuk pemulihan dan perbandingan.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Interval checkpoint otomatis';
+
+ @override
+ String get settingsHistoryRetention => 'Masa simpan';
+
+ @override
+ String get settingsHistoryStorage => 'Penyimpanan maksimum';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Jalur yang dikecualikan';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Satu jalur absolut per baris';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value detik';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value hari';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: masukkan angka antara $minimum dan $maximum.';
diff --git a/lib/l10n/generated/app_localizations_it.dart b/lib/l10n/generated/app_localizations_it.dart
index dad2fc9e..a48c6de5 100644
--- a/lib/l10n/generated/app_localizations_it.dart
+++ b/lib/l10n/generated/app_localizations_it.dart
@@ -950,6 +950,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get hardLineBreak => 'Interruzione di riga forzata';
+ @override
+ String get insertBlankLine => 'Inserisci riga vuota';
+
@override
String get textStyle => 'Stile del testo';
@@ -1132,7 +1135,7 @@ class AppLocalizationsIt extends AppLocalizations {
String get files => 'File';
@override
- String get toc => 'TOC';
+ String get toc => 'Argomenti';
@override
String get tocActions => 'Azioni dell\'indice';
@@ -1571,6 +1574,21 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Espressione regolare non valida';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Le corrispondenze regex di lunghezza zero non sono supportate (ad esempio ^, \$ o lookahead che corrispondono solo a una posizione).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'È mostrata solo una parte dei risultati. Mostrane altri o restringi la ricerca.';
+
+ @override
+ String get workspaceSearchShowMore => 'Mostra altri risultati';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'La ricerca è incompleta. Questi file superano 1 MiB o non è stato possibile leggerli:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'File di grandi dimensioni: evidenziazione e ripiegamento sono sospesi';
@@ -3918,6 +3936,293 @@ class AppLocalizationsIt extends AppLocalizations {
String get exportInvalidCss =>
'Scegli un file .css UTF-8 leggibile fino a 256 KiB, senza importazioni, URL di risorse, HTML o CSS eseguibile o con escape.';
+ @override
+ String get clipboardHistory => 'Cronologia della Copia';
+
+ @override
+ String get clipboardHistorySearchHint =>
+ 'Cerca nella cronologia degli appunti';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Apri la cronologia della copia locale della sessione.';
+
+ @override
+ String get clipboardCurrent => 'Copia corrente';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Copia corrente da un\'altra applicazione';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Incolla in $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Memorizzata solo per questa sessione BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'La cronologia della copia è vuota.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Nessun elemento della copia corrisponde a questa ricerca.';
+
+ @override
+ String get clipboardDisabled =>
+ 'La collezione Cronologia della Copia è disabilitata nelle Impostazioni.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Il contenuto della copia corrente non è disponibile o non è supportato.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Questo elemento del segnalibro è troppo grande per essere conservato.';
+
+ @override
+ String get clipboardPastePlainText => 'Incolla come testo semplice';
+
+ @override
+ String get clipboardClearAll => 'Cancella la cronologia del segnalibro';
+
+ @override
+ String get clipboardEntryText => 'Testo';
+
+ @override
+ String get clipboardEntryRichText => 'Testo ricco';
+
+ @override
+ String get clipboardEntryImage => 'Immagine';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Copiato da $name';
+ }
+
+ @override
+ String get localHistory => 'Cronologia locale';
+
+ @override
+ String get localHistoryEllipsis => 'Cronologia locale…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Mostra le revisioni locali salvate e automatiche per il documento corrente.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Trova nella cronologia locale…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Cerca documenti e contenuti delle revisioni nella cronologia locale.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Nessun documento ha ancora una cronologia locale.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Questo documento non ha revisioni nella cronologia locale.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Nessuna revisione corrisponde a questa ricerca.';
+
+ @override
+ String get localHistorySearchHint => 'Cerca nella cronologia locale';
+
+ @override
+ String get localHistorySelectedRevision => 'Revisione selezionata';
+
+ @override
+ String get localHistoryCurrentEditor => 'Contenuto dell\'editor corrente';
+
+ @override
+ String get localHistoryCurrentDisk => 'Contenuto corrente sul disco';
+
+ @override
+ String get localHistoryMissingFile => 'Il file originale è mancante.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Ripristina Versione';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Ripristina alla Posizione Originale';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Ripristina alla Nuova Posizione…';
+
+ @override
+ String get localHistoryRestoreChange => 'Ripristina Modifica';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Questa modifica non può essere ripristinata perché il confronto non è più valido.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Questo confronto ampio è semplificato. Il ripristino di singole modifiche non è disponibile.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Modifica di una versione precedente: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Modifica del contenuto corrente: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Eliminato';
+
+ @override
+ String get localHistoryUntitled => 'Senza Titolo';
+
+ @override
+ String get localHistoryClearDocument =>
+ 'Cancella la cronologia di questo documento';
+
+ @override
+ String get localHistoryClearAll => 'Cancella tutta la cronologia locale';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Cancella la cronologia del documento?';
+
+ @override
+ String get localHistoryClearAllTitle =>
+ 'Cancella tutta la cronologia locale?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Questo rimuove permanentemente le revisioni memorizzate. Questa azione non può essere annullata.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'L\'indice della cronologia locale è stato danneggiato e ricostruito da revisioni intatte.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Questo archivio della cronologia locale utilizza un formato non supportato $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'La cronologia locale non è disponibile: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'La registrazione è disabilitata o questo percorso è escluso. Riattiva la registrazione prima di ripristinare.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'La cronologia locale non è riuscita a registrare la modifica del percorso: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'La cronologia locale non è riuscita a conservare il percorso eliminato: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'La revisione selezionata è mancante o danneggiata.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'La revisione selezionata non può essere letta: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'La cronologia locale non è riuscita a catturare questa revisione: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Versione iniziale';
+
+ @override
+ String get localHistoryReasonSaved => 'Salvato';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Controllo automatico';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Prima del ricaricamento';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Prima di eliminare';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Prima del ripristino';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Prima della cancellazione';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Modifica esterna';
+
+ @override
+ String get settingsHistory => 'Cronologia';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Raccogli la cronologia della clipboard';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Mantieni il contenuto copiato e incollato per questa sessione BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Registra la cronologia locale';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Salva le revisioni del documento su questo dispositivo per il recupero e il confronto.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Intervallo di checkpoint automatico';
+
+ @override
+ String get settingsHistoryRetention => 'Periodo di conservazione';
+
+ @override
+ String get settingsHistoryStorage => 'Massimo spazio di archiviazione';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Percorsi esclusi';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Un percorso assoluto per riga';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value secondi';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value giorni';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: inserisci un numero tra $minimum e $maximum.';
diff --git a/lib/l10n/generated/app_localizations_ja.dart b/lib/l10n/generated/app_localizations_ja.dart
index ec9de0d9..b7d5094e 100644
--- a/lib/l10n/generated/app_localizations_ja.dart
+++ b/lib/l10n/generated/app_localizations_ja.dart
@@ -903,6 +903,9 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get hardLineBreak => '強制改行';
+ @override
+ String get insertBlankLine => '空行を挿入';
+
@override
String get textStyle => 'テキストスタイル';
@@ -1084,7 +1087,7 @@ class AppLocalizationsJa extends AppLocalizations {
String get files => 'ファイル';
@override
- String get toc => '目次';
+ String get toc => 'トピック';
@override
String get tocActions => '目次の操作';
@@ -1507,6 +1510,21 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => '無効な正規表現';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ '長さがゼロの正規表現の一致には対応していません(^、\$、位置のみに一致する先読みなど)。';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ '一部の結果のみ表示しています。さらに表示するか、検索を絞り込んでください。';
+
+ @override
+ String get workspaceSearchShowMore => 'さらに結果を表示';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ '検索は未完了です。次のファイルは 1 MiB を超えているか、読み取れませんでした:';
+
@override
String get sourceLargeFileFeaturesPaused => '大きなファイル:ハイライトと折りたたみを一時停止しています';
@@ -3786,6 +3804,271 @@ class AppLocalizationsJa extends AppLocalizations {
String get exportInvalidCss =>
'読み取り可能な UTF-8 .css ファイル(最大 256 KiB)を選択してください。インポート、リソース URL、HTML、実行可能な CSS、エスケープを含む CSS は使用できません。';
+ @override
+ String get clipboardHistory => 'コピーボード履歴';
+
+ @override
+ String get clipboardHistorySearchHint => 'クリップボード履歴を検索';
+
+ @override
+ String get clipboardHistoryDescription => 'セッション固有のコピーボード履歴を開く。';
+
+ @override
+ String get clipboardCurrent => '現在のコピーボード';
+
+ @override
+ String get clipboardCurrentExternal => '別のアプリケーションからの現在のコピーボード';
+
+ @override
+ String clipboardDestination(String name) {
+ return '$name に貼り付け';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'このBusyMarkセッションのみに保存されます。';
+
+ @override
+ String get clipboardNoItems => 'コピーボード履歴は空です。';
+
+ @override
+ String get clipboardNoMatches => 'この検索に一致するコピーボードの項目はありません。';
+
+ @override
+ String get clipboardDisabled => 'コピーボード履歴のコレクションは設定で無効になっています。';
+
+ @override
+ String get clipboardUnavailable => '現在のコピーボードの内容は利用できませんまたはサポートされていません。';
+
+ @override
+ String get clipboardTooLarge => 'このクリップボードの項目は大きすぎて保持できません。';
+
+ @override
+ String get clipboardPastePlainText => 'テキストとして貼り付け';
+
+ @override
+ String get clipboardClearAll => 'クリップボードの履歴をクリア';
+
+ @override
+ String get clipboardEntryText => 'テキスト';
+
+ @override
+ String get clipboardEntryRichText => '豊富なテキスト';
+
+ @override
+ String get clipboardEntryImage => '画像';
+
+ @override
+ String clipboardOrigin(String name) {
+ return '$name からコピー';
+ }
+
+ @override
+ String get localHistory => 'ローカル履歴';
+
+ @override
+ String get localHistoryEllipsis => 'ローカル履歴…';
+
+ @override
+ String get localHistoryDescription => '現在のドキュメントの保存されたおよび自動ローカルのバージョンを表示します。';
+
+ @override
+ String get findLocalHistoryEllipsis => 'ローカル履歴で検索…';
+
+ @override
+ String get findLocalHistoryDescription => 'ローカル履歴のドキュメントとリビジョンの内容を検索します。';
+
+ @override
+ String get localHistoryNoDocuments => 'まだローカル履歴を持つドキュメントはありません。';
+
+ @override
+ String get localHistoryNoRevisions => 'このドキュメントにはローカル履歴の修正はありません。';
+
+ @override
+ String get localHistoryNoMatches => 'この検索に一致するローカル履歴の修正はありません。';
+
+ @override
+ String get localHistorySearchHint => 'ローカル履歴を検索';
+
+ @override
+ String get localHistorySelectedRevision => '選択されたリビジョン';
+
+ @override
+ String get localHistoryCurrentEditor => '現在の編集内容';
+
+ @override
+ String get localHistoryCurrentDisk => 'ディスク上の現在の内容';
+
+ @override
+ String get localHistoryMissingFile => '元のファイルが見つかりません。';
+
+ @override
+ String get localHistoryRestoreRevision => '変更を復元';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => '元の位置に復元';
+
+ @override
+ String get localHistoryRestoreNewLocation => '新しい位置に復元…';
+
+ @override
+ String get localHistoryRestoreChange => '変更を復元';
+
+ @override
+ String get localHistoryRestoreUnavailable => 'この変更は復元できません。比較が最新ではありません。';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'この大きな比較は簡略化されています。個別の変更の復元はできません。';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return '以前の変更: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return '現在の変更: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => '削除';
+
+ @override
+ String get localHistoryUntitled => 'タイトルなし';
+
+ @override
+ String get localHistoryClearDocument => 'このドキュメントの履歴をクリアする';
+
+ @override
+ String get localHistoryClearAll => 'すべてのローカル履歴をクリアする';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'ドキュメントの履歴をクリアしますか?';
+
+ @override
+ String get localHistoryClearAllTitle => 'すべてのローカル履歴をクリアしますか?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'これにより、保存されたバージョンが完全に削除されます。この操作は取り消せません。';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'ローカル履歴のインデックスが破損し、正常なバージョンから再構築されました。';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'このローカル履歴ストアはサポートされていない形式 $version を使用しています。';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'ローカル履歴は利用できません: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ '録音が無効になっているか、このパスは除外されています。復元する前に録音を再度有効にしてください。';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'ローカル履歴がパスの変更を記録できませんでした: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'ローカル履歴は削除されたパスを保持できません: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing => '選択されたバージョンが存在しないか破損しています。';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return '選択されたバージョンを読み取ることができません: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'ローカル履歴は、このバージョンをキャプチャできません: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason・$timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'ベースライン';
+
+ @override
+ String get localHistoryReasonSaved => '保存';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => '自動チェックポイント';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'リロード前';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => '破棄前';
+
+ @override
+ String get localHistoryReasonBeforeRestore => '復元前';
+
+ @override
+ String get localHistoryReasonBeforeDelete => '削除前';
+
+ @override
+ String get localHistoryReasonExternalChange => '外部からの変更';
+
+ @override
+ String get settingsHistory => '履歴';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'コピー&ペースト履歴を収集';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'このBusyMarkセッションでのコピー&ペーストされたコンテンツのみを保持。';
+
+ @override
+ String get settingsLocalHistoryTitle => 'ローカル履歴を記録';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'このデバイスにドキュメントのバージョンを保存し、復元および比較。';
+
+ @override
+ String get settingsHistoryCheckpoint => '自動チェックポイント間隔';
+
+ @override
+ String get settingsHistoryRetention => '保管期間';
+
+ @override
+ String get settingsHistoryStorage => '最大ストレージ';
+
+ @override
+ String get settingsHistoryExcludedPaths => '除外されたパス';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => '1行に1つの絶対パス';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value 秒';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value 日';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: $minimum から $maximum の数値を入力してください。';
diff --git a/lib/l10n/generated/app_localizations_ko.dart b/lib/l10n/generated/app_localizations_ko.dart
index 89a028a0..5020fd71 100644
--- a/lib/l10n/generated/app_localizations_ko.dart
+++ b/lib/l10n/generated/app_localizations_ko.dart
@@ -900,6 +900,9 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get hardLineBreak => '강제 줄 바꿈';
+ @override
+ String get insertBlankLine => '빈 줄 삽입';
+
@override
String get textStyle => '텍스트 스타일';
@@ -1082,7 +1085,7 @@ class AppLocalizationsKo extends AppLocalizations {
String get files => '파일';
@override
- String get toc => '목차';
+ String get toc => '토픽';
@override
String get tocActions => '목차 작업';
@@ -1505,6 +1508,20 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => '잘못된 정규식';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ '길이가 0인 정규식 일치는 지원하지 않습니다(예: ^, \$, 위치에만 일치하는 전방 탐색).';
+
+ @override
+ String get workspaceSearchIncomplete => '일부 결과만 표시됩니다. 더 표시하거나 검색 범위를 좁히세요.';
+
+ @override
+ String get workspaceSearchShowMore => '결과 더 보기';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ '검색이 완료되지 않았습니다. 다음 파일은 1 MiB를 초과하거나 읽을 수 없습니다:';
+
@override
String get sourceLargeFileFeaturesPaused => '대용량 파일: 강조 표시 및 접기가 일시 중지됩니다.';
@@ -3782,6 +3799,271 @@ class AppLocalizationsKo extends AppLocalizations {
String get exportInvalidCss =>
'가져오기, 리소스 URL, HTML, 실행 가능하거나 이스케이프된 CSS가 없는 읽기 가능한 UTF-8 .css 파일(최대 256 KiB)을 선택하세요.';
+ @override
+ String get clipboardHistory => '클립보드 기록';
+
+ @override
+ String get clipboardHistorySearchHint => '클립보드 기록 검색';
+
+ @override
+ String get clipboardHistoryDescription => '세션별 클립보드 기록을 엽니다.';
+
+ @override
+ String get clipboardCurrent => '현재 클립보드';
+
+ @override
+ String get clipboardCurrentExternal => '다른 애플리케이션의 현재 클립보드';
+
+ @override
+ String clipboardDestination(String name) {
+ return '$name에 붙여넣기';
+ }
+
+ @override
+ String get clipboardSessionOnly => '이 BusyMark 세션에만 저장됩니다.';
+
+ @override
+ String get clipboardNoItems => '클립보드 기록이 비어 있습니다.';
+
+ @override
+ String get clipboardNoMatches => '이 검색과 일치하는 클립보드 항목이 없습니다.';
+
+ @override
+ String get clipboardDisabled => '클립보드 기록 컬렉션이 설정에서 비활성화되었습니다.';
+
+ @override
+ String get clipboardUnavailable => '현재 클립보드 내용은 사용할 수 없거나 지원되지 않습니다.';
+
+ @override
+ String get clipboardTooLarge => '이 클립보드 항목은 저장할 수 없을 정도로 큽니다.';
+
+ @override
+ String get clipboardPastePlainText => '일반 텍스트로 붙여넣기';
+
+ @override
+ String get clipboardClearAll => '클립보드 기록 지우기';
+
+ @override
+ String get clipboardEntryText => '텍스트';
+
+ @override
+ String get clipboardEntryRichText => '풍부한 텍스트';
+
+ @override
+ String get clipboardEntryImage => '이미지';
+
+ @override
+ String clipboardOrigin(String name) {
+ return '$name에서 복사됨';
+ }
+
+ @override
+ String get localHistory => '로컬 기록';
+
+ @override
+ String get localHistoryEllipsis => '로컬 기록…';
+
+ @override
+ String get localHistoryDescription => '현재 문서에 저장된 자동 로컬 변경 사항을 표시합니다.';
+
+ @override
+ String get findLocalHistoryEllipsis => '로컬 히스토리에서 찾기…';
+
+ @override
+ String get findLocalHistoryDescription => '로컬 히스토리 문서 및 수정 내용 검색.';
+
+ @override
+ String get localHistoryNoDocuments => '아직 로컬 히스토리가 있는 문서는 없습니다.';
+
+ @override
+ String get localHistoryNoRevisions => '이 문서는 로컬 히스토리 수정 사항이 없습니다.';
+
+ @override
+ String get localHistoryNoMatches => '이 검색과 일치하는 로컬 히스토리 수정 사항이 없습니다.';
+
+ @override
+ String get localHistorySearchHint => '로컬 기록 검색';
+
+ @override
+ String get localHistorySelectedRevision => '선택된 수정';
+
+ @override
+ String get localHistoryCurrentEditor => '현재 편집 내용';
+
+ @override
+ String get localHistoryCurrentDisk => '디스크에 있는 현재 내용';
+
+ @override
+ String get localHistoryMissingFile => '원본 파일이 없습니다.';
+
+ @override
+ String get localHistoryRestoreRevision => '이전 버전 복원';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => '원래 위치로 복원';
+
+ @override
+ String get localHistoryRestoreNewLocation => '새로운 위치로 복원…';
+
+ @override
+ String get localHistoryRestoreChange => '변경 사항 복원';
+
+ @override
+ String get localHistoryRestoreUnavailable => '이 변경 사항은 더 이상 비교가 불가능합니다.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ '이 큰 비교는 단순화되었습니다. 개별 변경 사항 복원은 불가능합니다.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return '이전 버전의 변경: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return '현재 콘텐츠의 변경: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => '삭제됨';
+
+ @override
+ String get localHistoryUntitled => '제목 없음';
+
+ @override
+ String get localHistoryClearDocument => '이 문서의 기록 삭제';
+
+ @override
+ String get localHistoryClearAll => '모든 로컬 기록 삭제';
+
+ @override
+ String get localHistoryClearDocumentTitle => '문서 기록 삭제?';
+
+ @override
+ String get localHistoryClearAllTitle => '모든 로컬 기록 삭제?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ '이로 인해 저장된 버전이 영구적으로 삭제됩니다. 이 작업은 취소할 수 없습니다.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ '로컬 기록 인덱스가 손상되어 정상적인 버전에서 재구성되었습니다.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return '이 로컬 기록 저장소는 지원되지 않는 형식 $version을 사용합니다.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return '로컬 기록은 사용할 수 없습니다: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ '녹음이 비활성화되었거나 이 경로가 제외되었습니다. 복원하기 전에 녹음을 다시 활성화하십시오.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return '로컬 기록이 경로 변경을 기록할 수 없습니다: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return '로컬 히스토리에서 삭제된 경로를 저장할 수 없습니다: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing => '선택된 버전이 누락되었거나 손상되었습니다.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return '선택된 버전을 읽을 수 없습니다: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return '로컬 히스토리가 이 버전을 캡처할 수 없습니다: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => '기본 버전';
+
+ @override
+ String get localHistoryReasonSaved => '저장됨';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => '자동 체크포인트';
+
+ @override
+ String get localHistoryReasonBeforeReload => '재로드 전';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => '버리기 전';
+
+ @override
+ String get localHistoryReasonBeforeRestore => '복원 전';
+
+ @override
+ String get localHistoryReasonBeforeDelete => '삭제 전';
+
+ @override
+ String get localHistoryReasonExternalChange => '외부 변경';
+
+ @override
+ String get settingsHistory => '버전 기록';
+
+ @override
+ String get settingsClipboardHistoryTitle => '클립보드 기록 수집';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ '이 BusyMark 세션에만 복사 및 성공적으로 붙여넣은 콘텐츠를 유지합니다.';
+
+ @override
+ String get settingsLocalHistoryTitle => '로컬 버전 기록';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ '복구 및 비교를 위해 이 장치에 문서 버전을 저장합니다.';
+
+ @override
+ String get settingsHistoryCheckpoint => '자동 체크포인트 간격';
+
+ @override
+ String get settingsHistoryRetention => '보관 기간';
+
+ @override
+ String get settingsHistoryStorage => '최대 저장 공간';
+
+ @override
+ String get settingsHistoryExcludedPaths => '제외된 경로';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => '한 줄에 하나의 절대 경로';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value 초';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value 일';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: $minimum에서 $maximum 사이의 숫자를 입력하세요.';
diff --git a/lib/l10n/generated/app_localizations_nb.dart b/lib/l10n/generated/app_localizations_nb.dart
index 406d4c42..05ccbee8 100644
--- a/lib/l10n/generated/app_localizations_nb.dart
+++ b/lib/l10n/generated/app_localizations_nb.dart
@@ -940,6 +940,9 @@ class AppLocalizationsNb extends AppLocalizations {
@override
String get hardLineBreak => 'Tvungent linjeskift';
+ @override
+ String get insertBlankLine => 'Sett inn tom linje';
+
@override
String get textStyle => 'Tekststil';
@@ -1123,7 +1126,7 @@ class AppLocalizationsNb extends AppLocalizations {
String get files => 'Filer';
@override
- String get toc => 'Innholdsfortegnelse';
+ String get toc => 'Emner';
@override
String get tocActions => 'Handlinger for innholdsfortegnelsen';
@@ -1558,6 +1561,21 @@ class AppLocalizationsNb extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Ugyldig regulært uttrykk';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Regex-treff med null lengde støttes ikke (for eksempel ^, \$ eller fremoversøk som bare samsvarer med en posisjon).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Bare noen av resultatene vises. Vis flere eller avgrens søket.';
+
+ @override
+ String get workspaceSearchShowMore => 'Vis flere resultater';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Søket er ufullstendig. Disse filene er større enn 1 MiB eller kunne ikke leses:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Stor fil: utheving og folding er satt på pause';
@@ -3888,6 +3906,286 @@ class AppLocalizationsNb extends AppLocalizations {
String get exportInvalidCss =>
'Velg en lesbar UTF-8 .css-fil opptil 256 KiB uten importer, ressurs-URL-er, HTML eller kjørbar eller escaped CSS.';
+ @override
+ String get clipboardHistory => 'Utklippshistorie';
+
+ @override
+ String get clipboardHistorySearchHint => 'Søk i utklippstavlehistorikken';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Åpne historien for utklippet som er lokal for sesjonen.';
+
+ @override
+ String get clipboardCurrent => 'Nåværende utklipp';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Nåværende utklipp fra en annen applikasjon';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Lim inn i $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'Lagret kun for denne BusyMark-sesjonen.';
+
+ @override
+ String get clipboardNoItems => 'Utklippshistorien er tom.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Ingen utklippsposter samsvarer med denne søkingen.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Samlingen \"Utklippshistorie\" er deaktivert i Innstillinger.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Innholdet i det nåværende utklippet er ikke tilgjengelig eller støttet.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Denne utklippsteksten er for stor til å lagre.';
+
+ @override
+ String get clipboardPastePlainText => 'Lim inn som ren tekst';
+
+ @override
+ String get clipboardClearAll => 'Tøm utklippshistorie';
+
+ @override
+ String get clipboardEntryText => 'Tekst';
+
+ @override
+ String get clipboardEntryRichText => 'Rik tekst';
+
+ @override
+ String get clipboardEntryImage => 'Bilde';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Kopiert fra $name';
+ }
+
+ @override
+ String get localHistory => 'Lokal historie';
+
+ @override
+ String get localHistoryEllipsis => 'Lokal historie…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Vis lagrede og automatiserte lokale versjoner for det nåværende dokumentet.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Finn i Lokal Historikk…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Søk etter dokumenter og innhold i Lokal Historikk.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Ingen dokumenter har Lokal Historikk ennå.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Dette dokumentet har ingen revisjoner i Lokal Historikk.';
+
+ @override
+ String get localHistoryNoMatches => 'Ingen revisjoner matcher denne søken.';
+
+ @override
+ String get localHistorySearchHint => 'Søk i lokal historikk';
+
+ @override
+ String get localHistorySelectedRevision => 'Valgt revisjon';
+
+ @override
+ String get localHistoryCurrentEditor => 'Nåværende innhold i redigereren';
+
+ @override
+ String get localHistoryCurrentDisk => 'Nåværende innhold på disken';
+
+ @override
+ String get localHistoryMissingFile => 'Den opprinnelige filen mangler.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Gjenopprett Versjon';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Gjenopprett til Opprinnelig Sted';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Gjenopprett til Nytt Sted…';
+
+ @override
+ String get localHistoryRestoreChange => 'Gjenopprett Endring';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Denne endringen kan ikke gjenopprettes fordi sammenligningen er ikke lenger aktuell.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Denne store sammenligningen er forenklet. Gjenopprett individuelle endringer er ikke tilgjengelig.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Eldre versjonsendring: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Nåværende innholdsendring: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Slettet';
+
+ @override
+ String get localHistoryUntitled => 'Uten tittel';
+
+ @override
+ String get localHistoryClearDocument =>
+ 'Tøm historikken for dette dokumentet';
+
+ @override
+ String get localHistoryClearAll => 'Tøm all lokal historikk';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'Tøm dokumentets historikk?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Tøm all lokal historikk?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Dette sletter permanent lagrede versjoner. Denne handlingen kan ikke angres.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Lokal historikk indeksen ble skadet og gjenopprettet fra intakte versjoner.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Denne lokale historikk-lagringen bruker et støttet format $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Lokal historikk er ikke tilgjengelig: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Opptak er deaktivert eller denne stien er utelatt. Aktiver opptak før gjenoppretting.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Lokal historikk kunne ikke registrere endringen av stien: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Lokal Historikk kunne ikke lagre den slettede stien: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Den valgte versjonen mangler eller er skadet.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Den valgte versjonen kan ikke leses: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Lokal Historikk kunne ikke fange opp denne versjonen: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Baselinje';
+
+ @override
+ String get localHistoryReasonSaved => 'Lagret';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Automatisk sjekkpunkt';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Før omlasting';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Før avvisning';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Før gjenoppbygging';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Før sletting';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Ekstern endring';
+
+ @override
+ String get settingsHistory => 'Historikk';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Samle Historikk fra utklippstavlen';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Behold kopiert og vellykket limt innhold kun for denne BusyMark-sesjonen.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Lag lokal historikk';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Lagre dokumentversjoner på denne enheten for gjenoppretting og sammenligning.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Automatisk sjekkpunktintervall';
+
+ @override
+ String get settingsHistoryRetention => 'Bevaringsperiode';
+
+ @override
+ String get settingsHistoryStorage => 'Maks lagringsplass';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Udelte stier';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Én absolutt sti per linje';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value sekunder';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value dager';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: skriv inn et tall mellom $minimum og $maximum.';
diff --git a/lib/l10n/generated/app_localizations_nl.dart b/lib/l10n/generated/app_localizations_nl.dart
index 3c8f86f2..ec88b5aa 100644
--- a/lib/l10n/generated/app_localizations_nl.dart
+++ b/lib/l10n/generated/app_localizations_nl.dart
@@ -941,6 +941,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get hardLineBreak => 'Hard regeleinde';
+ @override
+ String get insertBlankLine => 'Lege regel invoegen';
+
@override
String get textStyle => 'Tekststijl';
@@ -1124,7 +1127,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get files => 'Bestanden';
@override
- String get toc => 'TOC';
+ String get toc => 'Onderwerpen';
@override
String get tocActions => 'TOC-acties';
@@ -1566,6 +1569,21 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Ongeldige reguliere expressie';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Regex-overeenkomsten met lengte nul worden niet ondersteund (zoals ^, \$ of lookaheads die alleen een positie vinden).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Slechts een deel van de resultaten wordt getoond. Toon meer of verfijn de zoekopdracht.';
+
+ @override
+ String get workspaceSearchShowMore => 'Meer resultaten tonen';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'De zoekopdracht is onvolledig. Deze bestanden zijn groter dan 1 MiB of konden niet worden gelezen:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Groot bestand: markeren en vouwen worden gepauzeerd';
@@ -3931,6 +3949,291 @@ class AppLocalizationsNl extends AppLocalizations {
String get exportInvalidCss =>
'Kies een leesbaar UTF-8 .css-bestand tot 256 KiB zonder imports, bron-URL’s, HTML of uitvoerbare of ontsnapte CSS.';
+ @override
+ String get clipboardHistory => 'Historie van de klembord';
+
+ @override
+ String get clipboardHistorySearchHint => 'Klembordgeschiedenis doorzoeken';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Open de klembordgeschiedenis per sessie.';
+
+ @override
+ String get clipboardCurrent => 'Huidig klembord';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Huidig klembord van een ander programma';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Plakken in $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Alleen opgeslagen voor deze BusyMark sessie.';
+
+ @override
+ String get clipboardNoItems => 'De klembordgeschiedenis is leeg.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Geen klemborditems komen overeen met deze zoekopdracht.';
+
+ @override
+ String get clipboardDisabled =>
+ 'De collectie \"Historie van het klembord\" is uitgeschakeld in de instellingen.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'De inhoud van het huidige klembord is niet beschikbaar of niet ondersteund.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Dit item op het klembord is te groot om te bewaren.';
+
+ @override
+ String get clipboardPastePlainText => 'Plak als gewone tekst';
+
+ @override
+ String get clipboardClearAll => 'Maak de geschiedenis van het klembord leeg';
+
+ @override
+ String get clipboardEntryText => 'Tekst';
+
+ @override
+ String get clipboardEntryRichText => 'Rijke tekst';
+
+ @override
+ String get clipboardEntryImage => 'Afbeelding';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Geplakt van $name';
+ }
+
+ @override
+ String get localHistory => 'Lokale geschiedenis';
+
+ @override
+ String get localHistoryEllipsis => 'Lokale geschiedenis…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Toon opgeslagen en automatische lokale revisies voor het huidige document.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Zoeken in Lokale Geschiedenis…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Zoek naar documenten en inhoud van revisies in de lokale geschiedenis.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Er zijn nog geen documenten met Lokale Geschiedenis.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Dit document heeft geen revisies van de Lokale Geschiedenis.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Geen revisies van de Lokale Geschiedenis komen overeen met deze zoekopdracht.';
+
+ @override
+ String get localHistorySearchHint => 'Lokale geschiedenis doorzoeken';
+
+ @override
+ String get localHistorySelectedRevision => 'Geselecteerde revisie';
+
+ @override
+ String get localHistoryCurrentEditor => 'Huidige inhoud van de editor';
+
+ @override
+ String get localHistoryCurrentDisk => 'Huidige inhoud op schijf';
+
+ @override
+ String get localHistoryMissingFile => 'Het originele bestand ontbreekt.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Herstel Versie';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Herstel naar Originele Locatie';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Herstel naar Nieuwe Locatie…';
+
+ @override
+ String get localHistoryRestoreChange => 'Herstel Wijziging';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Deze wijziging kan niet worden hersteld omdat de vergelijking niet meer actueel is.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Deze grote vergelijking is vereenvoudigd. Het herstellen van individuele wijzigingen is niet mogelijk.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Oudere versie wijziging: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Huidige inhoud wijziging: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Verwijderd';
+
+ @override
+ String get localHistoryUntitled => 'Onbenoemd';
+
+ @override
+ String get localHistoryClearDocument =>
+ 'Verwijder de geschiedenis van dit document';
+
+ @override
+ String get localHistoryClearAll => 'Verwijder de lokale geschiedenis';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Verwijder de documentgeschiedenis?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Verwijder de lokale geschiedenis?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Dit verwijdert permanent opgeslagen revisies. Deze actie kan niet worden teruggedraaid.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'De index van de lokale geschiedenis is beschadigd en opnieuw gebouwd vanuit intacte revisies.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Deze lokale geschiedenisopslag gebruikt een onondersteunde $version-formaat.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'De lokale geschiedenis is niet beschikbaar: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Opnemen is uitgeschakeld of deze pad is uitgesloten. Schakel opnemen opnieuw in voordat u herstelt.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'De lokale geschiedenis kon de padwijziging niet opslaan: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Lokale Geschiedenis kon de verwijderde pad niet opslaan: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'De geselecteerde versie ontbreekt of is beschadigd.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'De geselecteerde versie kan niet worden gelezen: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Lokale Geschiedenis kon deze versie niet vastleggen: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Basislijn';
+
+ @override
+ String get localHistoryReasonSaved => 'Bewaard';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Automatische controlepunt';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Voor herladen';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Voor verwijderen';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Voordat herstellen';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Voordat verwijderen';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Externe wijziging';
+
+ @override
+ String get settingsHistory => 'Geschiedenis';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Verzamelen van de geschiedenis van de klembord';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Behoud van gekopieerd en succesvol geplakt materiaal alleen voor deze BusyMark sessie.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Opnemen van lokale geschiedenis';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Opslaan van documentversies op dit apparaat voor herstel en vergelijking.';
+
+ @override
+ String get settingsHistoryCheckpoint =>
+ 'Automatische interval voor controlepunten';
+
+ @override
+ String get settingsHistoryRetention => 'Bewaarperiode';
+
+ @override
+ String get settingsHistoryStorage => 'Maximale opslag';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Uitsluitingen';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Eén absolute pad per regel';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value seconden';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value dagen';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: voer een getal tussen $minimum en $maximum in.';
diff --git a/lib/l10n/generated/app_localizations_pl.dart b/lib/l10n/generated/app_localizations_pl.dart
index 9d4290c7..401758c7 100644
--- a/lib/l10n/generated/app_localizations_pl.dart
+++ b/lib/l10n/generated/app_localizations_pl.dart
@@ -956,6 +956,9 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get hardLineBreak => 'Twardy podział wiersza';
+ @override
+ String get insertBlankLine => 'Wstaw pusty wiersz';
+
@override
String get textStyle => 'Styl tekstu';
@@ -1140,7 +1143,7 @@ class AppLocalizationsPl extends AppLocalizations {
String get files => 'Pliki';
@override
- String get toc => 'Spis treści';
+ String get toc => 'Tematy';
@override
String get tocActions => 'Działania dotyczące spisu treści';
@@ -1582,6 +1585,21 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Nieprawidłowe wyrażenie regularne';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Dopasowania wyrażeń regularnych o zerowej długości nie są obsługiwane (np. ^, \$ lub asercje dopasowujące tylko pozycję).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Wyświetlono tylko część wyników. Pokaż więcej lub zawęź wyszukiwanie.';
+
+ @override
+ String get workspaceSearchShowMore => 'Pokaż więcej wyników';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Wyszukiwanie jest niepełne. Te pliki przekraczają 1 MiB lub nie można ich odczytać:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Duży plik: podświetlanie i zwijanie są wstrzymane';
@@ -3940,6 +3958,289 @@ class AppLocalizationsPl extends AppLocalizations {
String get exportInvalidCss =>
'Wybierz czytelny plik .css UTF-8 do 256 KiB, bez importów, adresów zasobów, HTML oraz wykonywalnego CSS i sekwencji ucieczki.';
+ @override
+ String get clipboardHistory => 'Historia w Koszyku';
+
+ @override
+ String get clipboardHistorySearchHint => 'Przeszukaj historię schowka';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Otwórz historię w koszyku lokalną dla sesji.';
+
+ @override
+ String get clipboardCurrent => 'Aktualny koszyk';
+
+ @override
+ String get clipboardCurrentExternal => 'Aktualny koszyk z innego programu';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Wklej do $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Przechowywane tylko dla tej sesji BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'Historia w koszyku jest pusta.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Żadne elementy w koszyku nie pasują do tego wyszukiwania.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Zbiór \"Historia w koszyku\" wyłączony w Ustawieniach.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Aktualny zawartość koszyka niedostępna lub nieobsługiwana.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Ten element schowka jest zbyt duży, aby go zapisać.';
+
+ @override
+ String get clipboardPastePlainText => 'Wklej jako zwykły tekst';
+
+ @override
+ String get clipboardClearAll => 'Wyczyść historię schowka';
+
+ @override
+ String get clipboardEntryText => 'Tekst';
+
+ @override
+ String get clipboardEntryRichText => 'Bogaty tekst';
+
+ @override
+ String get clipboardEntryImage => 'Obraz';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Skopiowano z $name';
+ }
+
+ @override
+ String get localHistory => 'Lokalna historia';
+
+ @override
+ String get localHistoryEllipsis => 'Lokalna historia…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Pokaż zapisane i automatyczne lokalne wersje dla bieżącego dokumentu.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Znajdź w Lokalnej Historii…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Wyszukaj dokumenty i zawartość wersji w Lokalnej Historii.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Żaden dokument nie ma Lokalnej Historii.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Ten dokument nie ma wersji w Lokalnej Historii.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Żadne wersje nie pasują do tego wyszukiwania.';
+
+ @override
+ String get localHistorySearchHint => 'Przeszukaj historię lokalną';
+
+ @override
+ String get localHistorySelectedRevision => 'Wybrana wersja';
+
+ @override
+ String get localHistoryCurrentEditor => 'Aktualny zawartość edytora';
+
+ @override
+ String get localHistoryCurrentDisk => 'Aktualna zawartość na dysku';
+
+ @override
+ String get localHistoryMissingFile => 'Oryginalny plik nie istnieje.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Przywróć Wersję';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Przywróć do Oryginalnej Lokalizacji';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Przywróć do Nowej Lokalizacji…';
+
+ @override
+ String get localHistoryRestoreChange => 'Przywróć Zmianę';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Ta zmiana nie może być przywrócona, ponieważ porównanie nie jest już aktualne.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'To duże porównanie zostało uproszczone. Przywracanie pojedynczych zmian jest niedostępne.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Stara wersja zmiany: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Aktualna zmiana zawartości: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Usunięto';
+
+ @override
+ String get localHistoryUntitled => 'Bez tytułu';
+
+ @override
+ String get localHistoryClearDocument => 'Wyczyść historię tego dokumentu';
+
+ @override
+ String get localHistoryClearAll => 'Wyczyść całą lokalną historię';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Czy chcesz wyczyścić historię dokumentu?';
+
+ @override
+ String get localHistoryClearAllTitle =>
+ 'Czy chcesz wyczyścić całą lokalną historię?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'To trwale usuwa zapisane wersje. Ta akcja nie może być cofnięta.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Indeks Lokalnej Historii został uszkodzony i odtworzony z nieuszkodzonych wersji.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Ta Lokalna Historia używa nieobsługiwanego formatu $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Lokalna Historia jest niedostępna: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Nagrywanie jest wyłączone lub ta ścieżka jest wykluczona. Włącz nagrywanie przed przywracaniem.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Lokalna Historia nie mogła zarejestrować zmiany ścieżki: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Lokalna historia nie mogła zachować usuniętego ścieżki: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Wybrana wersja jest nieobecna lub uszkodzona.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Wybrana wersja nie może być odczytana: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Lokalna historia nie mogła przechwycić tej wersji: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Podstawa';
+
+ @override
+ String get localHistoryReasonSaved => 'Zapisano';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Automatyczny checkpoint';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Przed ponownym załadowaniem';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Przed wyrzuceniem';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Przed przywróceniem';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Przed usunięciem';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Zmiana z zewnątrz';
+
+ @override
+ String get settingsHistory => 'Historia';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Zapisywanie historii w schowku';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Zachowaj skopiowany i pomyślnie wklejony zawartość tylko dla tej sesji BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Zapisywanie lokalnej historii';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Przechowywanie wersji dokumentu na tym urządzeniu w celu odzyskania i porównywania.';
+
+ @override
+ String get settingsHistoryCheckpoint =>
+ 'Automatyczny interwał kontrolnego zapisu';
+
+ @override
+ String get settingsHistoryRetention => 'Okres przechowywania';
+
+ @override
+ String get settingsHistoryStorage => 'Maksymalna pojemność';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Wykluczone ścieżki';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Jedna absolutna ścieżka na linię';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value sekundy';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value dni';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: wpisz liczbę między $minimum a $maximum.';
diff --git a/lib/l10n/generated/app_localizations_pt.dart b/lib/l10n/generated/app_localizations_pt.dart
index 01ee508b..c6d679a0 100644
--- a/lib/l10n/generated/app_localizations_pt.dart
+++ b/lib/l10n/generated/app_localizations_pt.dart
@@ -948,6 +948,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get hardLineBreak => 'Quebra de linha forçada';
+ @override
+ String get insertBlankLine => 'Inserir linha em branco';
+
@override
String get textStyle => 'Estilo de texto';
@@ -1131,7 +1134,7 @@ class AppLocalizationsPt extends AppLocalizations {
String get files => 'Ficheiros';
@override
- String get toc => 'TOC';
+ String get toc => 'Tópicos';
@override
String get tocActions => 'Ações do sumário';
@@ -1568,6 +1571,21 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Expressão regular inválida';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Não são suportadas correspondências regex de comprimento zero (por exemplo, ^, \$ ou antecipações que correspondem apenas a uma posição).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Só são apresentados alguns resultados. Mostre mais ou restrinja a pesquisa.';
+
+ @override
+ String get workspaceSearchShowMore => 'Mostrar mais resultados';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'A pesquisa está incompleta. Estes ficheiros excedem 1 MiB ou não foi possível lê-los:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Ficheiro grande: o realce e o recolhimento estão pausados';
@@ -3945,6 +3963,291 @@ class AppLocalizationsPt extends AppLocalizations {
String get exportInvalidCss =>
'Escolha um ficheiro .css UTF-8 legível até 256 KiB, sem importações, URL de recursos, HTML ou CSS executável ou com escapes.';
+ @override
+ String get clipboardHistory => 'Histórico da Área de Transferência';
+
+ @override
+ String get clipboardHistorySearchHint =>
+ 'Pesquisar no histórico da área de transferência';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Abrir o histórico da área de transferência local da sessão.';
+
+ @override
+ String get clipboardCurrent => 'Área de transferência atual';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Área de transferência de outro aplicativo';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Colar em $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Armazenado apenas para esta sessão BusyMark.';
+
+ @override
+ String get clipboardNoItems =>
+ 'O histórico da área de transferência está vazio.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Nenhum item da área de transferência corresponde a esta pesquisa.';
+
+ @override
+ String get clipboardDisabled =>
+ 'A coleção de Histórico da Área de Transferência está desativada nas Configurações.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'O conteúdo da área de transferência atual não está disponível ou não é suportado.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Este item na área de transferência é demasiado grande para manter.';
+
+ @override
+ String get clipboardPastePlainText => 'Colar como Texto Simples';
+
+ @override
+ String get clipboardClearAll => 'Limpar o Histórico da Área de Transferência';
+
+ @override
+ String get clipboardEntryText => 'Texto';
+
+ @override
+ String get clipboardEntryRichText => 'Texto formatado';
+
+ @override
+ String get clipboardEntryImage => 'Imagem';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Copiado de $name';
+ }
+
+ @override
+ String get localHistory => 'Histórico Local';
+
+ @override
+ String get localHistoryEllipsis => 'Histórico Local…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Mostrar as revisões locais salvas e automáticas para o documento atual.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Encontrar no Histórico Local…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Procurar documentos e conteúdos de revisão no Histórico Local.';
+
+ @override
+ String get localHistoryNoDocuments => 'Nenhum documento tem Histórico Local.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Este documento não tem revisões no Histórico Local.';
+
+ @override
+ String get localHistoryNoMatches => 'Nenhuma revisão corresponde à pesquisa.';
+
+ @override
+ String get localHistorySearchHint => 'Pesquisar no histórico local';
+
+ @override
+ String get localHistorySelectedRevision => 'Revisão selecionada';
+
+ @override
+ String get localHistoryCurrentEditor => 'Conteúdo atual do editor';
+
+ @override
+ String get localHistoryCurrentDisk => 'Conteúdo atual no disco';
+
+ @override
+ String get localHistoryMissingFile => 'O ficheiro original está ausente.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Restaurar Versão';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Restaurar para Localização Original';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Restaurar para Nova Localização…';
+
+ @override
+ String get localHistoryRestoreChange => 'Restaurar Alteração';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Esta alteração não pode ser restaurada porque a comparação não está mais atualizada.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Esta alteração grande foi simplificada. Restaurar alterações individuais não está disponível.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Alteração de versão anterior: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Alteração de conteúdo atual: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Excluído';
+
+ @override
+ String get localHistoryUntitled => 'Sem título';
+
+ @override
+ String get localHistoryClearDocument => 'Limpar o Histórico deste Documento';
+
+ @override
+ String get localHistoryClearAll => 'Limpar Todo o Histórico Local';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Limpar o Histórico do Documento?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Limpar Todo o Histórico Local?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Isto elimina permanentemente as versões armazenadas. Esta ação não pode ser desfeita.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'O índice do Histórico Local foi danificado e reconstruído a partir de versões intactas.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Esta loja de Histórico Local utiliza um formato não suportado $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'O Histórico Local não está disponível: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'A gravação está desativada ou este caminho está excluído. Reative a gravação antes de restaurar.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'O Histórico Local não conseguiu gravar a alteração do caminho: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'O Histórico Local não conseguiu reter o caminho eliminado: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'A revisão selecionada está ausente ou danificada.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'A revisão selecionada não pode ser lida: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'O Histórico Local não conseguiu capturar esta revisão: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Linha Base';
+
+ @override
+ String get localHistoryReasonSaved => 'Guardado';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Checkpoint automático';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Antes de recarregar';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Antes de descartar';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Antes de restaurar';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Antes de apagar';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Alteração externa';
+
+ @override
+ String get settingsHistory => 'Histórico';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Recolher Histórico da Área de Transferência';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Manter o conteúdo copiado e colado com sucesso apenas para esta sessão BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Gravar Histórico Local';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Armazenar as versões do documento neste dispositivo para recuperação e comparação.';
+
+ @override
+ String get settingsHistoryCheckpoint =>
+ 'Intervalo de ponto de controlo automático';
+
+ @override
+ String get settingsHistoryRetention => 'Período de retenção';
+
+ @override
+ String get settingsHistoryStorage => 'Armazenamento máximo';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Caminhos excluídos';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Um caminho absoluto por linha';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value segundos';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value dias';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: introduza um número entre $minimum e $maximum.';
@@ -4894,6 +5197,9 @@ class AppLocalizationsPtBr extends AppLocalizationsPt {
@override
String get hardLineBreak => 'Quebra de linha forçada';
+ @override
+ String get insertBlankLine => 'Inserir linha em branco';
+
@override
String get textStyle => 'Estilo de texto';
@@ -5077,7 +5383,7 @@ class AppLocalizationsPtBr extends AppLocalizationsPt {
String get files => 'Arquivos';
@override
- String get toc => 'TOC';
+ String get toc => 'Tópicos';
@override
String get tocActions => 'Ações do sumário';
@@ -5514,6 +5820,21 @@ class AppLocalizationsPtBr extends AppLocalizationsPt {
@override
String get sourceSearchInvalidRegex => 'Expressão regular inválida';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Não há suporte para correspondências regex de comprimento zero (por exemplo, ^, \$ ou antecipações que correspondem apenas a uma posição).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Apenas alguns resultados são exibidos. Mostre mais ou restrinja a pesquisa.';
+
+ @override
+ String get workspaceSearchShowMore => 'Mostrar mais resultados';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'A pesquisa está incompleta. Estes arquivos excedem 1 MiB ou não puderam ser lidos:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Arquivo grande: o realce e o recolhimento estão pausados';
@@ -7888,6 +8209,292 @@ class AppLocalizationsPtBr extends AppLocalizationsPt {
String get exportInvalidCss =>
'Escolha um arquivo .css UTF-8 legível até 256 KiB, sem importações, URL de recursos, HTML ou CSS executável ou com escapes.';
+ @override
+ String get clipboardHistory => 'Histórico da Área de Transferência';
+
+ @override
+ String get clipboardHistorySearchHint =>
+ 'Pesquisar no histórico da área de transferência';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Abrir o histórico da área de transferência local da sessão.';
+
+ @override
+ String get clipboardCurrent => 'Área de transferência atual';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Área de transferência de outro aplicativo';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Colar em $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Armazenado apenas para esta sessão BusyMark.';
+
+ @override
+ String get clipboardNoItems =>
+ 'O histórico da área de transferência está vazio.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Nenhum item da área de transferência corresponde a esta pesquisa.';
+
+ @override
+ String get clipboardDisabled =>
+ 'A coleção de Histórico da Área de Transferência está desativada nas Configurações.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'O conteúdo da área de transferência atual não está disponível ou não é suportado.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Este item da área de transferência é muito grande para ser armazenado.';
+
+ @override
+ String get clipboardPastePlainText => 'Colar como Texto Simples';
+
+ @override
+ String get clipboardClearAll => 'Limpar o Histórico da Área de Transferência';
+
+ @override
+ String get clipboardEntryText => 'Texto';
+
+ @override
+ String get clipboardEntryRichText => 'Texto formatado';
+
+ @override
+ String get clipboardEntryImage => 'Imagem';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Copiado de $name';
+ }
+
+ @override
+ String get localHistory => 'Histórico Local';
+
+ @override
+ String get localHistoryEllipsis => 'Histórico Local…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Mostrar revisões locais salvas e automáticas para o documento atual.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Encontrar no Histórico Local…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Pesquisar documentos e conteúdo de revisões no Histórico Local.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Nenhum documento possui Histórico Local.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Este documento não possui revisões no Histórico Local.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Nenhuma revisão no Histórico Local corresponde a esta pesquisa.';
+
+ @override
+ String get localHistorySearchHint => 'Pesquisar no histórico local';
+
+ @override
+ String get localHistorySelectedRevision => 'Revisão selecionada';
+
+ @override
+ String get localHistoryCurrentEditor => 'Conteúdo atual do editor';
+
+ @override
+ String get localHistoryCurrentDisk => 'Conteúdo atual no disco';
+
+ @override
+ String get localHistoryMissingFile => 'O arquivo original está faltando.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Restaurar Versão';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Restaurar para a Localização Original';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Restaurar para a Nova Localização…';
+
+ @override
+ String get localHistoryRestoreChange => 'Restaurar Alteração';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Esta alteração não pode ser restaurada porque a comparação não está mais atualizada.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Esta comparação grande foi simplificada. Restaurar alterações individuais não está disponível.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Alteração da versão anterior: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Alteração do conteúdo atual: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Excluído';
+
+ @override
+ String get localHistoryUntitled => 'Sem Título';
+
+ @override
+ String get localHistoryClearDocument => 'Limpar o Histórico deste Documento';
+
+ @override
+ String get localHistoryClearAll => 'Limpar todo o Histórico Local';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Limpar o histórico do documento?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Limpar todo o Histórico Local?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Isso remove permanentemente as revisões armazenadas. Esta ação não pode ser desfeita.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'O índice do Histórico Local foi danificado e reconstruído a partir de revisões íntegras.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Esta loja de Histórico Local usa o formato não suportado $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Histórico Local não está disponível: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'A gravação está desativada ou este caminho está excluído. Reative a gravação antes de restaurar.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'O Histórico Local não conseguiu gravar a alteração do caminho: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'O Histórico Local não conseguiu reter o caminho excluído: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'A revisão selecionada está ausente ou corrompida.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'A revisão selecionada não pode ser lida: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'O Histórico Local não conseguiu capturar esta revisão: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Linha de Base';
+
+ @override
+ String get localHistoryReasonSaved => 'Salvo';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Checkpoint automático';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Antes de recarregar';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Antes de descartar';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Antes da restauração';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Antes da exclusão';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Mudança externa';
+
+ @override
+ String get settingsHistory => 'Histórico';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Coletar Histórico da Área de Transferência';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Manter o conteúdo copiado e colado com sucesso apenas para esta sessão BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Gravar Histórico Local';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Armazenar revisões do documento neste dispositivo para recuperação e comparação.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Intervalo de checkpoint automático';
+
+ @override
+ String get settingsHistoryRetention => 'Tempo de retenção';
+
+ @override
+ String get settingsHistoryStorage => 'Armazenamento máximo';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Caminhos excluídos';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Um caminho absoluto por linha';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value segundos';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value dias';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: insira um número entre $minimum e $maximum.';
diff --git a/lib/l10n/generated/app_localizations_ru.dart b/lib/l10n/generated/app_localizations_ru.dart
index 1bae9acf..513224ce 100644
--- a/lib/l10n/generated/app_localizations_ru.dart
+++ b/lib/l10n/generated/app_localizations_ru.dart
@@ -952,6 +952,9 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get hardLineBreak => 'Принудительный перенос строки';
+ @override
+ String get insertBlankLine => 'Вставить пустую строку';
+
@override
String get textStyle => 'Стиль текста';
@@ -1137,7 +1140,7 @@ class AppLocalizationsRu extends AppLocalizations {
String get files => 'Файлы';
@override
- String get toc => 'Оглавление';
+ String get toc => 'Темы';
@override
String get tocActions => 'Действия с оглавлением';
@@ -1577,6 +1580,21 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Некорректное регулярное выражение';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Совпадения регулярных выражений нулевой длины не поддерживаются (например, ^, \$ или проверки вперёд, совпадающие только с позицией).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Показана только часть результатов. Покажите больше или уточните поиск.';
+
+ @override
+ String get workspaceSearchShowMore => 'Показать больше результатов';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Поиск неполный. Эти файлы превышают 1 MiB или не могут быть прочитаны:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Большой файл: подсветка и сворачивание приостановлены';
@@ -3941,6 +3959,289 @@ class AppLocalizationsRu extends AppLocalizations {
String get exportInvalidCss =>
'Выберите читаемый файл .css в UTF-8 до 256 КиБ без импортов, URL ресурсов, HTML, исполняемого CSS и escape-последовательностей.';
+ @override
+ String get clipboardHistory => 'История буфера обмена';
+
+ @override
+ String get clipboardHistorySearchHint => 'Поиск в истории буфера обмена';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Открыть историю буфера обмена, специфичную для сеанса.';
+
+ @override
+ String get clipboardCurrent => 'Текущий буфер обмена';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Текущий буфер обмена из другого приложения';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Вставить в $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Сохраняется только для текущего сеанса BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'История буфера обмена пуста.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Нет элементов буфера обмена, соответствующих этому поиску.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Коллекция \"История буфера обмена\" отключена в настройках.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Текущее содержимое буфера обмена недоступно или не поддерживается.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Этот элемент буфера обмена слишком велик для хранения.';
+
+ @override
+ String get clipboardPastePlainText => 'Вставить как обычный текст';
+
+ @override
+ String get clipboardClearAll => 'Очистить историю буфера обмена';
+
+ @override
+ String get clipboardEntryText => 'Текст';
+
+ @override
+ String get clipboardEntryRichText => 'Форматированный текст';
+
+ @override
+ String get clipboardEntryImage => 'Изображение';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Скопировано из $name';
+ }
+
+ @override
+ String get localHistory => 'Локальная история';
+
+ @override
+ String get localHistoryEllipsis => 'Локальная история…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Отобразить сохраненные и автоматические локальные версии для текущего документа.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Найти в локальной истории…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Поиск документов и содержимого версий в локальной истории.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Ни один документ не имеет локальной истории.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Этот документ не имеет версий в локальной истории.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Ни одна версия не соответствует этому поиску.';
+
+ @override
+ String get localHistorySearchHint => 'Поиск в локальной истории';
+
+ @override
+ String get localHistorySelectedRevision => 'Выбранная версия';
+
+ @override
+ String get localHistoryCurrentEditor => 'Текущее содержимое редактора';
+
+ @override
+ String get localHistoryCurrentDisk => 'Текущее содержимое на диске';
+
+ @override
+ String get localHistoryMissingFile => 'Оригинальный файл отсутствует.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Восстановить версию';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Восстановить в исходное местоположение';
+
+ @override
+ String get localHistoryRestoreNewLocation =>
+ 'Восстановить в новое местоположение…';
+
+ @override
+ String get localHistoryRestoreChange => 'Восстановить изменения';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Эти изменения нельзя восстановить, так как сравнение больше не актуально.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Это большое сравнение упрощено. Восстановление отдельных изменений недоступно.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Изменения в более старой версии: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Изменения в текущей версии: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Удалено';
+
+ @override
+ String get localHistoryUntitled => 'Безымянный';
+
+ @override
+ String get localHistoryClearDocument => 'Очистить историю этого документа';
+
+ @override
+ String get localHistoryClearAll => 'Очистить всю локальную историю';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'Очистить историю документа?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Очистить всю локальную историю?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Это навсегда удаляет сохраненные версии. Это действие нельзя отменить.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Индекс локальной истории был поврежден и восстановлен из неповрежденных версий.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Эта локальная история использует неподдерживаемый формат $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Локальная история недоступна: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Запись отключена или этот путь исключен. Включите запись перед восстановлением.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Локальная история не смогла записать изменение пути: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Локальная история не смогла сохранить удалённый путь: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Выбранная версия отсутствует или повреждена.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Выбранную версию невозможно прочитать: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Локальная история не смогла захватить эту версию: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Базовая версия';
+
+ @override
+ String get localHistoryReasonSaved => 'Сохранено';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Автоматическая контрольная точка';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Перед перезагрузкой';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Перед удалением';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Перед восстановлением';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Перед удалением';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Внешнее изменение';
+
+ @override
+ String get settingsHistory => 'История';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Сохранить историю буфера обмена';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Сохранять только скопированный и успешно вставленный контент для текущей сессии BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Записывать локальную историю';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Сохранять версии документа на этом устройстве для восстановления и сравнения.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Интервал автоматического сохранения';
+
+ @override
+ String get settingsHistoryRetention => 'Срок хранения';
+
+ @override
+ String get settingsHistoryStorage => 'Максимальное хранилище';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Исключенные пути';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Один абсолютный путь в строке';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value секунды';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value дни';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: введите число от $minimum до $maximum.';
diff --git a/lib/l10n/generated/app_localizations_tr.dart b/lib/l10n/generated/app_localizations_tr.dart
index d6a9c6da..c7e6ab7e 100644
--- a/lib/l10n/generated/app_localizations_tr.dart
+++ b/lib/l10n/generated/app_localizations_tr.dart
@@ -942,6 +942,9 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get hardLineBreak => 'Zorunlu satır sonu';
+ @override
+ String get insertBlankLine => 'Boş satır ekle';
+
@override
String get textStyle => 'Metin stili';
@@ -1124,7 +1127,7 @@ class AppLocalizationsTr extends AppLocalizations {
String get files => 'Dosyalar';
@override
- String get toc => 'İçindekiler';
+ String get toc => 'Konular';
@override
String get tocActions => 'İçindekiler eylemleri';
@@ -1554,6 +1557,21 @@ class AppLocalizationsTr extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Geçersiz düzenli ifade';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Sıfır uzunluklu düzenli ifade eşleşmeleri desteklenmez (örneğin ^, \$ veya yalnızca bir konumla eşleşen ileri bakışlar).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Sonuçların yalnızca bir kısmı gösteriliyor. Daha fazlasını gösterin veya aramayı daraltın.';
+
+ @override
+ String get workspaceSearchShowMore => 'Daha fazla sonuç göster';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Arama eksik. Bu dosyalar 1 MiB boyutunu aşıyor veya okunamadı:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Büyük dosya: vurgulama ve katlama duraklatıldı';
@@ -3889,6 +3907,289 @@ class AppLocalizationsTr extends AppLocalizations {
String get exportInvalidCss =>
'İçe aktarım, kaynak URL’si, HTML, çalıştırılabilir veya kaçışlı CSS içermeyen, okunabilir UTF-8 .css dosyası seçin (en fazla 256 KiB).';
+ @override
+ String get clipboardHistory => 'Kopyalama Geçmişi';
+
+ @override
+ String get clipboardHistorySearchHint => 'Pano geçmişinde ara';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Oturuma özgü kopyalama geçmişini aç.';
+
+ @override
+ String get clipboardCurrent => 'Mevcut kopyalama';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Başka bir uygulamadan mevcut kopyalama';
+
+ @override
+ String clipboardDestination(String name) {
+ return '$name\'e yapıştır';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Sadece bu BusyMark oturumu için saklanır.';
+
+ @override
+ String get clipboardNoItems => 'Kopyalama geçmişi boş.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Bu arama ile eşleşen kopyalama öğesi bulunamadı.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Kopyalama Geçmişi koleksiyonu Ayarlar\'da devre dışıdır.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Mevcut kopyalama içeriği mevcut değil veya desteklenmiyor.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Bu kopyalanmış öğe, saklamak için çok büyüktür.';
+
+ @override
+ String get clipboardPastePlainText => 'Temiz Metin Olarak Yapıştır';
+
+ @override
+ String get clipboardClearAll => 'Kopyalama Geçmişini Temizle';
+
+ @override
+ String get clipboardEntryText => 'Metin';
+
+ @override
+ String get clipboardEntryRichText => 'Zengin Metin';
+
+ @override
+ String get clipboardEntryImage => 'Resim';
+
+ @override
+ String clipboardOrigin(String name) {
+ return '$name\'den Kopyalandı';
+ }
+
+ @override
+ String get localHistory => 'Yerel Geçmiş';
+
+ @override
+ String get localHistoryEllipsis => 'Yerel Geçmiş…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Mevcut belge için kaydedilmiş ve otomatik yerel sürümleri göster.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Yerel Geçmişte Bul...';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Yerel Geçmişte belgeleri ve revizyon içeriğini arayın.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Henüz herhangi bir belge Yerel Geçmişe sahip değil.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Bu belge Yerel Geçmiş revizyonlarına sahip değil.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Bu arama ile eşleşen Yerel Geçmiş revizyonu yok.';
+
+ @override
+ String get localHistorySearchHint => 'Yerel geçmişte ara';
+
+ @override
+ String get localHistorySelectedRevision => 'Seçilen revizyon';
+
+ @override
+ String get localHistoryCurrentEditor => 'Mevcut düzenleme içeriği';
+
+ @override
+ String get localHistoryCurrentDisk => 'Disk üzerinde mevcut içerik';
+
+ @override
+ String get localHistoryMissingFile => 'Orijinal dosya bulunmuyor.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Versiyonu Geri Yükle';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Orijinal Konuma Geri Yükle';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Yeni Konuma Geri Yükle…';
+
+ @override
+ String get localHistoryRestoreChange => 'Değişikliği Geri Yükle';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Bu değişiklik geri alınamaz çünkü karşılaştırma artık geçerli değil.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Bu büyük karşılaştırma basitleştirildi. Tek tek değişiklikleri geri yüklemek mümkün değil.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Eski versiyon değişikliği: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Mevcut içerik değişikliği: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Silindi';
+
+ @override
+ String get localHistoryUntitled => 'Başlımsız';
+
+ @override
+ String get localHistoryClearDocument => 'Bu Dokümanın Geçmişini Temizle';
+
+ @override
+ String get localHistoryClearAll => 'Tüm Yerel Geçmişi Temizle';
+
+ @override
+ String get localHistoryClearDocumentTitle =>
+ 'Doküman Geçmişini Temizlemek İstemiyor Musunuz?';
+
+ @override
+ String get localHistoryClearAllTitle =>
+ 'Tüm Yerel Geçmişi Temizlemek İstemiyor Musunuz?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Bu, kaydedilen tüm sürümleri kalıcı olarak kaldırır. Bu işlem geri alınamaz.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Yerel Geçmiş indeksi, sağlam sürümlerden yeniden oluşturuldu.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Bu Yerel Geçmiş depolama, desteklenmeyen format $version kullanır.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Yerel Geçmiş kullanılabilir değil: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Kayıt devre dışı bırakılmış veya bu yol hariç tutulmuş. Geri yükleme yapmadan önce tekrar etkinleştirin.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Yerel Geçmiş, yol değişikliğini kaydetemedi: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Yerel Geçmiş, silinen yolu kaydetemedi: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Seçilen sürüm eksik veya bozulmuş.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Seçilen sürüm okunamaz: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Yerel Geçmiş, bu sürümü yakalamadı: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Temel Sürüm';
+
+ @override
+ String get localHistoryReasonSaved => 'Kaydedildi';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Otomatik kontrol noktası';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Yenilemeden önce';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Atılmadan önce';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Restorasyon öncesi';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Silme öncesi';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Dış kaynak değişiklik';
+
+ @override
+ String get settingsHistory => 'Geçmiş';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Geçmişi Kopyala';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Bu BusyMark oturumu için kopyalanan ve başarıyla yapıştırılan içeriği sakla.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Yerel Geçmişi Kaydet';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Belgeleri kurtarma ve karşılaştırma için bu cihazda değişiklikleri sakla.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Otomatik kontrol noktası aralığı';
+
+ @override
+ String get settingsHistoryRetention => 'Saklama süresi';
+
+ @override
+ String get settingsHistoryStorage => 'Maksimum depolama alanı';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Hariç tutulan yollar';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => 'Bir mutlak yol her satırda';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value saniye';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value gün';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: $minimum ile $maximum arasında bir sayı girin.';
diff --git a/lib/l10n/generated/app_localizations_uk.dart b/lib/l10n/generated/app_localizations_uk.dart
index 76494aa2..5b1790d9 100644
--- a/lib/l10n/generated/app_localizations_uk.dart
+++ b/lib/l10n/generated/app_localizations_uk.dart
@@ -958,6 +958,9 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get hardLineBreak => 'Примусовий розрив рядка';
+ @override
+ String get insertBlankLine => 'Вставити порожній рядок';
+
@override
String get textStyle => 'Стиль тексту';
@@ -1144,7 +1147,7 @@ class AppLocalizationsUk extends AppLocalizations {
String get files => 'Файли';
@override
- String get toc => 'Зміст';
+ String get toc => 'Теми';
@override
String get tocActions => 'Дії зі змістом';
@@ -1585,6 +1588,21 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Некоректний регулярний вираз';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Збіги регулярних виразів нульової довжини не підтримуються (наприклад, ^, \$ або перевірки вперед, що збігаються лише з позицією).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Показано лише частину результатів. Покажіть більше або звузьте пошук.';
+
+ @override
+ String get workspaceSearchShowMore => 'Показати більше результатів';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Пошук неповний. Ці файли перевищують 1 MiB або їх не вдалося прочитати:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Великий файл: підсвічування та згортання призупинено';
@@ -3949,6 +3967,289 @@ class AppLocalizationsUk extends AppLocalizations {
String get exportInvalidCss =>
'Виберіть читабельний файл .css у UTF-8 до 256 КіБ без імпортів, URL ресурсів, HTML, виконуваного CSS і escape-послідовностей.';
+ @override
+ String get clipboardHistory => 'Історія Вирізання';
+
+ @override
+ String get clipboardHistorySearchHint => 'Пошук в історії буфера обміну';
+
+ @override
+ String get clipboardHistoryDescription =>
+ 'Відкрити історію вирізання, прив\'язану до сесії.';
+
+ @override
+ String get clipboardCurrent => 'Поточне вирізання';
+
+ @override
+ String get clipboardCurrentExternal => 'Поточне вирізання з іншого додатку';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Вставити в $name';
+ }
+
+ @override
+ String get clipboardSessionOnly =>
+ 'Зберігається лише для цієї сесії BusyMark.';
+
+ @override
+ String get clipboardNoItems => 'Історія вирізання порожня.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Жоден елемент вирізання не відповідає цьому пошуку.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Збірка \"Історія Вирізання\" вимкнена в Налаштуваннях.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Поточний вміст вирізання недоступний або не підтримується.';
+
+ @override
+ String get clipboardTooLarge =>
+ 'Цей елемент буфера обміну занадто великий для зберігання.';
+
+ @override
+ String get clipboardPastePlainText => 'Вставити як звичайний текст';
+
+ @override
+ String get clipboardClearAll => 'Очистити історію буфера обміну';
+
+ @override
+ String get clipboardEntryText => 'Текст';
+
+ @override
+ String get clipboardEntryRichText => 'Збагачений текст';
+
+ @override
+ String get clipboardEntryImage => 'Зображення';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Скопійовано з $name';
+ }
+
+ @override
+ String get localHistory => 'Локальна історія';
+
+ @override
+ String get localHistoryEllipsis => 'Локальна історія…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Показати збережені та автоматичні локальні версії для поточної документа.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Знайти в локальній історії…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Пошук документів та вмісту версій у локальній історії.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Жоден документ поки не має локальної історії.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Цей документ не має версій локальної історії.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Жодна версія локальної історії не відповідає цьому пошуку.';
+
+ @override
+ String get localHistorySearchHint => 'Пошук у локальній історії';
+
+ @override
+ String get localHistorySelectedRevision => 'Обрана версія';
+
+ @override
+ String get localHistoryCurrentEditor => 'Поточний вміст редактора';
+
+ @override
+ String get localHistoryCurrentDisk => 'Поточний вміст на диску';
+
+ @override
+ String get localHistoryMissingFile => 'Оригінальний файл відсутній.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Відновити версію';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Відновити до початкової локації';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Відновити до нової локації…';
+
+ @override
+ String get localHistoryRestoreChange => 'Відновити зміни';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Ці зміни не можуть бути відновлені, оскільки порівняння більше не актуальне.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'Це велике порівняння спрощено. Відновлення окремих змін недоступне.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Зміна в попередній версії: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Зміна в поточному вмісті: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Видалено';
+
+ @override
+ String get localHistoryUntitled => 'Без назви';
+
+ @override
+ String get localHistoryClearDocument => 'Очистити історію цього документа';
+
+ @override
+ String get localHistoryClearAll => 'Очистити всю локальну історію';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'Очистити історію документа?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Очистити всю локальну історію?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Це постійно видаляє збережені версії. Цю дію неможливо скасувати.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Індекс локальної історії пошкоджено та відновлено з цілих версій.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Ця локальна історія використовує непідтримуваний формат $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Локальна історія недоступна: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Запис вимкнено або цей шлях виключено. Увімкніть запис перед відновленням.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Локальна історія не змогла записати зміну шляху: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Локальна історія не змогла зберегти видалений шлях: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Вибрана версія відсутня або пошкоджена.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Вибрану версію неможливо прочитати: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Локальна історія не змогла зберегти цю версію: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Базова версія';
+
+ @override
+ String get localHistoryReasonSaved => 'Збережено';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint =>
+ 'Автоматичний контрольний пункт';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Перед перезавантаженням';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Перед видаленням';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Перед відновленням';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Перед видаленням';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Зовнішня зміна';
+
+ @override
+ String get settingsHistory => 'Історія';
+
+ @override
+ String get settingsClipboardHistoryTitle =>
+ 'Зберегти історію, скопійовану в буфер обміну';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Зберігати лише скопійований та успішно вставлений вміст для цієї сесії BusyMark.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Записувати локальну історію';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Зберігати версії документа на цьому пристрої для відновлення та порівняння.';
+
+ @override
+ String get settingsHistoryCheckpoint =>
+ 'Автоматичний інтервал збереження контрольної точки';
+
+ @override
+ String get settingsHistoryRetention => 'Тривалість зберігання';
+
+ @override
+ String get settingsHistoryStorage => 'Максимальне місце для зберігання';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Виключені шляхи';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Один абсолютний шлях на рядок';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value секунди';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value дні';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: введіть число від $minimum до $maximum.';
diff --git a/lib/l10n/generated/app_localizations_vi.dart b/lib/l10n/generated/app_localizations_vi.dart
index 79d2c2ce..68003a57 100644
--- a/lib/l10n/generated/app_localizations_vi.dart
+++ b/lib/l10n/generated/app_localizations_vi.dart
@@ -935,6 +935,9 @@ class AppLocalizationsVi extends AppLocalizations {
@override
String get hardLineBreak => 'Ngắt dòng cứng';
+ @override
+ String get insertBlankLine => 'Chèn dòng trống';
+
@override
String get textStyle => 'Kiểu văn bản';
@@ -1117,7 +1120,7 @@ class AppLocalizationsVi extends AppLocalizations {
String get files => 'Tệp';
@override
- String get toc => 'Mục lục';
+ String get toc => 'Chủ đề';
@override
String get tocActions => 'Thao tác với mục lục';
@@ -1550,6 +1553,21 @@ class AppLocalizationsVi extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => 'Biểu thức chính quy không hợp lệ';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ 'Không hỗ trợ kết quả khớp regex có độ dài bằng 0 (ví dụ ^, \$ hoặc lookahead chỉ khớp với một vị trí).';
+
+ @override
+ String get workspaceSearchIncomplete =>
+ 'Chỉ hiển thị một phần kết quả. Hiển thị thêm hoặc thu hẹp tìm kiếm.';
+
+ @override
+ String get workspaceSearchShowMore => 'Hiển thị thêm kết quả';
+
+ @override
+ String get workspaceSearchSkippedFiles =>
+ 'Tìm kiếm chưa đầy đủ. Các tệp này vượt quá 1 MiB hoặc không thể đọc được:';
+
@override
String get sourceLargeFileFeaturesPaused =>
'Tệp lớn: đã tạm dừng tô sáng và thu gọn';
@@ -3884,6 +3902,284 @@ class AppLocalizationsVi extends AppLocalizations {
String get exportInvalidCss =>
'Chọn tệp .css UTF-8 đọc được tối đa 256 KiB, không có nhập tài nguyên, URL tài nguyên, HTML, CSS thực thi hoặc ký tự thoát.';
+ @override
+ String get clipboardHistory => 'Lịch sử sao chép';
+
+ @override
+ String get clipboardHistorySearchHint => 'Tìm kiếm lịch sử bảng nhớ tạm';
+
+ @override
+ String get clipboardHistoryDescription => 'Mở lịch sử sao chép cục bộ phiên.';
+
+ @override
+ String get clipboardCurrent => 'Lịch sử sao chép hiện tại';
+
+ @override
+ String get clipboardCurrentExternal =>
+ 'Lịch sử sao chép từ một ứng dụng khác';
+
+ @override
+ String clipboardDestination(String name) {
+ return 'Dán vào $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => 'Chỉ lưu trong phiên BusyMark này.';
+
+ @override
+ String get clipboardNoItems => 'Lịch sử sao chép trống.';
+
+ @override
+ String get clipboardNoMatches =>
+ 'Không có mục sao chép nào khớp với tìm kiếm này.';
+
+ @override
+ String get clipboardDisabled =>
+ 'Bộ sưu tập Lịch sử sao chép bị vô hiệu hóa trong Cài đặt.';
+
+ @override
+ String get clipboardUnavailable =>
+ 'Nội dung sao chép hiện tại không khả dụng hoặc không được hỗ trợ.';
+
+ @override
+ String get clipboardTooLarge => 'Mục này trong clipboard quá lớn để lưu.';
+
+ @override
+ String get clipboardPastePlainText => 'Dán dưới dạng văn bản thuần túy';
+
+ @override
+ String get clipboardClearAll => 'Xóa lịch sử clipboard';
+
+ @override
+ String get clipboardEntryText => 'Văn bản';
+
+ @override
+ String get clipboardEntryRichText => 'Văn bản phong phú';
+
+ @override
+ String get clipboardEntryImage => 'Hình ảnh';
+
+ @override
+ String clipboardOrigin(String name) {
+ return 'Sao chép từ $name';
+ }
+
+ @override
+ String get localHistory => 'Lịch sử cục bộ';
+
+ @override
+ String get localHistoryEllipsis => 'Lịch sử cục bộ…';
+
+ @override
+ String get localHistoryDescription =>
+ 'Hiển thị các phiên bản cục bộ đã lưu và tự động cho tài liệu hiện tại.';
+
+ @override
+ String get findLocalHistoryEllipsis => 'Tìm trong Lịch sử cục bộ…';
+
+ @override
+ String get findLocalHistoryDescription =>
+ 'Tìm kiếm các tài liệu và nội dung phiên bản trong Lịch sử cục bộ.';
+
+ @override
+ String get localHistoryNoDocuments =>
+ 'Chưa có tài liệu nào có Lịch sử cục bộ.';
+
+ @override
+ String get localHistoryNoRevisions =>
+ 'Tài liệu này không có các phiên bản Lịch sử cục bộ.';
+
+ @override
+ String get localHistoryNoMatches =>
+ 'Không có phiên bản nào trong Lịch sử cục bộ khớp với tìm kiếm này.';
+
+ @override
+ String get localHistorySearchHint => 'Tìm kiếm lịch sử cục bộ';
+
+ @override
+ String get localHistorySelectedRevision => 'Phiên bản đã chọn';
+
+ @override
+ String get localHistoryCurrentEditor => 'Nội dung trình soạn thảo hiện tại';
+
+ @override
+ String get localHistoryCurrentDisk => 'Nội dung hiện tại trên ổ đĩa';
+
+ @override
+ String get localHistoryMissingFile => 'Tệp gốc bị thiếu.';
+
+ @override
+ String get localHistoryRestoreRevision => 'Khôi phục Phiên bản';
+
+ @override
+ String get localHistoryRestoreOriginalLocation =>
+ 'Khôi phục về Vị trí Ban đầu';
+
+ @override
+ String get localHistoryRestoreNewLocation => 'Khôi phục về Vị trí Mới…';
+
+ @override
+ String get localHistoryRestoreChange => 'Khôi phục Thay đổi';
+
+ @override
+ String get localHistoryRestoreUnavailable =>
+ 'Thay đổi này không thể khôi phục vì so sánh không còn hợp lệ.';
+
+ @override
+ String get localHistoryComparisonSimplified =>
+ 'So sánh này rất lớn, khôi phục thay đổi riêng không khả thi.';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return 'Thay đổi phiên bản cũ: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return 'Thay đổi nội dung hiện tại: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => 'Xóa';
+
+ @override
+ String get localHistoryUntitled => 'Không có tiêu đề';
+
+ @override
+ String get localHistoryClearDocument => 'Xóa Lịch Sử Của Tài Liệu Này';
+
+ @override
+ String get localHistoryClearAll => 'Xóa Lịch Sử Địa Phương';
+
+ @override
+ String get localHistoryClearDocumentTitle => 'Xóa lịch sử tài liệu?';
+
+ @override
+ String get localHistoryClearAllTitle => 'Xóa lịch sử địa phương?';
+
+ @override
+ String get localHistoryClearConfirmation =>
+ 'Điều này sẽ xóa vĩnh viễn các phiên bản đã lưu. Hành động này không thể hoàn tác.';
+
+ @override
+ String get localHistoryWarningIndexRebuilt =>
+ 'Chỉ mục Lịch Sử Địa Phương bị hỏng và được xây dựng lại từ các phiên bản còn nguyên vẹn.';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return 'Kho Lịch Sử Địa Phương sử dụng định dạng không được hỗ trợ $version.';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return 'Lịch Sử Địa Phương không khả dụng: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ 'Ghi âm bị tắt hoặc đường dẫn này bị loại trừ. Vui lòng bật lại ghi âm trước khi khôi phục.';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return 'Lịch Sử Địa Phương không thể ghi lại thay đổi đường dẫn: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return 'Lịch sử cục bộ không thể lưu trữ đường dẫn đã xóa: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing =>
+ 'Phiên bản đã chọn bị thiếu hoặc bị hỏng.';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return 'Phiên bản đã chọn không thể đọc: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return 'Lịch sử cục bộ không thể ghi lại phiên bản này: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason – $timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => 'Mốc cơ sở';
+
+ @override
+ String get localHistoryReasonSaved => 'Lưu';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => 'Tự động lưu';
+
+ @override
+ String get localHistoryReasonBeforeReload => 'Trước khi tải lại';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => 'Trước khi vứt';
+
+ @override
+ String get localHistoryReasonBeforeRestore => 'Trước khi khôi phục';
+
+ @override
+ String get localHistoryReasonBeforeDelete => 'Trước khi xóa';
+
+ @override
+ String get localHistoryReasonExternalChange => 'Thay đổi từ bên ngoài';
+
+ @override
+ String get settingsHistory => 'Lịch sử';
+
+ @override
+ String get settingsClipboardHistoryTitle => 'Thu thập lịch sử sao chép';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ 'Giữ nội dung đã sao chép và dán thành công chỉ cho phiên BusyMark này.';
+
+ @override
+ String get settingsLocalHistoryTitle => 'Ghi lại lịch sử cục bộ';
+
+ @override
+ String get settingsLocalHistoryDescription =>
+ 'Lưu các phiên bản tài liệu trên thiết bị để khôi phục và so sánh.';
+
+ @override
+ String get settingsHistoryCheckpoint => 'Khoảng thời gian kiểm tra tự động';
+
+ @override
+ String get settingsHistoryRetention => 'Thời gian lưu';
+
+ @override
+ String get settingsHistoryStorage => 'Dung lượng tối đa';
+
+ @override
+ String get settingsHistoryExcludedPaths => 'Đường dẫn bị loại trừ';
+
+ @override
+ String get settingsHistoryExcludedPathsHint =>
+ 'Một đường dẫn tuyệt đối trên mỗi dòng';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value giây';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value ngày';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field: nhập số từ $minimum đến $maximum.';
diff --git a/lib/l10n/generated/app_localizations_zh.dart b/lib/l10n/generated/app_localizations_zh.dart
index 60422903..2c7851cc 100644
--- a/lib/l10n/generated/app_localizations_zh.dart
+++ b/lib/l10n/generated/app_localizations_zh.dart
@@ -888,6 +888,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get hardLineBreak => '硬换行';
+ @override
+ String get insertBlankLine => '插入空行';
+
@override
String get textStyle => '文本样式';
@@ -1069,7 +1072,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get files => '文件';
@override
- String get toc => '目录';
+ String get toc => '主题';
@override
String get tocActions => '目录操作';
@@ -1487,6 +1490,19 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get sourceSearchInvalidRegex => '无效的正则表达式';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ '不支持零长度的正则表达式匹配(例如 ^、\$ 或仅匹配位置的前瞻断言)。';
+
+ @override
+ String get workspaceSearchIncomplete => '仅显示了部分结果。请显示更多结果或缩小搜索范围。';
+
+ @override
+ String get workspaceSearchShowMore => '显示更多结果';
+
+ @override
+ String get workspaceSearchSkippedFiles => '搜索不完整。以下文件超过 1 MiB 或无法读取:';
+
@override
String get sourceLargeFileFeaturesPaused => '大文件:已暂停语法突出显示和折叠';
@@ -3721,6 +3737,267 @@ class AppLocalizationsZh extends AppLocalizations {
String get exportInvalidCss =>
'请选择可读取的 UTF-8 .css 文件(最大 256 KiB),不得包含导入、资源 URL、HTML、可执行或转义 CSS。';
+ @override
+ String get clipboardHistory => '剪贴板历史';
+
+ @override
+ String get clipboardHistorySearchHint => '搜索剪贴板历史记录';
+
+ @override
+ String get clipboardHistoryDescription => '打开会话本地剪贴板历史。';
+
+ @override
+ String get clipboardCurrent => '当前剪贴板';
+
+ @override
+ String get clipboardCurrentExternal => '来自其他应用程序的当前剪贴板';
+
+ @override
+ String clipboardDestination(String name) {
+ return '粘贴到 $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => '仅存储在本次 BusyMark 会话中。';
+
+ @override
+ String get clipboardNoItems => '剪贴板历史为空。';
+
+ @override
+ String get clipboardNoMatches => '没有匹配此搜索的剪贴板项目。';
+
+ @override
+ String get clipboardDisabled => '剪贴板历史集合在设置中已禁用。';
+
+ @override
+ String get clipboardUnavailable => '当前剪贴板内容不可用或不受支持。';
+
+ @override
+ String get clipboardTooLarge => '此剪贴板内容过大,无法保留。';
+
+ @override
+ String get clipboardPastePlainText => '作为纯文本粘贴';
+
+ @override
+ String get clipboardClearAll => '清除剪贴板历史';
+
+ @override
+ String get clipboardEntryText => '文本';
+
+ @override
+ String get clipboardEntryRichText => '丰富文本';
+
+ @override
+ String get clipboardEntryImage => '图片';
+
+ @override
+ String clipboardOrigin(String name) {
+ return '复制自 $name';
+ }
+
+ @override
+ String get localHistory => '本地历史';
+
+ @override
+ String get localHistoryEllipsis => '本地历史…';
+
+ @override
+ String get localHistoryDescription => '显示当前文档的已保存和自动本地修订。';
+
+ @override
+ String get findLocalHistoryEllipsis => '在本地历史中查找…';
+
+ @override
+ String get findLocalHistoryDescription => '搜索本地历史文档和修订内容。';
+
+ @override
+ String get localHistoryNoDocuments => '尚无文档具有本地历史。';
+
+ @override
+ String get localHistoryNoRevisions => '此文档没有本地历史修订。';
+
+ @override
+ String get localHistoryNoMatches => '没有本地历史修订与此搜索匹配。';
+
+ @override
+ String get localHistorySearchHint => '搜索本地历史记录';
+
+ @override
+ String get localHistorySelectedRevision => '已选择的修订';
+
+ @override
+ String get localHistoryCurrentEditor => '当前编辑器内容';
+
+ @override
+ String get localHistoryCurrentDisk => '磁盘上的当前内容';
+
+ @override
+ String get localHistoryMissingFile => '原始文件丢失。';
+
+ @override
+ String get localHistoryRestoreRevision => '恢复版本';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => '恢复到原始位置';
+
+ @override
+ String get localHistoryRestoreNewLocation => '恢复到新位置…';
+
+ @override
+ String get localHistoryRestoreChange => '恢复更改';
+
+ @override
+ String get localHistoryRestoreUnavailable => '无法恢复此更改,因为比较已过时。';
+
+ @override
+ String get localHistoryComparisonSimplified => '此大型比较已简化。 无法恢复单个更改。';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return '较旧的版本更改: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return '当前内容更改: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => '已删除';
+
+ @override
+ String get localHistoryUntitled => '未命名';
+
+ @override
+ String get localHistoryClearDocument => '清除此文档的历史';
+
+ @override
+ String get localHistoryClearAll => '清除所有本地历史';
+
+ @override
+ String get localHistoryClearDocumentTitle => '清除文档历史?';
+
+ @override
+ String get localHistoryClearAllTitle => '清除所有本地历史?';
+
+ @override
+ String get localHistoryClearConfirmation => '这将永久删除存储的修订。 此操作无法撤销。';
+
+ @override
+ String get localHistoryWarningIndexRebuilt => '本地历史索引已损坏并从完整修订中重建。';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return '此本地历史存储使用不受支持的格式 $version。';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return '本地历史不可用: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ '录音已禁用或此路径已排除。 在恢复之前重新启用录音。';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return '无法记录本地历史路径更改: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return '本地历史记录无法保留已删除的路径: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing => '所选版本丢失或损坏。';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return '无法读取所选版本: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return '本地历史记录无法捕获此版本: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason・$timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => '基线';
+
+ @override
+ String get localHistoryReasonSaved => '已保存';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => '自动检查点';
+
+ @override
+ String get localHistoryReasonBeforeReload => '在重新加载之前';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => '在丢弃之前';
+
+ @override
+ String get localHistoryReasonBeforeRestore => '恢复前';
+
+ @override
+ String get localHistoryReasonBeforeDelete => '删除前';
+
+ @override
+ String get localHistoryReasonExternalChange => '外部修改';
+
+ @override
+ String get settingsHistory => '历史记录';
+
+ @override
+ String get settingsClipboardHistoryTitle => '收集剪贴板历史';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ '只保留本次 BusyMark 会话中已复制并成功粘贴的内容。';
+
+ @override
+ String get settingsLocalHistoryTitle => '记录本地历史';
+
+ @override
+ String get settingsLocalHistoryDescription => '将文档修订存储在此设备上,以便恢复和比较。';
+
+ @override
+ String get settingsHistoryCheckpoint => '自动检查点间隔';
+
+ @override
+ String get settingsHistoryRetention => '保留期限';
+
+ @override
+ String get settingsHistoryStorage => '最大存储';
+
+ @override
+ String get settingsHistoryExcludedPaths => '排除路径';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => '每行一个绝对路径';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value 秒';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value 天';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field:请输入 $minimum 到 $maximum 之间的数字。';
@@ -4609,6 +4886,9 @@ class AppLocalizationsZhCn extends AppLocalizationsZh {
@override
String get hardLineBreak => '硬换行';
+ @override
+ String get insertBlankLine => '插入空行';
+
@override
String get textStyle => '文本样式';
@@ -4790,7 +5070,7 @@ class AppLocalizationsZhCn extends AppLocalizationsZh {
String get files => '文件';
@override
- String get toc => '目录';
+ String get toc => '主题';
@override
String get tocActions => '目录操作';
@@ -5208,6 +5488,19 @@ class AppLocalizationsZhCn extends AppLocalizationsZh {
@override
String get sourceSearchInvalidRegex => '无效的正则表达式';
+ @override
+ String get sourceSearchZeroLengthUnsupported =>
+ '不支持零长度的正则表达式匹配(例如 ^、\$ 或仅匹配位置的前瞻断言)。';
+
+ @override
+ String get workspaceSearchIncomplete => '仅显示了部分结果。请显示更多结果或缩小搜索范围。';
+
+ @override
+ String get workspaceSearchShowMore => '显示更多结果';
+
+ @override
+ String get workspaceSearchSkippedFiles => '搜索不完整。以下文件超过 1 MiB 或无法读取:';
+
@override
String get sourceLargeFileFeaturesPaused => '大文件:已暂停语法突出显示和折叠';
@@ -7442,6 +7735,267 @@ class AppLocalizationsZhCn extends AppLocalizationsZh {
String get exportInvalidCss =>
'请选择可读取的 UTF-8 .css 文件(最大 256 KiB),不得包含导入、资源 URL、HTML、可执行或转义 CSS。';
+ @override
+ String get clipboardHistory => '剪贴板历史';
+
+ @override
+ String get clipboardHistorySearchHint => '搜索剪贴板历史记录';
+
+ @override
+ String get clipboardHistoryDescription => '打开会话本地剪贴板历史。';
+
+ @override
+ String get clipboardCurrent => '当前剪贴板';
+
+ @override
+ String get clipboardCurrentExternal => '来自其他应用程序的当前剪贴板';
+
+ @override
+ String clipboardDestination(String name) {
+ return '粘贴到 $name';
+ }
+
+ @override
+ String get clipboardSessionOnly => '仅存储在本次 BusyMark 会话中。';
+
+ @override
+ String get clipboardNoItems => '剪贴板历史为空。';
+
+ @override
+ String get clipboardNoMatches => '没有匹配此搜索的剪贴板项目。';
+
+ @override
+ String get clipboardDisabled => '剪贴板历史集合在设置中已禁用。';
+
+ @override
+ String get clipboardUnavailable => '当前剪贴板内容不可用或不受支持。';
+
+ @override
+ String get clipboardTooLarge => '此剪贴板内容过大,无法保留。';
+
+ @override
+ String get clipboardPastePlainText => '作为纯文本粘贴';
+
+ @override
+ String get clipboardClearAll => '清除剪贴板历史';
+
+ @override
+ String get clipboardEntryText => '文本';
+
+ @override
+ String get clipboardEntryRichText => '丰富文本';
+
+ @override
+ String get clipboardEntryImage => '图片';
+
+ @override
+ String clipboardOrigin(String name) {
+ return '复制自 $name';
+ }
+
+ @override
+ String get localHistory => '本地历史';
+
+ @override
+ String get localHistoryEllipsis => '本地历史…';
+
+ @override
+ String get localHistoryDescription => '显示当前文档的已保存和自动本地修订。';
+
+ @override
+ String get findLocalHistoryEllipsis => '在本地历史中查找…';
+
+ @override
+ String get findLocalHistoryDescription => '搜索本地历史文档和修订内容。';
+
+ @override
+ String get localHistoryNoDocuments => '尚无文档具有本地历史。';
+
+ @override
+ String get localHistoryNoRevisions => '此文档没有本地历史修订。';
+
+ @override
+ String get localHistoryNoMatches => '没有本地历史修订与此搜索匹配。';
+
+ @override
+ String get localHistorySearchHint => '搜索本地历史记录';
+
+ @override
+ String get localHistorySelectedRevision => '已选择的修订';
+
+ @override
+ String get localHistoryCurrentEditor => '当前编辑器内容';
+
+ @override
+ String get localHistoryCurrentDisk => '磁盘上的当前内容';
+
+ @override
+ String get localHistoryMissingFile => '原始文件丢失。';
+
+ @override
+ String get localHistoryRestoreRevision => '恢复版本';
+
+ @override
+ String get localHistoryRestoreOriginalLocation => '恢复到原始位置';
+
+ @override
+ String get localHistoryRestoreNewLocation => '恢复到新位置…';
+
+ @override
+ String get localHistoryRestoreChange => '恢复更改';
+
+ @override
+ String get localHistoryRestoreUnavailable => '无法恢复此更改,因为比较已过时。';
+
+ @override
+ String get localHistoryComparisonSimplified => '此大型比较已简化。 无法恢复单个更改。';
+
+ @override
+ String localHistoryOlderChange(String text) {
+ return '较旧的版本更改: $text';
+ }
+
+ @override
+ String localHistoryCurrentChange(String text) {
+ return '当前内容更改: $text';
+ }
+
+ @override
+ String get localHistoryDeleted => '已删除';
+
+ @override
+ String get localHistoryUntitled => '未命名';
+
+ @override
+ String get localHistoryClearDocument => '清除此文档的历史';
+
+ @override
+ String get localHistoryClearAll => '清除所有本地历史';
+
+ @override
+ String get localHistoryClearDocumentTitle => '清除文档历史?';
+
+ @override
+ String get localHistoryClearAllTitle => '清除所有本地历史?';
+
+ @override
+ String get localHistoryClearConfirmation => '这将永久删除存储的修订。 此操作无法撤销。';
+
+ @override
+ String get localHistoryWarningIndexRebuilt => '本地历史索引已损坏并从完整修订中重建。';
+
+ @override
+ String localHistoryWarningUnsupportedFormat(String version) {
+ return '此本地历史存储使用不受支持的格式 $version。';
+ }
+
+ @override
+ String localHistoryWarningUnavailable(String detail) {
+ return '本地历史不可用: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRecordingDisabled =>
+ '录音已禁用或此路径已排除。 在恢复之前重新启用录音。';
+
+ @override
+ String localHistoryWarningPathChange(String detail) {
+ return '无法记录本地历史路径更改: $detail';
+ }
+
+ @override
+ String localHistoryWarningDeletedPath(String detail) {
+ return '本地历史记录无法保留已删除的路径: $detail';
+ }
+
+ @override
+ String get localHistoryWarningRevisionMissing => '所选版本丢失或损坏。';
+
+ @override
+ String localHistoryWarningRevisionRead(String detail) {
+ return '无法读取所选版本: $detail';
+ }
+
+ @override
+ String localHistoryWarningCapture(String detail) {
+ return '本地历史记录无法捕获此版本: $detail';
+ }
+
+ @override
+ String localHistoryRevisionAt(String reason, String timestamp) {
+ return '$reason・$timestamp';
+ }
+
+ @override
+ String get localHistoryReasonBaseline => '基线';
+
+ @override
+ String get localHistoryReasonSaved => '已保存';
+
+ @override
+ String get localHistoryReasonAutomaticCheckpoint => '自动检查点';
+
+ @override
+ String get localHistoryReasonBeforeReload => '在重新加载之前';
+
+ @override
+ String get localHistoryReasonBeforeDiscard => '在丢弃之前';
+
+ @override
+ String get localHistoryReasonBeforeRestore => '恢复前';
+
+ @override
+ String get localHistoryReasonBeforeDelete => '删除前';
+
+ @override
+ String get localHistoryReasonExternalChange => '外部修改';
+
+ @override
+ String get settingsHistory => '历史记录';
+
+ @override
+ String get settingsClipboardHistoryTitle => '收集剪贴板历史';
+
+ @override
+ String get settingsClipboardHistoryDescription =>
+ '只保留本次 BusyMark 会话中已复制并成功粘贴的内容。';
+
+ @override
+ String get settingsLocalHistoryTitle => '记录本地历史';
+
+ @override
+ String get settingsLocalHistoryDescription => '将文档修订存储在此设备上,以便恢复和比较。';
+
+ @override
+ String get settingsHistoryCheckpoint => '自动检查点间隔';
+
+ @override
+ String get settingsHistoryRetention => '保留期限';
+
+ @override
+ String get settingsHistoryStorage => '最大存储';
+
+ @override
+ String get settingsHistoryExcludedPaths => '排除路径';
+
+ @override
+ String get settingsHistoryExcludedPathsHint => '每行一个绝对路径';
+
+ @override
+ String settingsSecondsValue(int value) {
+ return '$value 秒';
+ }
+
+ @override
+ String settingsDaysValue(int value) {
+ return '$value 天';
+ }
+
+ @override
+ String settingsMebibytesValue(int value) {
+ return '$value MiB';
+ }
+
@override
String exportInvalidRange(String field, String minimum, String maximum) {
return '$field:请输入 $minimum 到 $maximum 之间的数字。';
diff --git a/lib/src/app/app_metadata.dart b/lib/src/app/app_metadata.dart
index ca81e724..e3a1300e 100644
--- a/lib/src/app/app_metadata.dart
+++ b/lib/src/app/app_metadata.dart
@@ -1 +1 @@
-const busyMarkAppVersion = '0.3.5';
+const busyMarkAppVersion = '0.4.0';
diff --git a/lib/src/app/app_settings.dart b/lib/src/app/app_settings.dart
index cbcc9209..0fa4544e 100644
--- a/lib/src/app/app_settings.dart
+++ b/lib/src/app/app_settings.dart
@@ -86,6 +86,12 @@ class AppSettings {
required this.editorToolbarDirection,
required this.autoSave,
required this.validateOnEdit,
+ required this.clipboardHistoryEnabled,
+ required this.localHistoryRecordingEnabled,
+ required this.localHistoryCheckpointSeconds,
+ required this.localHistoryRetentionDays,
+ required this.localHistoryMaximumStorageMiB,
+ required this.localHistoryExcludedPaths,
required this.aiProviderPreference,
required this.aiOllamaEndpoint,
required this.aiOllamaModel,
@@ -119,6 +125,12 @@ class AppSettings {
editorToolbarDirection: EditorToolbarDirection.horizontal,
autoSave: true,
validateOnEdit: true,
+ clipboardHistoryEnabled: true,
+ localHistoryRecordingEnabled: true,
+ localHistoryCheckpointSeconds: 60,
+ localHistoryRetentionDays: 30,
+ localHistoryMaximumStorageMiB: 512,
+ localHistoryExcludedPaths: [],
aiProviderPreference: AiProviderPreference.disabled,
aiOllamaEndpoint: 'http://127.0.0.1:11434',
aiOllamaModel: '',
@@ -176,6 +188,33 @@ class AppSettings {
autoSave: json['autoSave'] as bool? ?? defaults.autoSave,
validateOnEdit:
json['validateOnEdit'] as bool? ?? defaults.validateOnEdit,
+ clipboardHistoryEnabled:
+ json['clipboardHistoryEnabled'] as bool? ??
+ defaults.clipboardHistoryEnabled,
+ localHistoryRecordingEnabled:
+ json['localHistoryRecordingEnabled'] as bool? ??
+ defaults.localHistoryRecordingEnabled,
+ localHistoryCheckpointSeconds: _boundedInt(
+ json['localHistoryCheckpointSeconds'],
+ defaults.localHistoryCheckpointSeconds,
+ 10,
+ 3600,
+ ),
+ localHistoryRetentionDays: _boundedInt(
+ json['localHistoryRetentionDays'],
+ defaults.localHistoryRetentionDays,
+ 1,
+ 3650,
+ ),
+ localHistoryMaximumStorageMiB: _boundedInt(
+ json['localHistoryMaximumStorageMiB'],
+ defaults.localHistoryMaximumStorageMiB,
+ 16,
+ 4096,
+ ),
+ localHistoryExcludedPaths: _historyExclusionPathListFromJson(
+ json['localHistoryExcludedPaths'],
+ ),
aiProviderPreference: _enumFromName(
AiProviderPreference.values,
json['aiProviderPreference'],
@@ -242,6 +281,12 @@ class AppSettings {
final EditorToolbarDirection editorToolbarDirection;
final bool autoSave;
final bool validateOnEdit;
+ final bool clipboardHistoryEnabled;
+ final bool localHistoryRecordingEnabled;
+ final int localHistoryCheckpointSeconds;
+ final int localHistoryRetentionDays;
+ final int localHistoryMaximumStorageMiB;
+ final List localHistoryExcludedPaths;
final AiProviderPreference aiProviderPreference;
final String aiOllamaEndpoint;
final String aiOllamaModel;
@@ -277,6 +322,12 @@ class AppSettings {
'editorToolbarDirection': editorToolbarDirection.name,
'autoSave': autoSave,
'validateOnEdit': validateOnEdit,
+ 'clipboardHistoryEnabled': clipboardHistoryEnabled,
+ 'localHistoryRecordingEnabled': localHistoryRecordingEnabled,
+ 'localHistoryCheckpointSeconds': localHistoryCheckpointSeconds,
+ 'localHistoryRetentionDays': localHistoryRetentionDays,
+ 'localHistoryMaximumStorageMiB': localHistoryMaximumStorageMiB,
+ 'localHistoryExcludedPaths': localHistoryExcludedPaths,
'aiProviderPreference': aiProviderPreference.name,
'aiOllamaEndpoint': aiOllamaEndpoint,
'aiOllamaModel': aiOllamaModel,
@@ -351,6 +402,12 @@ class AppSettings {
EditorToolbarDirection? editorToolbarDirection,
bool? autoSave,
bool? validateOnEdit,
+ bool? clipboardHistoryEnabled,
+ bool? localHistoryRecordingEnabled,
+ int? localHistoryCheckpointSeconds,
+ int? localHistoryRetentionDays,
+ int? localHistoryMaximumStorageMiB,
+ List? localHistoryExcludedPaths,
AiProviderPreference? aiProviderPreference,
String? aiOllamaEndpoint,
String? aiOllamaModel,
@@ -386,6 +443,18 @@ class AppSettings {
editorToolbarDirection ?? this.editorToolbarDirection,
autoSave: autoSave ?? this.autoSave,
validateOnEdit: validateOnEdit ?? this.validateOnEdit,
+ clipboardHistoryEnabled:
+ clipboardHistoryEnabled ?? this.clipboardHistoryEnabled,
+ localHistoryRecordingEnabled:
+ localHistoryRecordingEnabled ?? this.localHistoryRecordingEnabled,
+ localHistoryCheckpointSeconds:
+ localHistoryCheckpointSeconds ?? this.localHistoryCheckpointSeconds,
+ localHistoryRetentionDays:
+ localHistoryRetentionDays ?? this.localHistoryRetentionDays,
+ localHistoryMaximumStorageMiB:
+ localHistoryMaximumStorageMiB ?? this.localHistoryMaximumStorageMiB,
+ localHistoryExcludedPaths:
+ localHistoryExcludedPaths ?? this.localHistoryExcludedPaths,
aiProviderPreference: aiProviderPreference ?? this.aiProviderPreference,
aiOllamaEndpoint: aiOllamaEndpoint ?? this.aiOllamaEndpoint,
aiOllamaModel: aiOllamaModel ?? this.aiOllamaModel,
@@ -580,6 +649,51 @@ class AppSettingsController extends Notifier {
return _mutate((settings) => settings.copyWith(validateOnEdit: enabled));
}
+ Future setClipboardHistoryEnabled(bool enabled) {
+ return _mutate(
+ (settings) => settings.copyWith(clipboardHistoryEnabled: enabled),
+ );
+ }
+
+ Future setLocalHistoryRecordingEnabled(bool enabled) {
+ return _mutate(
+ (settings) => settings.copyWith(localHistoryRecordingEnabled: enabled),
+ );
+ }
+
+ Future setLocalHistoryCheckpointSeconds(int seconds) {
+ return _mutate(
+ (settings) => settings.copyWith(
+ localHistoryCheckpointSeconds: seconds.clamp(10, 3600),
+ ),
+ );
+ }
+
+ Future setLocalHistoryRetentionDays(int days) {
+ return _mutate(
+ (settings) =>
+ settings.copyWith(localHistoryRetentionDays: days.clamp(1, 3650)),
+ );
+ }
+
+ Future setLocalHistoryMaximumStorageMiB(int mebibytes) {
+ return _mutate(
+ (settings) => settings.copyWith(
+ localHistoryMaximumStorageMiB: mebibytes.clamp(16, 4096),
+ ),
+ );
+ }
+
+ Future setLocalHistoryExcludedPaths(Iterable paths) {
+ final normalized = {
+ for (final path in paths)
+ if (_normalizedHistoryExclusionPath(path) case final value?) value,
+ }.toList()..sort();
+ return _mutate(
+ (settings) => settings.copyWith(localHistoryExcludedPaths: normalized),
+ );
+ }
+
Future setAiProviderPreference(AiProviderPreference preference) {
return _mutate(
(settings) => settings.copyWith(aiProviderPreference: preference),
@@ -863,6 +977,28 @@ List _workspacePathListFromJson(Object? value) {
return paths;
}
+int _boundedInt(Object? value, int fallback, int minimum, int maximum) {
+ final parsed = value is num ? value.toInt() : int.tryParse('$value');
+ return (parsed ?? fallback).clamp(minimum, maximum);
+}
+
+List _historyExclusionPathListFromJson(Object? value) {
+ if (value is! List) return const [];
+ final paths = {
+ for (final item in value)
+ if (_normalizedHistoryExclusionPath(item?.toString()) case final path?)
+ path,
+ }.toList()..sort();
+ return paths;
+}
+
+String? _normalizedHistoryExclusionPath(String? value) {
+ final trimmed = value?.trim();
+ if (trimmed == null || trimmed.isEmpty) return null;
+ if (!p.isAbsolute(trimmed)) return null;
+ return p.normalize(trimmed);
+}
+
List _gitWorkspacePathListFromJson(Object? value) {
if (value is! List) {
return const [];
diff --git a/lib/src/app/busymark_app.dart b/lib/src/app/busymark_app.dart
index 3a8bcaa5..bc24bdf3 100644
--- a/lib/src/app/busymark_app.dart
+++ b/lib/src/app/busymark_app.dart
@@ -11,8 +11,10 @@ import 'package:ubuntu_localizations/ubuntu_localizations.dart';
import '../../l10n/generated/app_localizations.dart';
import '../export/workspace_export_ui.dart';
+import '../clipboard/clipboard_history_controller.dart';
import '../git/application/git_controller.dart';
import '../platform/linux_header_bar_service.dart';
+import '../local_history/local_history_controller.dart';
import '../workspace/workspace_controller.dart';
import '../workspace/workspace_model.dart';
import '../workspace/presentation/welcome_screen.dart';
@@ -52,6 +54,9 @@ final busyMarkCommandRegistryProvider = Provider((
BusyMarkCommandIds.closeTab: const _CloseTabIntent(),
BusyMarkCommandIds.closeAllTabs: const _CloseAllTabsIntent(),
BusyMarkCommandIds.toggleSidebar: const _ToggleSidebarIntent(),
+ BusyMarkCommandIds.clipboardHistory: const _OpenClipboardHistoryIntent(),
+ BusyMarkCommandIds.localHistory: const _OpenLocalHistoryIntent(),
+ BusyMarkCommandIds.findLocalHistory: const _FindLocalHistoryIntent(),
BusyMarkCommandIds.viewEditor: const _DocumentViewModeIntent(
DocumentViewModePreference.editor,
),
@@ -82,6 +87,12 @@ final busyMarkCommandRegistryProvider = Provider((
canExportWorkspace(ref.read(workspaceControllerProvider)),
BusyMarkCommandIds.search: () =>
ref.read(workspaceControllerProvider).workspace != null,
+ BusyMarkCommandIds.clipboardHistory: () =>
+ ref.read(workspaceControllerProvider).workspace != null,
+ BusyMarkCommandIds.localHistory: () =>
+ ref.read(workspaceControllerProvider).workspace != null,
+ BusyMarkCommandIds.findLocalHistory: () =>
+ ref.read(workspaceControllerProvider).workspace != null,
},
);
});
@@ -276,6 +287,35 @@ class BusyMarkApp extends ConsumerWidget {
return null;
},
),
+ _OpenClipboardHistoryIntent:
+ CallbackAction<_OpenClipboardHistoryIntent>(
+ onInvoke: (intent) {
+ ref
+ .read(
+ clipboardHistoryOpenRequestProvider.notifier,
+ )
+ .request();
+ return null;
+ },
+ ),
+ _OpenLocalHistoryIntent:
+ CallbackAction<_OpenLocalHistoryIntent>(
+ onInvoke: (intent) {
+ ref
+ .read(localHistoryOpenRequestProvider.notifier)
+ .request();
+ return null;
+ },
+ ),
+ _FindLocalHistoryIntent:
+ CallbackAction<_FindLocalHistoryIntent>(
+ onInvoke: (intent) {
+ ref
+ .read(localHistoryFindRequestProvider.notifier)
+ .request();
+ return null;
+ },
+ ),
_NextTabIntent: CallbackAction<_NextTabIntent>(
onInvoke: (intent) {
final navigatorContext = rootNavigatorKey.currentContext;
@@ -613,6 +653,13 @@ class BusyMarkApp extends ConsumerWidget {
gitState: gitState,
documentBuffers: workspaceState.documentBuffers,
activeBufferId: workspaceState.activeBufferId,
+ localHistoryRevisionId: ref
+ .read(localHistoryControllerProvider)
+ .selectedRevisionId,
+ localHistoryDocumentName: ref
+ .read(localHistoryControllerProvider)
+ .selectedDocument
+ ?.displayName,
);
if (tabs.length < 2) {
return;
@@ -640,6 +687,13 @@ class BusyMarkApp extends ConsumerWidget {
gitState: gitState,
documentBuffers: workspaceState.documentBuffers,
activeBufferId: workspaceState.activeBufferId,
+ localHistoryRevisionId: ref
+ .read(localHistoryControllerProvider)
+ .selectedRevisionId,
+ localHistoryDocumentName: ref
+ .read(localHistoryControllerProvider)
+ .selectedDocument
+ ?.displayName,
);
final activeIndex = activeWorkspaceTabIndex(tabs);
if (activeIndex < 0) {
@@ -669,6 +723,13 @@ class BusyMarkApp extends ConsumerWidget {
gitState: gitState,
documentBuffers: workspaceState.documentBuffers,
activeBufferId: workspaceState.activeBufferId,
+ localHistoryRevisionId: ref
+ .read(localHistoryControllerProvider)
+ .selectedRevisionId,
+ localHistoryDocumentName: ref
+ .read(localHistoryControllerProvider)
+ .selectedDocument
+ ?.displayName,
).isEmpty) {
return;
}
@@ -686,15 +747,19 @@ class BusyMarkApp extends ConsumerWidget {
final gitController = ref.read(gitControllerProvider.notifier);
switch (tab.kind) {
case WorkspaceTabKind.file:
+ ref.read(localHistoryControllerProvider.notifier).clearComparison();
await ref
.read(workspaceControllerProvider.notifier)
.activateDocumentBuffer(tab.bufferId!);
gitController.deactivateDiffFile();
case WorkspaceTabKind.gitDiff:
+ ref.read(localHistoryControllerProvider.notifier).clearComparison();
if (tab.path.isEmpty) {
return;
}
await gitController.activateDiffFile(tab.path);
+ case WorkspaceTabKind.localHistory:
+ return;
}
}
@@ -712,6 +777,8 @@ class BusyMarkApp extends ConsumerWidget {
} else {
gitController.closeDiffFile(tab.path);
}
+ case WorkspaceTabKind.localHistory:
+ ref.read(localHistoryControllerProvider.notifier).clearComparison();
}
}
@@ -817,9 +884,6 @@ class BusyMarkApp extends ConsumerWidget {
back: material.backButtonTooltip,
backShortcut: shortcut(BusyMarkCommandIds.back),
save: label(BusyMarkCommandIds.save),
- export: label(BusyMarkCommandIds.export),
- exportShortcut: shortcut(BusyMarkCommandIds.export),
- exportGtkAccelerator: accelerator(BusyMarkCommandIds.export),
fullScreen: label(BusyMarkCommandIds.fullScreen),
fullScreenShortcut: shortcut(BusyMarkCommandIds.fullScreen),
fullScreenGtkAccelerator: accelerator(BusyMarkCommandIds.fullScreen),
@@ -1148,6 +1212,18 @@ class _OpenSearchIntent extends Intent {
const _OpenSearchIntent();
}
+class _OpenClipboardHistoryIntent extends Intent {
+ const _OpenClipboardHistoryIntent();
+}
+
+class _OpenLocalHistoryIntent extends Intent {
+ const _OpenLocalHistoryIntent();
+}
+
+class _FindLocalHistoryIntent extends Intent {
+ const _FindLocalHistoryIntent();
+}
+
class _ToggleSidebarIntent extends Intent {
const _ToggleSidebarIntent();
}
diff --git a/lib/src/app/busymark_design.dart b/lib/src/app/busymark_design.dart
index 6ca84c9b..ad18fd0d 100644
--- a/lib/src/app/busymark_design.dart
+++ b/lib/src/app/busymark_design.dart
@@ -2430,6 +2430,265 @@ class BusyMarkSidebarNavigationTile extends StatelessWidget {
}
}
+typedef BusyMarkSidebarRecordMenuBuilder =
+ List> Function(BuildContext context);
+
+/// The interactive record surface used by persistent sidebar lists.
+///
+/// This is the shared form of the Recent-workspace row: a transparent rounded
+/// surface with native hover treatment and, when supplied, one overflow and
+/// context-menu action model. Callers can use the standard title/subtitle
+/// identity layout or provide domain-specific [content].
+class BusyMarkSidebarRecordRow extends StatefulWidget {
+ const BusyMarkSidebarRecordRow({
+ super.key,
+ required this.icon,
+ required this.onTap,
+ this.title,
+ this.subtitle,
+ this.content,
+ this.selected = false,
+ this.onDoubleTap,
+ this.semanticsLabel,
+ this.tooltip,
+ this.menuTooltip,
+ this.menuItemsBuilder,
+ this.onMenuSelected,
+ this.onMenuOpening,
+ }) : assert(
+ (title == null) != (content == null),
+ 'Supply either the standard title or custom content.',
+ ),
+ assert(
+ (menuItemsBuilder == null) == (onMenuSelected == null),
+ 'Menu items and selection handling must be supplied together.',
+ ),
+ assert(
+ menuItemsBuilder == null || menuTooltip != null,
+ 'Rows with a menu need a tooltip for its trigger.',
+ );
+
+ final IconData icon;
+ final String? title;
+ final String? subtitle;
+ final Widget? content;
+ final bool selected;
+ final VoidCallback onTap;
+ final VoidCallback? onDoubleTap;
+ final String? semanticsLabel;
+ final String? tooltip;
+ final String? menuTooltip;
+ final BusyMarkSidebarRecordMenuBuilder? menuItemsBuilder;
+ final ValueChanged? onMenuSelected;
+ final VoidCallback? onMenuOpening;
+
+ @override
+ State> createState() =>
+ _BusyMarkSidebarRecordRowState();
+}
+
+class _BusyMarkSidebarRecordRowState
+ extends State> {
+ final _rowKey = GlobalKey();
+ late final FocusNode _rowFocusNode;
+ var _contextMenuOpen = false;
+ DateTime? _lastTapAt;
+
+ bool get _hasMenu =>
+ widget.menuItemsBuilder != null && widget.onMenuSelected != null;
+
+ @override
+ void initState() {
+ super.initState();
+ _rowFocusNode = FocusNode(debugLabel: 'BusyMark sidebar record row');
+ }
+
+ @override
+ void dispose() {
+ _rowFocusNode.dispose();
+ super.dispose();
+ }
+
+ KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) {
+ if (!_hasMenu || !isBusyMarkContextMenuKeyEvent(event)) {
+ return KeyEventResult.ignored;
+ }
+ unawaited(_showContextMenu());
+ return KeyEventResult.handled;
+ }
+
+ void _handleTap() {
+ final onDoubleTap = widget.onDoubleTap;
+ if (onDoubleTap == null) {
+ widget.onTap();
+ return;
+ }
+ final now = DateTime.now();
+ final previous = _lastTapAt;
+ final doubleTap =
+ previous != null && now.difference(previous) <= kDoubleTapTimeout;
+ _lastTapAt = doubleTap ? null : now;
+ widget.onTap();
+ if (doubleTap) onDoubleTap();
+ }
+
+ Future _showContextMenu([Offset? position]) async {
+ if (!_hasMenu || _contextMenuOpen) return;
+ final rowContext = _rowKey.currentContext;
+ if (rowContext == null) return;
+
+ widget.onMenuOpening?.call();
+ setState(() => _contextMenuOpen = true);
+ T? action;
+ try {
+ final items = widget.menuItemsBuilder!(rowContext);
+ action = position == null
+ ? await showBusyMarkMenu(
+ context: rowContext,
+ anchorContext: rowContext,
+ items: items,
+ focusFirst: true,
+ )
+ : await showBusyMarkContextMenu(
+ rowContext,
+ position,
+ items: items,
+ );
+ } finally {
+ if (mounted) setState(() => _contextMenuOpen = false);
+ }
+ if (mounted && action != null) widget.onMenuSelected!(action);
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final colors = BusyMarkSurfaceColors.of(context);
+ final subtitle = widget.subtitle?.trim();
+ final customContent = widget.content;
+ final menuItems = widget.menuItemsBuilder?.call(context);
+ final trailing = menuItems == null || menuItems.isEmpty
+ ? null
+ : BusyMarkMenuButton(
+ tooltip: widget.menuTooltip!,
+ items: menuItems,
+ onSelected: widget.onMenuSelected!,
+ triggerBuilder: (context, trigger) => trigger.anchor(
+ child: BusyMarkCompactIconButton(
+ tooltip: widget.menuTooltip!,
+ icon: BusyMarkGlyphs.menuVertical,
+ focusNode: trigger.focusNode,
+ onPressed: trigger.onPressed == null
+ ? null
+ : () {
+ widget.onMenuOpening?.call();
+ trigger.onPressed!();
+ },
+ ),
+ ),
+ );
+ final content = ConstrainedBox(
+ constraints: BoxConstraints(
+ minHeight: BusyMarkSizes.sidebarTreeRowHeight * 2,
+ ),
+ child: Row(
+ children: [
+ const SizedBox(width: BusyMarkSpacing.sm),
+ Icon(
+ widget.icon,
+ size: BusyMarkSizes.iconSm,
+ color: colors.mutedForeground,
+ ),
+ const SizedBox(width: BusyMarkSpacing.sm),
+ Expanded(
+ child: Padding(
+ padding: customContent == null
+ ? EdgeInsets.zero
+ : const EdgeInsets.symmetric(vertical: BusyMarkSpacing.sm),
+ child:
+ customContent ??
+ Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ widget.title!,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(context).textTheme.bodyMedium?.copyWith(
+ color: colors.foreground,
+ ),
+ ),
+ if (subtitle != null && subtitle.isNotEmpty) ...[
+ const SizedBox(height: BusyMarkSpacing.xxs),
+ Text(
+ subtitle,
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(context).textTheme.bodySmall
+ ?.copyWith(color: colors.mutedForeground),
+ ),
+ ],
+ ],
+ ),
+ ),
+ ),
+ if (trailing != null) ...[
+ const SizedBox(width: BusyMarkSpacing.xs),
+ trailing,
+ ],
+ const SizedBox(width: BusyMarkSpacing.sm),
+ ],
+ ),
+ );
+ final row = KeyedSubtree(
+ key: _rowKey,
+ child: Focus(
+ onKeyEvent: _handleKeyEvent,
+ child: Padding(
+ padding: const EdgeInsets.symmetric(
+ vertical: BusyMarkStroke.hairline,
+ ),
+ child: Material(
+ color: widget.selected || _contextMenuOpen
+ ? busyMarkRowHoverColor(context)
+ : BusyMarkLinuxPalette.transparent,
+ borderRadius: BorderRadius.circular(BusyMarkRadius.md),
+ clipBehavior: Clip.antiAlias,
+ child: InkWell(
+ hoverColor: busyMarkRowHoverColor(context),
+ focusNode: _rowFocusNode,
+ onTap: _handleTap,
+ onSecondaryTapUp: !_hasMenu
+ ? null
+ : (details) {
+ _rowFocusNode.requestFocus();
+ unawaited(_showContextMenu(details.globalPosition));
+ },
+ child: content,
+ ),
+ ),
+ ),
+ ),
+ );
+ final semanticRow = Semantics(
+ container: true,
+ selected: widget.selected,
+ button: true,
+ label: widget.semanticsLabel,
+ onTap: _handleTap,
+ child: row,
+ );
+ final tooltip = widget.tooltip;
+ return tooltip == null || tooltip.isEmpty
+ ? semanticRow
+ : Tooltip(
+ message: tooltip,
+ excludeFromSemantics: true,
+ child: semanticRow,
+ );
+ }
+}
+
class BusyMarkGroupedList extends StatelessWidget {
const BusyMarkGroupedList({
super.key,
diff --git a/lib/src/app/busymark_dialogs.dart b/lib/src/app/busymark_dialogs.dart
index 1266c196..7a0c939d 100644
--- a/lib/src/app/busymark_dialogs.dart
+++ b/lib/src/app/busymark_dialogs.dart
@@ -1004,6 +1004,20 @@ void showLegacyBusyMarkKeyboardShortcutsDialog(BuildContext context) {
BusyMarkSidebarShortcutLabels.git,
),
),
+ BusyMarkActionRow(
+ title: context.l10n.localHistoryEllipsis,
+ leading: const Icon(BusyMarkGlyphs.documentHistory),
+ trailing: const _KeyboardShortcutBadge(
+ BusyMarkSidebarShortcutLabels.localHistory,
+ ),
+ ),
+ BusyMarkActionRow(
+ title: context.l10n.clipboardHistory,
+ leading: const Icon(BusyMarkGlyphs.copy),
+ trailing: const _KeyboardShortcutBadge(
+ BusyMarkSidebarShortcutLabels.clipboardHistory,
+ ),
+ ),
BusyMarkActionRow(
title: context.l10n.delete,
subtitle: context.l10n.shortcutDeleteTreeItemDescription,
@@ -1599,6 +1613,13 @@ class _SyntaxReferenceDialogState extends State<_SyntaxReferenceDialog> {
scope: l10n.markdown,
documentationUri: _htmlDocumentationUri,
),
+ _SyntaxReferenceEntry(
+ title: l10n.insertBlankLine,
+ example: 'First line\n
\n
\nSecond line',
+ identifiers: 'br',
+ scope: l10n.markdown,
+ documentationUri: _htmlDocumentationUri,
+ ),
],
footer: _SyntaxReferenceNote(
items: [
diff --git a/lib/src/app/busymark_glyphs.dart b/lib/src/app/busymark_glyphs.dart
index 056ad1d1..3a5b4805 100644
--- a/lib/src/app/busymark_glyphs.dart
+++ b/lib/src/app/busymark_glyphs.dart
@@ -10,6 +10,7 @@ abstract final class BusyMarkGlyphs {
static const IconData ai = YaruIcons.star_filled;
static const IconData appearance = YaruIcons.desktop_appearance;
static const IconData blockquote = YaruIcons.chat_text;
+ static const IconData blankLine = YaruIcons.insert_text;
static const IconData bold = YaruIcons.bold;
static const IconData branch = Icons.fork_right;
static const IconData category = YaruIcons.tag;
diff --git a/lib/src/app/busymark_main_menu.dart b/lib/src/app/busymark_main_menu.dart
index 1e6a842e..b7190eea 100644
--- a/lib/src/app/busymark_main_menu.dart
+++ b/lib/src/app/busymark_main_menu.dart
@@ -10,8 +10,6 @@ import 'localization.dart';
import 'window_control_service.dart';
enum BusyMarkMainMenuAction {
- export,
- generateMarkdownToc,
fullScreen,
settings,
keyboardShortcuts,
@@ -22,18 +20,9 @@ enum BusyMarkMainMenuAction {
}
class BusyMarkMainMenuButton extends ConsumerWidget {
- const BusyMarkMainMenuButton({
- super.key,
- required this.onSelected,
- this.canExportPdf = false,
- this.canExportHtml = false,
- this.canGenerateMarkdownToc = false,
- });
+ const BusyMarkMainMenuButton({super.key, required this.onSelected});
final ValueChanged onSelected;
- final bool canExportPdf;
- final bool canExportHtml;
- final bool canGenerateMarkdownToc;
@override
Widget build(BuildContext context, WidgetRef ref) {
@@ -49,19 +38,6 @@ class BusyMarkMainMenuButton extends ConsumerWidget {
tooltip: l10n.mainMenu,
icon: BusyMarkGlyphs.menuVertical,
itemBuilder: (context) => [
- BusyMarkPopupMenuItem(
- value: BusyMarkMainMenuAction.export,
- label: command(BusyMarkCommandIds.export).label(context),
- icon: BusyMarkGlyphs.exportPdf,
- shortcut: command(BusyMarkCommandIds.export).shortcut?.label,
- enabled: canExportPdf || canExportHtml,
- ),
- BusyMarkPopupMenuItem(
- value: BusyMarkMainMenuAction.generateMarkdownToc,
- label: l10n.generateOrUpdateMarkdownToc,
- icon: BusyMarkGlyphs.orderedList,
- enabled: canGenerateMarkdownToc,
- ),
BusyMarkPopupMenuItem(
value: BusyMarkMainMenuAction.fullScreen,
label: command(BusyMarkCommandIds.fullScreen).label(context),
@@ -110,7 +86,6 @@ class BusyMarkMainMenuButton extends ConsumerWidget {
],
onSelected: (action) {
final commandId = switch (action) {
- BusyMarkMainMenuAction.export => BusyMarkCommandIds.export,
BusyMarkMainMenuAction.fullScreen => BusyMarkCommandIds.fullScreen,
BusyMarkMainMenuAction.settings => BusyMarkCommandIds.settings,
BusyMarkMainMenuAction.keyboardShortcuts =>
@@ -119,7 +94,6 @@ class BusyMarkMainMenuButton extends ConsumerWidget {
BusyMarkCommandIds.commandPalette,
BusyMarkMainMenuAction.syntaxReference =>
BusyMarkCommandIds.syntaxReference,
- BusyMarkMainMenuAction.generateMarkdownToc ||
BusyMarkMainMenuAction.reportIssue ||
BusyMarkMainMenuAction.aboutBusyMark => null,
};
diff --git a/lib/src/app/busymark_search_field.dart b/lib/src/app/busymark_search_field.dart
index 6a4eb72b..97970af3 100644
--- a/lib/src/app/busymark_search_field.dart
+++ b/lib/src/app/busymark_search_field.dart
@@ -2,10 +2,12 @@ import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:yaru/yaru.dart';
-/// Flutter fallback for BusyMark's native Linux search entry.
+/// Flutter counterpart to BusyMark's native Linux `GtkSearchEntry`.
///
-/// Linux header bars use `GtkSearchEntry`. Flutter-owned layouts delegate
-/// geometry, icons, focus presentation, and clear behavior to Yaru.
+/// The native entry cannot be embedded in Flutter-owned layouts. This uses the
+/// Yaru input theme shared by the rest of the application instead of
+/// [YaruSearchField], whose borderless pill geometry is intended for Flutter
+/// title bars rather than GTK-style entries inside a sidebar.
class BusyMarkSearchField extends StatefulWidget {
const BusyMarkSearchField({
super.key,
@@ -24,7 +26,7 @@ class BusyMarkSearchField extends StatefulWidget {
final String? hintText;
final bool autofocus;
- /// Increment this value to focus the Yaru-owned text entry again.
+ /// Increment this value to focus the themed text entry again.
final int focusRequest;
final ValueChanged? onChanged;
@@ -38,25 +40,26 @@ class BusyMarkSearchField extends StatefulWidget {
}
class _BusyMarkSearchFieldState extends State {
- final _focusScopeNode = FocusScopeNode(
- debugLabel: 'BusyMarkSearchField scope',
- );
- final _yaruKeyboardFocusNode = FocusNode(
- debugLabel: 'BusyMarkSearchField keyboard listener',
- skipTraversal: true,
- );
+ late TextEditingController _controller;
+ late bool _textIsEmpty;
+ final _focusNode = FocusNode(debugLabel: 'BusyMarkSearchField text entry');
@override
void initState() {
super.initState();
- if (widget.autofocus) {
- _requestTextFocus();
- }
+ _attachController();
}
@override
void didUpdateWidget(covariant BusyMarkSearchField oldWidget) {
super.didUpdateWidget(oldWidget);
+ if (oldWidget.controller != widget.controller) {
+ _controller.removeListener(_handleControllerChanged);
+ if (oldWidget.controller == null) {
+ _controller.dispose();
+ }
+ _attachController();
+ }
if (oldWidget.focusRequest != widget.focusRequest) {
_requestTextFocus();
}
@@ -64,50 +67,95 @@ class _BusyMarkSearchFieldState extends State {
@override
void dispose() {
- _focusScopeNode.dispose();
- _yaruKeyboardFocusNode.dispose();
+ _controller.removeListener(_handleControllerChanged);
+ if (widget.controller == null) {
+ _controller.dispose();
+ }
+ _focusNode.dispose();
super.dispose();
}
+ void _attachController() {
+ _controller = widget.controller ?? TextEditingController();
+ _textIsEmpty = _controller.text.isEmpty;
+ _controller.addListener(_handleControllerChanged);
+ }
+
+ void _handleControllerChanged() {
+ final textIsEmpty = _controller.text.isEmpty;
+ if (textIsEmpty != _textIsEmpty) {
+ setState(() => _textIsEmpty = textIsEmpty);
+ }
+ }
+
void _requestTextFocus() {
WidgetsBinding.instance.addPostFrameCallback((_) {
- if (!mounted) {
- return;
- }
- for (final node in _focusScopeNode.traversalDescendants) {
- if (node.canRequestFocus) {
- node.requestFocus();
- return;
- }
+ if (mounted) {
+ _focusNode.requestFocus();
}
});
}
+ void _clear() {
+ final hadText = _controller.text.isNotEmpty;
+ final onClear = widget.onClear;
+ if (onClear != null) {
+ onClear();
+ }
+ _controller.clear();
+ if (onClear == null && hadText) {
+ // TextField does not report programmatic controller changes through
+ // onChanged, so keep onChanged-only search owners in sync.
+ widget.onChanged?.call('');
+ }
+ }
+
@override
Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ final clearLabel =
+ widget.clearButtonSemanticLabel ??
+ MaterialLocalizations.of(context).clearButtonTooltip;
return Focus(
- onKeyEvent: (node, event) {
+ onKeyEvent: (_, event) {
if (event is KeyDownEvent &&
- event.logicalKey == LogicalKeyboardKey.escape &&
- widget.onEscape != null) {
- widget.onEscape!();
+ event.logicalKey == LogicalKeyboardKey.escape) {
+ _clear();
+ widget.onEscape?.call();
return KeyEventResult.handled;
}
return KeyEventResult.ignored;
},
- child: FocusScope(
- node: _focusScopeNode,
- child: YaruSearchField(
- controller: widget.controller,
- focusNode: _yaruKeyboardFocusNode,
- hintText: widget.hintText,
+ child: SizedBox(
+ height: kYaruTitleBarItemHeight,
+ child: TextField(
+ controller: _controller,
+ focusNode: _focusNode,
autofocus: widget.autofocus,
+ textInputAction: TextInputAction.search,
+ textAlignVertical: TextAlignVertical.center,
+ cursorWidth: 1,
onChanged: widget.onChanged,
onSubmitted: widget.onSubmitted,
- onClear: widget.onClear,
- clearIconSemanticLabel:
- widget.clearButtonSemanticLabel ??
- MaterialLocalizations.of(context).clearButtonTooltip,
+ decoration: InputDecoration(
+ hintText: widget.hintText,
+ filled: true,
+ fillColor: theme.colorScheme.surface,
+ prefixIcon: const Icon(YaruIcons.search),
+ prefixIconConstraints: const BoxConstraints.tightFor(
+ width: kYaruTitleBarItemHeight,
+ ),
+ suffixIcon: _textIsEmpty
+ ? null
+ : IconButton(
+ tooltip: clearLabel,
+ onPressed: _clear,
+ icon: const Icon(YaruIcons.edit_clear),
+ ),
+ suffixIconConstraints: const BoxConstraints.tightFor(
+ width: kYaruTitleBarItemHeight,
+ ),
+ ),
),
),
);
diff --git a/lib/src/app/busymark_shortcuts.dart b/lib/src/app/busymark_shortcuts.dart
index 94e814f4..7d9e628d 100644
--- a/lib/src/app/busymark_shortcuts.dart
+++ b/lib/src/app/busymark_shortcuts.dart
@@ -544,6 +544,7 @@ abstract final class BusyMarkEditorShortcuts {
static const blockquoteLabel = 'Ctrl+Shift+Q';
static const codeBlockLabel = 'Ctrl+Shift+K';
static const imageLabel = 'Ctrl+Shift+I';
+ static const thematicBreakLabel = 'Ctrl+Shift+-';
static const hardLineBreakLabel = 'Shift+Enter';
static const refineWithAi = BusyMarkShortcutDefinition(
@@ -702,6 +703,14 @@ abstract final class BusyMarkEditorShortcuts {
shift: true,
),
);
+ static const thematicBreak = BusyMarkShortcutDefinition(
+ label: thematicBreakLabel,
+ activator: SingleActivator(
+ LogicalKeyboardKey.minus,
+ control: true,
+ shift: true,
+ ),
+ );
static const hardLineBreak = BusyMarkShortcutDefinition(
label: hardLineBreakLabel,
activator: SingleActivator(LogicalKeyboardKey.enter, shift: true),
@@ -731,6 +740,7 @@ abstract final class BusyMarkEditorShortcuts {
BusyMarkEditorShortcutAction.blockquote: blockquote,
BusyMarkEditorShortcutAction.codeBlock: codeBlock,
BusyMarkEditorShortcutAction.image: image,
+ BusyMarkEditorShortcutAction.thematicBreak: thematicBreak,
BusyMarkEditorShortcutAction.hardLineBreak: hardLineBreak,
};
}
@@ -762,6 +772,7 @@ abstract final class BusyMarkEditorShortcutLabels {
static const blockquote = BusyMarkEditorShortcuts.blockquoteLabel;
static const codeBlock = BusyMarkEditorShortcuts.codeBlockLabel;
static const image = BusyMarkEditorShortcuts.imageLabel;
+ static const thematicBreak = BusyMarkEditorShortcuts.thematicBreakLabel;
static const hardLineBreak = BusyMarkEditorShortcuts.hardLineBreakLabel;
}
@@ -833,11 +844,20 @@ abstract final class BusyMarkEditorShortcutActivators {
static ShortcutActivator get codeBlock =>
BusyMarkEditorShortcuts.codeBlock.activator;
static ShortcutActivator get image => BusyMarkEditorShortcuts.image.activator;
+ static ShortcutActivator get thematicBreak =>
+ BusyMarkEditorShortcuts.thematicBreak.activator;
static ShortcutActivator get hardLineBreak =>
BusyMarkEditorShortcuts.hardLineBreak.activator;
}
-enum BusyMarkSidebarShortcutAction { files, toc, outline, git }
+enum BusyMarkSidebarShortcutAction {
+ files,
+ toc,
+ outline,
+ git,
+ localHistory,
+ clipboardHistory,
+}
abstract final class BusyMarkSidebarShortcuts {
const BusyMarkSidebarShortcuts._();
@@ -846,6 +866,8 @@ abstract final class BusyMarkSidebarShortcuts {
static const tocLabel = 'Ctrl+2';
static const outlineLabel = 'Ctrl+3';
static const gitLabel = 'Ctrl+4';
+ static const localHistoryLabel = 'Ctrl+5';
+ static const clipboardHistoryLabel = 'Ctrl+6';
static const toggleSidebar = BusyMarkAppShortcuts.toggleSidebar;
static const files = BusyMarkShortcutDefinition(
@@ -864,6 +886,14 @@ abstract final class BusyMarkSidebarShortcuts {
label: gitLabel,
activator: SingleActivator(LogicalKeyboardKey.digit4, control: true),
);
+ static const localHistory = BusyMarkShortcutDefinition(
+ label: localHistoryLabel,
+ activator: SingleActivator(LogicalKeyboardKey.digit5, control: true),
+ );
+ static const clipboardHistory = BusyMarkShortcutDefinition(
+ label: clipboardHistoryLabel,
+ activator: SingleActivator(LogicalKeyboardKey.digit6, control: true),
+ );
static const definitions =
{
@@ -871,6 +901,8 @@ abstract final class BusyMarkSidebarShortcuts {
BusyMarkSidebarShortcutAction.toc: toc,
BusyMarkSidebarShortcutAction.outline: outline,
BusyMarkSidebarShortcutAction.git: git,
+ BusyMarkSidebarShortcutAction.localHistory: localHistory,
+ BusyMarkSidebarShortcutAction.clipboardHistory: clipboardHistory,
};
}
@@ -882,6 +914,9 @@ abstract final class BusyMarkSidebarShortcutLabels {
static const toc = BusyMarkSidebarShortcuts.tocLabel;
static const outline = BusyMarkSidebarShortcuts.outlineLabel;
static const git = BusyMarkSidebarShortcuts.gitLabel;
+ static const localHistory = BusyMarkSidebarShortcuts.localHistoryLabel;
+ static const clipboardHistory =
+ BusyMarkSidebarShortcuts.clipboardHistoryLabel;
}
abstract final class BusyMarkSidebarShortcutActivators {
@@ -895,6 +930,10 @@ abstract final class BusyMarkSidebarShortcutActivators {
static ShortcutActivator get outline =>
BusyMarkSidebarShortcuts.outline.activator;
static ShortcutActivator get git => BusyMarkSidebarShortcuts.git.activator;
+ static ShortcutActivator get localHistory =>
+ BusyMarkSidebarShortcuts.localHistory.activator;
+ static ShortcutActivator get clipboardHistory =>
+ BusyMarkSidebarShortcuts.clipboardHistory.activator;
static BusyMarkSidebarShortcutAction? actionForKeyEvent(
KeyEvent event,
@@ -918,6 +957,10 @@ abstract final class BusyMarkSidebarShortcutActivators {
LogicalKeyboardKey.numpad3 => BusyMarkSidebarShortcutAction.outline,
LogicalKeyboardKey.digit4 ||
LogicalKeyboardKey.numpad4 => BusyMarkSidebarShortcutAction.git,
+ LogicalKeyboardKey.digit5 ||
+ LogicalKeyboardKey.numpad5 => BusyMarkSidebarShortcutAction.localHistory,
+ LogicalKeyboardKey.digit6 || LogicalKeyboardKey.numpad6 =>
+ BusyMarkSidebarShortcutAction.clipboardHistory,
_ => null,
};
}
diff --git a/lib/src/app/command_registry.dart b/lib/src/app/command_registry.dart
index 3813b741..33766699 100644
--- a/lib/src/app/command_registry.dart
+++ b/lib/src/app/command_registry.dart
@@ -276,6 +276,9 @@ abstract final class BusyMarkCommandIds {
static const sidebarToc = 'sidebar.toc';
static const sidebarOutline = 'sidebar.outline';
static const sidebarGit = 'sidebar.git';
+ static const clipboardHistory = 'sidebar.clipboardHistory';
+ static const localHistory = 'history.local';
+ static const findLocalHistory = 'history.findLocal';
static const treeDeleteSelection = 'tree.deleteSelection';
}
@@ -365,11 +368,17 @@ abstract final class BusyMarkCommandCatalog {
),
for (final action in BusyMarkSidebarShortcutAction.values)
command(
- id: 'sidebar.${action.name}',
+ id: _sidebarId(action),
label: (context) => _sidebarLabel(context, action),
category: (context) => context.l10n.shortcutGroupSidebar,
- scope: BusyMarkCommandScope.sidebar,
+ scope: switch (action) {
+ BusyMarkSidebarShortcutAction.localHistory ||
+ BusyMarkSidebarShortcutAction.clipboardHistory =>
+ BusyMarkCommandScope.application,
+ _ => BusyMarkCommandScope.sidebar,
+ },
shortcut: BusyMarkSidebarShortcuts.definitions[action],
+ description: (context) => _sidebarDescription(context, action),
),
for (final action in BusyMarkTreeShortcutAction.values)
command(
@@ -381,6 +390,13 @@ abstract final class BusyMarkCommandCatalog {
description: (context) =>
context.l10n.shortcutDeleteTreeItemDescription,
),
+ command(
+ id: BusyMarkCommandIds.findLocalHistory,
+ label: (context) => context.l10n.findLocalHistoryEllipsis,
+ category: (context) => context.l10n.shortcutGroupSidebar,
+ scope: BusyMarkCommandScope.application,
+ description: (context) => context.l10n.findLocalHistoryDescription,
+ ),
];
return BusyMarkCommandRegistry(commands);
}
@@ -629,6 +645,34 @@ abstract final class BusyMarkCommandCatalog {
BusyMarkSidebarShortcutAction.toc => context.l10n.toc,
BusyMarkSidebarShortcutAction.outline => context.l10n.outline,
BusyMarkSidebarShortcutAction.git => context.l10n.git,
+ BusyMarkSidebarShortcutAction.localHistory =>
+ context.l10n.localHistoryEllipsis,
+ BusyMarkSidebarShortcutAction.clipboardHistory =>
+ context.l10n.clipboardHistory,
+ };
+
+ static String _sidebarId(BusyMarkSidebarShortcutAction action) =>
+ switch (action) {
+ BusyMarkSidebarShortcutAction.files => BusyMarkCommandIds.sidebarFiles,
+ BusyMarkSidebarShortcutAction.toc => BusyMarkCommandIds.sidebarToc,
+ BusyMarkSidebarShortcutAction.outline =>
+ BusyMarkCommandIds.sidebarOutline,
+ BusyMarkSidebarShortcutAction.git => BusyMarkCommandIds.sidebarGit,
+ BusyMarkSidebarShortcutAction.localHistory =>
+ BusyMarkCommandIds.localHistory,
+ BusyMarkSidebarShortcutAction.clipboardHistory =>
+ BusyMarkCommandIds.clipboardHistory,
+ };
+
+ static String? _sidebarDescription(
+ BuildContext context,
+ BusyMarkSidebarShortcutAction action,
+ ) => switch (action) {
+ BusyMarkSidebarShortcutAction.localHistory =>
+ context.l10n.localHistoryDescription,
+ BusyMarkSidebarShortcutAction.clipboardHistory =>
+ context.l10n.clipboardHistoryDescription,
+ _ => null,
};
static BuildContext? get _focusedContext =>
diff --git a/lib/src/clipboard/clipboard_history_controller.dart b/lib/src/clipboard/clipboard_history_controller.dart
new file mode 100644
index 00000000..e9fe9cce
--- /dev/null
+++ b/lib/src/clipboard/clipboard_history_controller.dart
@@ -0,0 +1,292 @@
+import 'dart:async';
+import 'dart:io';
+import 'dart:typed_data';
+
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+import 'package:flutter_riverpod/legacy.dart';
+import 'package:path/path.dart' as p;
+import 'package:uuid/uuid.dart';
+
+import '../app/app_settings.dart';
+import '../assets/asset_input_service.dart';
+import '../platform/rich_clipboard_service.dart';
+import 'clipboard_insertion.dart';
+import 'clipboard_models.dart';
+
+class ClipboardHistoryState {
+ const ClipboardHistoryState({
+ this.entries = const [],
+ this.currentClipboard,
+ this.refreshing = false,
+ this.clipboardAvailable = true,
+ this.lastRetentionResult,
+ });
+
+ final List entries;
+ final BusyMarkClipboardPayload? currentClipboard;
+ final bool refreshing;
+ final bool clipboardAvailable;
+ final ClipboardRetentionResult? lastRetentionResult;
+
+ int get retainedBytes =>
+ entries.fold(0, (total, entry) => total + entry.accountedBytes);
+
+ ClipboardHistoryState copyWith({
+ List? entries,
+ Object? currentClipboard = _unset,
+ bool? refreshing,
+ bool? clipboardAvailable,
+ Object? lastRetentionResult = _unset,
+ }) {
+ return ClipboardHistoryState(
+ entries: entries ?? this.entries,
+ currentClipboard: identical(currentClipboard, _unset)
+ ? this.currentClipboard
+ : currentClipboard as BusyMarkClipboardPayload?,
+ refreshing: refreshing ?? this.refreshing,
+ clipboardAvailable: clipboardAvailable ?? this.clipboardAvailable,
+ lastRetentionResult: identical(lastRetentionResult, _unset)
+ ? this.lastRetentionResult
+ : lastRetentionResult as ClipboardRetentionResult?,
+ );
+ }
+}
+
+const Object _unset = Object();
+
+final richClipboardServiceProvider = Provider(
+ (ref) => busyMarkRichClipboardService,
+);
+
+final clipboardAssetInputServiceProvider = Provider(
+ (ref) => busyMarkAssetInputService,
+);
+
+final clipboardHistoryPolicyProvider = Provider(
+ (ref) => const ClipboardHistoryPolicy(),
+);
+
+final clipboardInsertionRegistryProvider =
+ ChangeNotifierProvider(
+ (ref) => BusyMarkClipboardInsertionRegistry(),
+ );
+
+final clipboardHistoryControllerProvider =
+ NotifierProvider(
+ ClipboardHistoryController.new,
+ );
+
+final clipboardHistoryOpenRequestProvider =
+ NotifierProvider(
+ ClipboardHistoryOpenRequestController.new,
+ );
+
+class ClipboardHistoryOpenRequestController extends Notifier {
+ @override
+ int build() => 0;
+
+ void request() => state++;
+}
+
+class ClipboardHistoryController extends Notifier {
+ late RichClipboardService _clipboard;
+ late AssetInputService _assetInput;
+ late ClipboardHistoryPolicy _policy;
+ var _refreshGeneration = 0;
+
+ @override
+ ClipboardHistoryState build() {
+ _clipboard = ref.read(richClipboardServiceProvider);
+ _assetInput = ref.read(clipboardAssetInputServiceProvider);
+ _policy = ref.read(clipboardHistoryPolicyProvider);
+ return const ClipboardHistoryState();
+ }
+
+ bool get collectionEnabled =>
+ ref.read(appSettingsControllerProvider).clipboardHistoryEnabled;
+
+ ClipboardRetentionResult retain(BusyMarkClipboardCapture capture) {
+ if (!collectionEnabled) {
+ state = state.copyWith(
+ lastRetentionResult: ClipboardRetentionResult.disabled,
+ );
+ return ClipboardRetentionResult.disabled;
+ }
+ if (!capture.isSupported) return ClipboardRetentionResult.oversized;
+ final payload = _payload(capture);
+ if (payload.accountedBytes > _policy.maximumBytes) {
+ state = state.copyWith(
+ lastRetentionResult: ClipboardRetentionResult.oversized,
+ );
+ return ClipboardRetentionResult.oversized;
+ }
+ final existing = state.entries
+ .where((entry) => entry.equivalentTo(payload))
+ .firstOrNull;
+ final newest = existing ?? payload;
+ final retained = [
+ newest,
+ for (final entry in state.entries)
+ if (!identical(entry, existing)) entry,
+ ];
+ var retainedBytes = retained.fold(
+ 0,
+ (total, entry) => total + entry.accountedBytes,
+ );
+ while (retained.length > _policy.maximumEntries ||
+ retainedBytes > _policy.maximumBytes) {
+ retainedBytes -= retained.removeLast().accountedBytes;
+ }
+ final result = existing == null
+ ? ClipboardRetentionResult.retained
+ : ClipboardRetentionResult.deduplicated;
+ state = state.copyWith(
+ entries: List.unmodifiable(retained),
+ lastRetentionResult: result,
+ );
+ return result;
+ }
+
+ void remove(String id) {
+ state = state.copyWith(
+ entries: List.unmodifiable(
+ state.entries.where((entry) => entry.id != id),
+ ),
+ lastRetentionResult: null,
+ );
+ }
+
+ void clear() {
+ _clipboard.discardObsoleteOwnership();
+ state = state.copyWith(entries: const [], lastRetentionResult: null);
+ }
+
+ Future refreshCurrentClipboard() async {
+ final generation = ++_refreshGeneration;
+ state = state.copyWith(refreshing: true);
+ final first = await _clipboard.read();
+ if (generation != _refreshGeneration || !ref.mounted) return;
+
+ BusyMarkClipboardCapture? capture;
+ if (first.richFragment != null ||
+ first.sourceText != null ||
+ first.html != null) {
+ capture = BusyMarkClipboardCapture(
+ kind: BusyMarkClipboardContentKind.richText,
+ text: first.text,
+ sourceText: first.sourceText,
+ html: first.html,
+ richFragment: first.richFragment,
+ mediaBytes: first.mediaBytes,
+ mediaComplete: first.mediaComplete,
+ origin: first.origin,
+ external: !first.sessionOwned,
+ );
+ } else if (first.text != null) {
+ capture = BusyMarkClipboardCapture(
+ kind: BusyMarkClipboardContentKind.text,
+ text: first.text,
+ sourceText: first.text,
+ origin: first.origin,
+ external: !first.sessionOwned,
+ );
+ } else {
+ final image = await _readCurrentImage();
+ final second = await _clipboard.read();
+ if (generation != _refreshGeneration || !ref.mounted) return;
+ // An image read uses the existing asset boundary. Re-read the rich
+ // boundary and discard the image if ownership changed in between.
+ if (image != null && first.sameExternalIdentity(second)) {
+ capture = BusyMarkClipboardCapture(
+ kind: BusyMarkClipboardContentKind.image,
+ imageBytes: image.bytes,
+ imageMimeType: image.mimeType,
+ imageDisplayName: image.name,
+ external: true,
+ );
+ }
+ }
+ state = state.copyWith(
+ currentClipboard: capture == null ? null : _payload(capture),
+ refreshing: false,
+ clipboardAvailable: capture != null,
+ );
+ }
+
+ /// Makes a transient external item retained only after a successful paste.
+ void retainCurrentAfterPaste(BusyMarkClipboardPayload payload) {
+ if (!payload.external) return;
+ retain(
+ BusyMarkClipboardCapture(
+ kind: payload.kind,
+ text: payload.text,
+ sourceText: payload.sourceText,
+ html: payload.html,
+ richFragment: payload.richFragment,
+ mediaBytes: payload.mediaBytes,
+ mediaComplete: payload.mediaComplete,
+ imageBytes: payload.imageBytes,
+ imageMimeType: payload.imageMimeType,
+ imageDisplayName: payload.imageDisplayName,
+ imageWidth: payload.imageWidth,
+ imageHeight: payload.imageHeight,
+ origin: payload.origin,
+ external: true,
+ ),
+ );
+ }
+
+ BusyMarkClipboardPayload _payload(BusyMarkClipboardCapture capture) {
+ return BusyMarkClipboardPayload(
+ id: const Uuid().v4(),
+ acquiredAt: DateTime.now(),
+ kind: capture.kind,
+ text: capture.text,
+ sourceText: capture.sourceText,
+ html: capture.html,
+ richFragment: capture.richFragment,
+ imageBytes: capture.imageBytes,
+ imageMimeType: capture.imageMimeType,
+ imageDisplayName: capture.imageDisplayName,
+ imageWidth: capture.imageWidth,
+ imageHeight: capture.imageHeight,
+ origin: capture.origin,
+ mediaBytes: capture.mediaBytes,
+ mediaComplete: capture.mediaComplete,
+ external: capture.external,
+ );
+ }
+
+ Future<({Uint8List bytes, String mimeType, String name})?>
+ _readCurrentImage() async {
+ final png = await _assetInput.readClipboardImagePng();
+ if (png != null && png.isNotEmpty && png.length <= _policy.maximumBytes) {
+ return (bytes: png, mimeType: 'image/png', name: 'clipboard-image.png');
+ }
+ final files = await _assetInput.readClipboardImageFiles();
+ if (files.isEmpty) return null;
+ final path = files.first;
+ try {
+ final file = File(path);
+ final size = await file.length();
+ if (size <= 0 || size > _policy.maximumBytes) return null;
+ final bytes = await file.readAsBytes();
+ final extension = p.extension(path).toLowerCase();
+ final mimeType = switch (extension) {
+ '.jpg' || '.jpeg' => 'image/jpeg',
+ '.gif' => 'image/gif',
+ '.webp' => 'image/webp',
+ '.bmp' => 'image/bmp',
+ '.svg' => 'image/svg+xml',
+ _ => 'image/png',
+ };
+ return (bytes: bytes, mimeType: mimeType, name: p.basename(path));
+ } on FileSystemException {
+ return null;
+ }
+ }
+}
+
+extension on Iterable {
+ T? get firstOrNull => isEmpty ? null : first;
+}
diff --git a/lib/src/clipboard/clipboard_history_panel.dart b/lib/src/clipboard/clipboard_history_panel.dart
new file mode 100644
index 00000000..afba6708
--- /dev/null
+++ b/lib/src/clipboard/clipboard_history_panel.dart
@@ -0,0 +1,476 @@
+import 'dart:async';
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_riverpod/flutter_riverpod.dart';
+
+import '../app/busymark_design.dart';
+import '../app/busymark_glyphs.dart';
+import '../app/busymark_search_field.dart';
+import '../app/busymark_toast.dart';
+import '../app/localization.dart';
+import 'clipboard_history_controller.dart';
+import 'clipboard_insertion.dart';
+import 'clipboard_models.dart';
+
+class ClipboardHistoryPanel extends ConsumerStatefulWidget {
+ const ClipboardHistoryPanel({this.onEscape, super.key});
+
+ final VoidCallback? onEscape;
+
+ @override
+ ConsumerState createState() =>
+ _ClipboardHistoryPanelState();
+}
+
+class _ClipboardHistoryPanelState extends ConsumerState {
+ final _searchController = TextEditingController();
+ final _listScrollController = ScrollController();
+ final _listFocusNode = FocusNode(debugLabel: 'Clipboard History list');
+ final _entryKeys = {};
+ String? _selectedId;
+
+ @override
+ void initState() {
+ super.initState();
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ unawaited(
+ ref
+ .read(clipboardHistoryControllerProvider.notifier)
+ .refreshCurrentClipboard(),
+ );
+ }
+ });
+ }
+
+ @override
+ void dispose() {
+ _searchController.dispose();
+ _listScrollController.dispose();
+ _listFocusNode.dispose();
+ super.dispose();
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ final state = ref.watch(clipboardHistoryControllerProvider);
+ final controller = ref.read(clipboardHistoryControllerProvider.notifier);
+ final registry = ref.watch(clipboardInsertionRegistryProvider);
+ final query = _searchController.text.toLowerCase();
+ final current = state.currentClipboard;
+ final visible = [
+ if (current != null && _matches(current, query)) current,
+ for (final entry in state.entries)
+ if ((current == null || !current.equivalentTo(entry)) &&
+ _matches(entry, query))
+ entry,
+ ];
+ if (_selectedId != null &&
+ !visible.any((entry) => entry.id == _selectedId)) {
+ _selectedId = null;
+ }
+ final visibleIds = visible.map((entry) => entry.id).toSet();
+ _entryKeys.removeWhere((id, _) => !visibleIds.contains(id));
+ return Focus(
+ focusNode: _listFocusNode,
+ onKeyEvent: (_, event) => _handleKey(event, visible, registry),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.stretch,
+ children: [
+ Padding(
+ padding: const EdgeInsets.fromLTRB(
+ BusyMarkSpacing.md,
+ BusyMarkSpacing.sm,
+ BusyMarkSpacing.md,
+ BusyMarkSpacing.sm,
+ ),
+ child: Row(
+ textDirection: TextDirection.ltr,
+ children: [
+ Expanded(
+ child: BusyMarkSearchField(
+ controller: _searchController,
+ hintText: context.l10n.clipboardHistorySearchHint,
+ onChanged: (_) => setState(() {}),
+ onEscape: widget.onEscape,
+ ),
+ ),
+ const SizedBox(width: BusyMarkSpacing.sm),
+ BusyMarkHeaderPopupMenuButton<_ClipboardHistoryAction>(
+ key: const ValueKey('clipboard-history-actions-menu'),
+ tooltip: context.l10n.actions,
+ icon: BusyMarkGlyphs.menuVertical,
+ transparent: true,
+ borderRadius: BusyMarkRadius.nativeHeaderButton,
+ highlightWhenOpen: false,
+ itemBuilder: (context) => [
+ BusyMarkPopupMenuItem(
+ value: _ClipboardHistoryAction.refresh,
+ label: MaterialLocalizations.of(
+ context,
+ ).refreshIndicatorSemanticLabel,
+ icon: BusyMarkGlyphs.refresh,
+ enabled: !state.refreshing,
+ ),
+ const PopupMenuDivider(height: BusyMarkSpacing.sm),
+ BusyMarkPopupMenuItem(
+ value: _ClipboardHistoryAction.clear,
+ label: context.l10n.clipboardClearAll,
+ icon: BusyMarkGlyphs.clearAll,
+ enabled: state.entries.isNotEmpty,
+ ),
+ ],
+ onSelected: (action) {
+ switch (action) {
+ case _ClipboardHistoryAction.refresh:
+ unawaited(controller.refreshCurrentClipboard());
+ case _ClipboardHistoryAction.clear:
+ controller.clear();
+ }
+ },
+ ),
+ ],
+ ),
+ ),
+ if (!controller.collectionEnabled)
+ _PanelNotice(
+ icon: BusyMarkGlyphs.info,
+ text: context.l10n.clipboardDisabled,
+ )
+ else if (state.lastRetentionResult ==
+ ClipboardRetentionResult.oversized)
+ _PanelNotice(
+ icon: BusyMarkGlyphs.warning,
+ text: context.l10n.clipboardTooLarge,
+ ),
+ Expanded(
+ child: visible.isEmpty
+ ? _PanelEmpty(
+ text: query.isNotEmpty
+ ? context.l10n.clipboardNoMatches
+ : state.clipboardAvailable
+ ? context.l10n.clipboardNoItems
+ : context.l10n.clipboardUnavailable,
+ )
+ : ListView(
+ key: const ValueKey('clipboard-history-list'),
+ controller: _listScrollController,
+ padding: BusyMarkInsets.sidebarList,
+ children: [
+ for (final payload in visible)
+ _ClipboardEntryTile(
+ key: _entryKeys.putIfAbsent(
+ payload.id,
+ () => GlobalKey(
+ debugLabel: 'Clipboard entry ${payload.id}',
+ ),
+ ),
+ payload: payload,
+ current: identical(payload, current),
+ selected: payload.id == _selectedId,
+ canPaste: registry.canPaste(payload),
+ onSelect: () {
+ setState(() => _selectedId = payload.id);
+ _listFocusNode.requestFocus();
+ },
+ onPaste: () => _paste(payload, plainText: false),
+ onPastePlain:
+ payload.hasMeaningfulTextRepresentation &&
+ registry.canPaste(payload, plainText: true)
+ ? () => _paste(payload, plainText: true)
+ : null,
+ onRemove: identical(payload, current)
+ ? null
+ : () => controller.remove(payload.id),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ bool _matches(BusyMarkClipboardPayload payload, String query) {
+ if (query.isEmpty) return true;
+ return [
+ payload.text,
+ payload.sourceText,
+ payload.imageDisplayName,
+ payload.origin?.documentName,
+ payload.origin?.documentPath,
+ ].whereType().any((value) => value.toLowerCase().contains(query));
+ }
+
+ KeyEventResult _handleKey(
+ KeyEvent event,
+ List visible,
+ BusyMarkClipboardInsertionRegistry registry,
+ ) {
+ if (event is! KeyDownEvent) return KeyEventResult.ignored;
+ if (event.logicalKey == LogicalKeyboardKey.escape) {
+ widget.onEscape?.call();
+ return KeyEventResult.handled;
+ }
+ if (visible.isEmpty) return KeyEventResult.ignored;
+ final index = visible.indexWhere((entry) => entry.id == _selectedId);
+ if (event.logicalKey == LogicalKeyboardKey.arrowDown ||
+ event.logicalKey == LogicalKeyboardKey.arrowUp) {
+ final delta = event.logicalKey == LogicalKeyboardKey.arrowDown ? 1 : -1;
+ final nextIndex = (index < 0 ? 0 : index + delta).clamp(
+ 0,
+ visible.length - 1,
+ );
+ final nextId = visible[nextIndex].id;
+ setState(() {
+ _selectedId = nextId;
+ });
+ _ensureSelectionVisible(nextId);
+ return KeyEventResult.handled;
+ }
+ final selected = index < 0 ? visible.first : visible[index];
+ if (event.logicalKey == LogicalKeyboardKey.enter &&
+ registry.canPaste(selected)) {
+ unawaited(_paste(selected, plainText: false));
+ return KeyEventResult.handled;
+ }
+ if (event.logicalKey == LogicalKeyboardKey.delete &&
+ ref
+ .read(clipboardHistoryControllerProvider)
+ .entries
+ .any((entry) => entry.id == selected.id)) {
+ ref.read(clipboardHistoryControllerProvider.notifier).remove(selected.id);
+ return KeyEventResult.handled;
+ }
+ return KeyEventResult.ignored;
+ }
+
+ void _ensureSelectionVisible(String id) {
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ final entryContext = _entryKeys[id]?.currentContext;
+ if (!mounted || entryContext == null || !entryContext.mounted) return;
+ unawaited(
+ Scrollable.ensureVisible(
+ entryContext,
+ duration: const Duration(milliseconds: 120),
+ alignment: 0,
+ alignmentPolicy: ScrollPositionAlignmentPolicy.explicit,
+ ),
+ );
+ });
+ }
+
+ Future _paste(
+ BusyMarkClipboardPayload payload, {
+ required bool plainText,
+ }) async {
+ final result = await ref
+ .read(clipboardInsertionRegistryProvider)
+ .paste(payload, plainText: plainText);
+ if (result == ClipboardPasteResult.inserted && payload.external) {
+ ref
+ .read(clipboardHistoryControllerProvider.notifier)
+ .retainCurrentAfterPaste(payload);
+ } else if (result != ClipboardPasteResult.inserted && mounted) {
+ BusyMarkToastOverlay.show(
+ context,
+ message: context.l10n.clipboardUnavailable,
+ priority: BusyMarkToastPriority.high,
+ );
+ }
+ }
+}
+
+enum _ClipboardHistoryAction { refresh, clear }
+
+enum _ClipboardEntryAction { paste, pastePlain, remove }
+
+class _ClipboardEntryTile extends StatelessWidget {
+ const _ClipboardEntryTile({
+ super.key,
+ required this.payload,
+ required this.current,
+ required this.selected,
+ required this.canPaste,
+ required this.onSelect,
+ required this.onPaste,
+ required this.onPastePlain,
+ required this.onRemove,
+ });
+
+ final BusyMarkClipboardPayload payload;
+ final bool current;
+ final bool selected;
+ final bool canPaste;
+ final VoidCallback onSelect;
+ final VoidCallback onPaste;
+ final VoidCallback? onPastePlain;
+ final VoidCallback? onRemove;
+
+ @override
+ Widget build(BuildContext context) {
+ final l10n = context.l10n;
+ final kindLabel = switch (payload.kind) {
+ BusyMarkClipboardContentKind.text => l10n.clipboardEntryText,
+ BusyMarkClipboardContentKind.richText => l10n.clipboardEntryRichText,
+ BusyMarkClipboardContentKind.image => l10n.clipboardEntryImage,
+ };
+ final status = current && payload.external
+ ? l10n.clipboardCurrentExternal
+ : current
+ ? l10n.clipboardCurrent
+ : kindLabel;
+ final preview = _clipboardEntryPreview(payload, kindLabel);
+ final metadata = '$status · ${_timestamp(context, payload.acquiredAt)}';
+ final origin = payload.origin;
+ final originPath = origin?.documentPath?.trim();
+ final colors = BusyMarkSurfaceColors.of(context);
+ return BusyMarkSidebarRecordRow<_ClipboardEntryAction>(
+ icon: payload.kind == BusyMarkClipboardContentKind.image
+ ? BusyMarkGlyphs.image
+ : BusyMarkGlyphs.copy,
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ preview,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(
+ context,
+ ).textTheme.bodyMedium?.copyWith(color: colors.foreground),
+ ),
+ const SizedBox(height: BusyMarkSpacing.xxs),
+ Text(
+ metadata,
+ maxLines: 2,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(
+ context,
+ ).textTheme.bodySmall?.copyWith(color: colors.mutedForeground),
+ ),
+ if (origin != null) ...[
+ const SizedBox(height: BusyMarkSpacing.xxs),
+ Text(
+ l10n.clipboardOrigin(origin.documentName),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ style: Theme.of(
+ context,
+ ).textTheme.bodySmall?.copyWith(color: colors.mutedForeground),
+ ),
+ ],
+ if (originPath != null && originPath.isNotEmpty)
+ Text(
+ busyMarkLtrIsolateFor(context, originPath),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ textDirection: TextDirection.ltr,
+ style: Theme.of(
+ context,
+ ).textTheme.bodySmall?.copyWith(color: colors.mutedForeground),
+ ),
+ ],
+ ),
+ selected: selected,
+ semanticsLabel: [
+ preview,
+ metadata,
+ if (origin != null) l10n.clipboardOrigin(origin.documentName),
+ if (originPath != null && originPath.isNotEmpty) originPath,
+ ].join('\n'),
+ onTap: onSelect,
+ onDoubleTap: canPaste ? onPaste : null,
+ menuTooltip: l10n.actions,
+ menuItemsBuilder: (context) => [
+ BusyMarkPopupMenuItem(
+ value: _ClipboardEntryAction.paste,
+ label: context.l10n.paste,
+ icon: BusyMarkGlyphs.paste,
+ enabled: canPaste,
+ ),
+ if (onPastePlain != null)
+ BusyMarkPopupMenuItem(
+ value: _ClipboardEntryAction.pastePlain,
+ label: context.l10n.clipboardPastePlainText,
+ icon: BusyMarkGlyphs.text,
+ ),
+ if (onRemove != null) ...[
+ const PopupMenuDivider(height: BusyMarkSpacing.sm),
+ BusyMarkPopupMenuItem(
+ value: _ClipboardEntryAction.remove,
+ label: context.l10n.removeAction,
+ icon: BusyMarkGlyphs.delete,
+ ),
+ ],
+ ],
+ onMenuOpening: onSelect,
+ onMenuSelected: (action) {
+ switch (action) {
+ case _ClipboardEntryAction.paste:
+ onPaste();
+ case _ClipboardEntryAction.pastePlain:
+ onPastePlain?.call();
+ case _ClipboardEntryAction.remove:
+ onRemove?.call();
+ }
+ },
+ );
+ }
+}
+
+String _clipboardEntryPreview(
+ BusyMarkClipboardPayload payload,
+ String fallback,
+) {
+ final value =
+ payload.imageDisplayName ??
+ payload.preferredSourceText ??
+ payload.text ??
+ fallback;
+ final preview = value.trim();
+ return preview.isEmpty ? fallback : preview;
+}
+
+class _PanelNotice extends StatelessWidget {
+ const _PanelNotice({required this.icon, required this.text});
+
+ final IconData icon;
+ final String text;
+
+ @override
+ Widget build(BuildContext context) => Padding(
+ padding: const EdgeInsets.all(BusyMarkSpacing.md),
+ child: Row(
+ children: [
+ Icon(icon, size: 18),
+ const SizedBox(width: BusyMarkSpacing.sm),
+ Expanded(
+ child: Text(text, style: Theme.of(context).textTheme.bodySmall),
+ ),
+ ],
+ ),
+ );
+}
+
+class _PanelEmpty extends StatelessWidget {
+ const _PanelEmpty({required this.text});
+
+ final String text;
+
+ @override
+ Widget build(BuildContext context) => Center(
+ child: Padding(
+ padding: const EdgeInsets.all(BusyMarkSpacing.lg),
+ child: Text(text, textAlign: TextAlign.center),
+ ),
+ );
+}
+
+String _timestamp(BuildContext context, DateTime timestamp) {
+ final local = timestamp.toLocal();
+ final material = MaterialLocalizations.of(context);
+ return '${material.formatShortDate(local)} ${material.formatTimeOfDay(TimeOfDay.fromDateTime(local))}';
+}
diff --git a/lib/src/clipboard/clipboard_insertion.dart b/lib/src/clipboard/clipboard_insertion.dart
new file mode 100644
index 00000000..f538520e
--- /dev/null
+++ b/lib/src/clipboard/clipboard_insertion.dart
@@ -0,0 +1,94 @@
+import 'dart:async';
+
+import 'package:flutter/foundation.dart';
+
+import 'clipboard_models.dart';
+
+abstract interface class BusyMarkClipboardInsertionTarget {
+ String get documentId;
+ String get documentName;
+ String? get documentPath;
+ bool get editable;
+
+ Future paste(
+ BusyMarkClipboardPayload payload, {
+ required bool plainText,
+ });
+
+ void requestEditorFocus();
+}
+
+/// Optional destination-specific capability used to keep panel actions honest.
+abstract interface class BusyMarkClipboardInsertionCapabilities {
+ bool canPaste(BusyMarkClipboardPayload payload, {required bool plainText});
+}
+
+/// Holds only the currently mounted document surface. Registrations remove
+/// themselves by identity, so a retiring editor cannot unregister its
+/// replacement or leave a disposed controller reachable from session state.
+class BusyMarkClipboardInsertionRegistry extends ChangeNotifier {
+ BusyMarkClipboardInsertionTarget? _target;
+ bool _notificationScheduled = false;
+ bool _disposed = false;
+
+ BusyMarkClipboardInsertionTarget? get target => _target;
+
+ bool canPaste(BusyMarkClipboardPayload payload, {bool plainText = false}) {
+ final captured = _target;
+ if (captured == null || !captured.editable) return false;
+ if (captured is BusyMarkClipboardInsertionCapabilities) {
+ final capabilities = captured as BusyMarkClipboardInsertionCapabilities;
+ return capabilities.canPaste(payload, plainText: plainText);
+ }
+ return true;
+ }
+
+ void register(BusyMarkClipboardInsertionTarget target) {
+ if (identical(_target, target)) return;
+ _target = target;
+ _scheduleNotification();
+ }
+
+ void unregister(BusyMarkClipboardInsertionTarget target) {
+ if (!identical(_target, target)) return;
+ _target = null;
+ _scheduleNotification();
+ }
+
+ void _scheduleNotification() {
+ if (_notificationScheduled || _disposed) return;
+ _notificationScheduled = true;
+ scheduleMicrotask(() {
+ _notificationScheduled = false;
+ if (!_disposed) notifyListeners();
+ });
+ }
+
+ @override
+ void dispose() {
+ _disposed = true;
+ super.dispose();
+ }
+
+ Future paste(
+ BusyMarkClipboardPayload payload, {
+ bool plainText = false,
+ }) async {
+ final captured = _target;
+ if (captured == null ||
+ !captured.editable ||
+ (captured is BusyMarkClipboardInsertionCapabilities &&
+ !(captured as BusyMarkClipboardInsertionCapabilities).canPaste(
+ payload,
+ plainText: plainText,
+ ))) {
+ return ClipboardPasteResult.unavailable;
+ }
+ final result = await captured.paste(payload, plainText: plainText);
+ if (result == ClipboardPasteResult.inserted &&
+ identical(_target, captured)) {
+ captured.requestEditorFocus();
+ }
+ return result;
+ }
+}
diff --git a/lib/src/clipboard/clipboard_models.dart b/lib/src/clipboard/clipboard_models.dart
new file mode 100644
index 00000000..df9ff64b
--- /dev/null
+++ b/lib/src/clipboard/clipboard_models.dart
@@ -0,0 +1,274 @@
+import 'dart:convert';
+import 'dart:typed_data' show BytesBuilder, Uint8List;
+
+import 'package:crypto/crypto.dart';
+import 'package:flutter/foundation.dart';
+
+enum BusyMarkClipboardContentKind { text, richText, image }
+
+@immutable
+class BusyMarkClipboardOrigin {
+ const BusyMarkClipboardOrigin({
+ required this.documentId,
+ required this.documentName,
+ this.documentPath,
+ });
+
+ final String documentId;
+ final String documentName;
+ final String? documentPath;
+}
+
+/// An immutable, source-independent clipboard snapshot.
+///
+/// [sourceText] is the representation inserted into Source mode. [text] is
+/// the interoperable plain-text representation. A rich fragment is an opaque,
+/// validated BusyMark schema and is never reconstructed from plain-text
+/// equality.
+@immutable
+class BusyMarkClipboardPayload {
+ BusyMarkClipboardPayload({
+ required this.id,
+ required this.acquiredAt,
+ required this.kind,
+ this.text,
+ this.sourceText,
+ this.html,
+ this.richFragment,
+ Uint8List? imageBytes,
+ this.imageMimeType,
+ this.imageDisplayName,
+ this.imageWidth,
+ this.imageHeight,
+ this.origin,
+ Map mediaBytes = const {},
+ this.mediaComplete = true,
+ this.external = false,
+ }) : imageBytes = imageBytes == null ? null : Uint8List.fromList(imageBytes),
+ mediaBytes = Map.unmodifiable({
+ for (final entry in mediaBytes.entries)
+ entry.key: Uint8List.fromList(entry.value),
+ }),
+ fingerprint = _fingerprint(
+ kind: kind,
+ text: text,
+ sourceText: sourceText,
+ html: html,
+ richFragment: richFragment,
+ imageBytes: imageBytes,
+ imageMimeType: imageMimeType,
+ imageDisplayName: imageDisplayName,
+ imageWidth: imageWidth,
+ imageHeight: imageHeight,
+ origin: origin,
+ mediaBytes: mediaBytes,
+ mediaComplete: mediaComplete,
+ ),
+ accountedBytes = _accountedBytes(
+ text: text,
+ sourceText: sourceText,
+ html: html,
+ richFragment: richFragment,
+ imageBytes: imageBytes,
+ imageMimeType: imageMimeType,
+ imageDisplayName: imageDisplayName,
+ imageWidth: imageWidth,
+ imageHeight: imageHeight,
+ origin: origin,
+ mediaBytes: mediaBytes,
+ );
+
+ final String id;
+ final DateTime acquiredAt;
+ final BusyMarkClipboardContentKind kind;
+ final String? text;
+ final String? sourceText;
+ final String? html;
+ final String? richFragment;
+ final Uint8List? imageBytes;
+ final String? imageMimeType;
+ final String? imageDisplayName;
+ final int? imageWidth;
+ final int? imageHeight;
+ final BusyMarkClipboardOrigin? origin;
+ final Map mediaBytes;
+ final bool mediaComplete;
+ final bool external;
+ final String fingerprint;
+ final int accountedBytes;
+
+ bool get hasMeaningfulTextRepresentation =>
+ sourceText != null || text != null;
+
+ String? get preferredSourceText => sourceText ?? text;
+
+ bool equivalentTo(BusyMarkClipboardPayload other) =>
+ fingerprint == other.fingerprint &&
+ kind == other.kind &&
+ text == other.text &&
+ sourceText == other.sourceText &&
+ html == other.html &&
+ richFragment == other.richFragment &&
+ imageMimeType == other.imageMimeType &&
+ imageDisplayName == other.imageDisplayName &&
+ imageWidth == other.imageWidth &&
+ imageHeight == other.imageHeight &&
+ origin?.documentId == other.origin?.documentId &&
+ origin?.documentPath == other.origin?.documentPath &&
+ mediaComplete == other.mediaComplete &&
+ _sameMediaBytes(mediaBytes, other.mediaBytes) &&
+ listEquals(imageBytes, other.imageBytes);
+
+ static String _fingerprint({
+ required BusyMarkClipboardContentKind kind,
+ required String? text,
+ required String? sourceText,
+ required String? html,
+ required String? richFragment,
+ required Uint8List? imageBytes,
+ required String? imageMimeType,
+ required String? imageDisplayName,
+ required int? imageWidth,
+ required int? imageHeight,
+ required BusyMarkClipboardOrigin? origin,
+ required Map mediaBytes,
+ required bool mediaComplete,
+ }) {
+ final bytes = BytesBuilder(copy: false);
+ void addText(String label, String? value) {
+ bytes.add(utf8.encode('$label\u0000${value ?? ''}\u0000'));
+ }
+
+ addText('kind', kind.name);
+ addText('text', text);
+ addText('source', sourceText);
+ addText('html', html);
+ addText('fragment', richFragment);
+ addText('mime', imageMimeType);
+ addText('name', imageDisplayName);
+ addText('width', imageWidth?.toString());
+ addText('height', imageHeight?.toString());
+ addText('origin-document', origin?.documentId);
+ addText('origin-path', origin?.documentPath);
+ addText('media-complete', mediaComplete.toString());
+ final mediaKeys = mediaBytes.keys.toList()..sort();
+ for (final key in mediaKeys) {
+ addText('media-key', key);
+ bytes.add(mediaBytes[key]!);
+ }
+ if (imageBytes != null) bytes.add(imageBytes);
+ return sha256.convert(bytes.takeBytes()).toString();
+ }
+
+ static int _accountedBytes({
+ required String? text,
+ required String? sourceText,
+ required String? html,
+ required String? richFragment,
+ required Uint8List? imageBytes,
+ required String? imageMimeType,
+ required String? imageDisplayName,
+ required int? imageWidth,
+ required int? imageHeight,
+ required BusyMarkClipboardOrigin? origin,
+ required Map mediaBytes,
+ }) {
+ var result = imageBytes?.lengthInBytes ?? 0;
+ for (final value in [
+ text,
+ sourceText,
+ html,
+ richFragment,
+ imageMimeType,
+ imageDisplayName,
+ imageWidth?.toString(),
+ imageHeight?.toString(),
+ origin?.documentId,
+ origin?.documentName,
+ origin?.documentPath,
+ ]) {
+ if (value != null) result += utf8.encode(value).length;
+ }
+ for (final entry in mediaBytes.entries) {
+ result += utf8.encode(entry.key).length + entry.value.lengthInBytes;
+ }
+ return result;
+ }
+}
+
+@immutable
+class BusyMarkClipboardCapture {
+ const BusyMarkClipboardCapture({
+ required this.kind,
+ this.text,
+ this.sourceText,
+ this.html,
+ this.richFragment,
+ this.imageBytes,
+ this.imageMimeType,
+ this.imageDisplayName,
+ this.imageWidth,
+ this.imageHeight,
+ this.origin,
+ this.mediaBytes = const {},
+ this.mediaComplete = true,
+ this.external = false,
+ });
+
+ final BusyMarkClipboardContentKind kind;
+ final String? text;
+ final String? sourceText;
+ final String? html;
+ final String? richFragment;
+ final Uint8List? imageBytes;
+ final String? imageMimeType;
+ final String? imageDisplayName;
+ final int? imageWidth;
+ final int? imageHeight;
+ final BusyMarkClipboardOrigin? origin;
+ final Map mediaBytes;
+ final bool mediaComplete;
+ final bool external;
+
+ bool get isSupported => switch (kind) {
+ BusyMarkClipboardContentKind.text => text != null || sourceText != null,
+ BusyMarkClipboardContentKind.richText =>
+ mediaComplete &&
+ (richFragment != null ||
+ html != null ||
+ sourceText != null ||
+ text != null),
+ BusyMarkClipboardContentKind.image =>
+ imageBytes != null && imageBytes!.isNotEmpty,
+ };
+}
+
+bool _sameMediaBytes(
+ Map left,
+ Map right,
+) {
+ if (left.length != right.length) return false;
+ for (final entry in left.entries) {
+ if (!listEquals(entry.value, right[entry.key])) return false;
+ }
+ return true;
+}
+
+@immutable
+class ClipboardHistoryPolicy {
+ const ClipboardHistoryPolicy({
+ this.maximumEntries = 100,
+ this.maximumBytes = 64 * 1024 * 1024,
+ this.maximumThumbnailBytes = 8 * 1024 * 1024,
+ }) : assert(maximumEntries > 0),
+ assert(maximumBytes > 0),
+ assert(maximumThumbnailBytes > 0);
+
+ final int maximumEntries;
+ final int maximumBytes;
+ final int maximumThumbnailBytes;
+}
+
+enum ClipboardRetentionResult { retained, deduplicated, disabled, oversized }
+
+enum ClipboardPasteResult { inserted, staleTarget, unsupported, unavailable }
diff --git a/lib/src/comparison/source_comparison.dart b/lib/src/comparison/source_comparison.dart
new file mode 100644
index 00000000..5f035a7e
--- /dev/null
+++ b/lib/src/comparison/source_comparison.dart
@@ -0,0 +1,461 @@
+import 'dart:math' as math;
+import 'dart:typed_data';
+
+import 'package:flutter/foundation.dart';
+
+@immutable
+class SourceComparisonInput {
+ const SourceComparisonInput({
+ required this.id,
+ required this.version,
+ required this.label,
+ required this.source,
+ });
+
+ final String id;
+ final int version;
+ final String label;
+ final String source;
+}
+
+@immutable
+class SourceComparisonRange {
+ const SourceComparisonRange(this.start, this.end)
+ : assert(start >= 0),
+ assert(end >= start);
+
+ final int start;
+ final int end;
+}
+
+@immutable
+class SourceComparisonChange {
+ const SourceComparisonChange({
+ required this.oldRange,
+ required this.currentRange,
+ required this.oldText,
+ required this.currentText,
+ required this.oldIntralineRange,
+ required this.currentIntralineRange,
+ required this.exact,
+ });
+
+ final SourceComparisonRange oldRange;
+ final SourceComparisonRange currentRange;
+ final String oldText;
+ final String currentText;
+ final SourceComparisonRange oldIntralineRange;
+ final SourceComparisonRange currentIntralineRange;
+ final bool exact;
+}
+
+@immutable
+class SourceComparison {
+ const SourceComparison({
+ required this.oldInput,
+ required this.currentInput,
+ required this.changes,
+ required this.simplified,
+ });
+
+ final SourceComparisonInput oldInput;
+ final SourceComparisonInput currentInput;
+ final List changes;
+ final bool simplified;
+
+ bool stillMatches(SourceComparisonInput old, SourceComparisonInput current) =>
+ old.id == oldInput.id &&
+ old.version == oldInput.version &&
+ current.id == currentInput.id &&
+ current.version == currentInput.version;
+}
+
+SourceComparison compareSource(
+ SourceComparisonInput oldInput,
+ SourceComparisonInput currentInput, {
+ int maximumLcsCells = 2000000,
+}) {
+ final oldLines = _sourceLines(oldInput.source);
+ final currentLines = _sourceLines(currentInput.source);
+ final operations = <_DiffOperation>[];
+ _diffSegment(
+ oldLines,
+ 0,
+ oldLines.length,
+ currentLines,
+ 0,
+ currentLines.length,
+ operations,
+ maximumLcsCells,
+ );
+ final changes = _changesFromOperations(operations);
+ return SourceComparison(
+ oldInput: oldInput,
+ currentInput: currentInput,
+ changes: List.unmodifiable(changes),
+ simplified: changes.any((change) => !change.exact),
+ );
+}
+
+class _SourceLine {
+ const _SourceLine(this.text, this.start, this.end);
+
+ final String text;
+ final int start;
+ final int end;
+}
+
+List<_SourceLine> _sourceLines(String source) {
+ if (source.isEmpty) return const [];
+ final result = <_SourceLine>[];
+ var start = 0;
+ while (start < source.length) {
+ final newline = source.indexOf('\n', start);
+ final end = newline < 0 ? source.length : newline + 1;
+ result.add(_SourceLine(source.substring(start, end), start, end));
+ start = end;
+ }
+ return result;
+}
+
+enum _OperationKind { equal, delete, insert }
+
+class _DiffOperation {
+ const _DiffOperation(this.kind, this.line, {this.exact = true});
+
+ final _OperationKind kind;
+ final _SourceLine line;
+ final bool exact;
+}
+
+void _diffSegment(
+ List<_SourceLine> oldLines,
+ int oldStart,
+ int oldEnd,
+ List<_SourceLine> currentLines,
+ int currentStart,
+ int currentEnd,
+ List<_DiffOperation> output,
+ int maximumLcsCells,
+) {
+ while (oldStart < oldEnd &&
+ currentStart < currentEnd &&
+ oldLines[oldStart].text == currentLines[currentStart].text) {
+ output.add(_DiffOperation(_OperationKind.equal, oldLines[oldStart]));
+ oldStart++;
+ currentStart++;
+ }
+ var suffix = 0;
+ while (oldStart < oldEnd - suffix &&
+ currentStart < currentEnd - suffix &&
+ oldLines[oldEnd - suffix - 1].text ==
+ currentLines[currentEnd - suffix - 1].text) {
+ suffix++;
+ }
+ oldEnd -= suffix;
+ currentEnd -= suffix;
+ if (oldStart == oldEnd || currentStart == currentEnd) {
+ for (var index = oldStart; index < oldEnd; index++) {
+ output.add(_DiffOperation(_OperationKind.delete, oldLines[index]));
+ }
+ for (var index = currentStart; index < currentEnd; index++) {
+ output.add(_DiffOperation(_OperationKind.insert, currentLines[index]));
+ }
+ } else {
+ final anchors = _patienceAnchors(
+ oldLines,
+ oldStart,
+ oldEnd,
+ currentLines,
+ currentStart,
+ currentEnd,
+ );
+ if (anchors.isNotEmpty) {
+ var previousOld = oldStart;
+ var previousCurrent = currentStart;
+ for (final anchor in anchors) {
+ _diffSegment(
+ oldLines,
+ previousOld,
+ anchor.oldIndex,
+ currentLines,
+ previousCurrent,
+ anchor.currentIndex,
+ output,
+ maximumLcsCells,
+ );
+ output.add(
+ _DiffOperation(_OperationKind.equal, oldLines[anchor.oldIndex]),
+ );
+ previousOld = anchor.oldIndex + 1;
+ previousCurrent = anchor.currentIndex + 1;
+ }
+ _diffSegment(
+ oldLines,
+ previousOld,
+ oldEnd,
+ currentLines,
+ previousCurrent,
+ currentEnd,
+ output,
+ maximumLcsCells,
+ );
+ } else if ((oldEnd - oldStart) * (currentEnd - currentStart) <=
+ maximumLcsCells) {
+ _boundedLcs(
+ oldLines,
+ oldStart,
+ oldEnd,
+ currentLines,
+ currentStart,
+ currentEnd,
+ output,
+ );
+ } else {
+ for (var index = oldStart; index < oldEnd; index++) {
+ output.add(
+ _DiffOperation(_OperationKind.delete, oldLines[index], exact: false),
+ );
+ }
+ for (var index = currentStart; index < currentEnd; index++) {
+ output.add(
+ _DiffOperation(
+ _OperationKind.insert,
+ currentLines[index],
+ exact: false,
+ ),
+ );
+ }
+ }
+ }
+ for (var index = 0; index < suffix; index++) {
+ output.add(_DiffOperation(_OperationKind.equal, oldLines[oldEnd + index]));
+ }
+}
+
+class _Anchor {
+ const _Anchor(this.oldIndex, this.currentIndex);
+
+ final int oldIndex;
+ final int currentIndex;
+}
+
+List<_Anchor> _patienceAnchors(
+ List<_SourceLine> oldLines,
+ int oldStart,
+ int oldEnd,
+ List<_SourceLine> currentLines,
+ int currentStart,
+ int currentEnd,
+) {
+ final oldCounts = {};
+ final oldPositions = {};
+ for (var index = oldStart; index < oldEnd; index++) {
+ final text = oldLines[index].text;
+ oldCounts[text] = (oldCounts[text] ?? 0) + 1;
+ oldPositions[text] = index;
+ }
+ final currentCounts = {};
+ final currentPositions = {};
+ for (var index = currentStart; index < currentEnd; index++) {
+ final text = currentLines[index].text;
+ currentCounts[text] = (currentCounts[text] ?? 0) + 1;
+ currentPositions[text] = index;
+ }
+ final candidates = <_Anchor>[];
+ for (final entry in oldPositions.entries) {
+ if (oldCounts[entry.key] == 1 && currentCounts[entry.key] == 1) {
+ candidates.add(_Anchor(entry.value, currentPositions[entry.key]!));
+ }
+ }
+ candidates.sort((left, right) => left.oldIndex.compareTo(right.oldIndex));
+ if (candidates.isEmpty) return const [];
+
+ final tails = [];
+ final tailCandidate = [];
+ final previous = List.filled(candidates.length, -1);
+ for (var index = 0; index < candidates.length; index++) {
+ final value = candidates[index].currentIndex;
+ var low = 0;
+ var high = tails.length;
+ while (low < high) {
+ final middle = (low + high) >> 1;
+ if (tails[middle] < value) {
+ low = middle + 1;
+ } else {
+ high = middle;
+ }
+ }
+ if (low > 0) previous[index] = tailCandidate[low - 1];
+ if (low == tails.length) {
+ tails.add(value);
+ tailCandidate.add(index);
+ } else {
+ tails[low] = value;
+ tailCandidate[low] = index;
+ }
+ }
+ final result = <_Anchor>[];
+ var index = tailCandidate.last;
+ while (index >= 0) {
+ result.add(candidates[index]);
+ index = previous[index];
+ }
+ return result.reversed.toList(growable: false);
+}
+
+void _boundedLcs(
+ List<_SourceLine> oldLines,
+ int oldStart,
+ int oldEnd,
+ List<_SourceLine> currentLines,
+ int currentStart,
+ int currentEnd,
+ List<_DiffOperation> output,
+) {
+ final oldCount = oldEnd - oldStart;
+ final currentCount = currentEnd - currentStart;
+ final width = currentCount + 1;
+ final table = Uint32List((oldCount + 1) * width);
+ for (var old = oldCount - 1; old >= 0; old--) {
+ for (var current = currentCount - 1; current >= 0; current--) {
+ final cell = old * width + current;
+ table[cell] =
+ oldLines[oldStart + old].text ==
+ currentLines[currentStart + current].text
+ ? table[(old + 1) * width + current + 1] + 1
+ : math.max(
+ table[(old + 1) * width + current],
+ table[old * width + current + 1],
+ );
+ }
+ }
+ var old = 0;
+ var current = 0;
+ while (old < oldCount && current < currentCount) {
+ final oldLine = oldLines[oldStart + old];
+ final currentLine = currentLines[currentStart + current];
+ if (oldLine.text == currentLine.text) {
+ output.add(_DiffOperation(_OperationKind.equal, oldLine));
+ old++;
+ current++;
+ } else if (table[(old + 1) * width + current] >=
+ table[old * width + current + 1]) {
+ output.add(_DiffOperation(_OperationKind.delete, oldLine));
+ old++;
+ } else {
+ output.add(_DiffOperation(_OperationKind.insert, currentLine));
+ current++;
+ }
+ }
+ while (old < oldCount) {
+ output.add(
+ _DiffOperation(_OperationKind.delete, oldLines[oldStart + old++]),
+ );
+ }
+ while (current < currentCount) {
+ output.add(
+ _DiffOperation(
+ _OperationKind.insert,
+ currentLines[currentStart + current++],
+ ),
+ );
+ }
+}
+
+List _changesFromOperations(
+ List<_DiffOperation> operations,
+) {
+ final result = [];
+ var oldOffset = 0;
+ var currentOffset = 0;
+ var index = 0;
+ while (index < operations.length) {
+ final operation = operations[index];
+ if (operation.kind == _OperationKind.equal) {
+ oldOffset += operation.line.text.length;
+ currentOffset += operation.line.text.length;
+ index++;
+ continue;
+ }
+ final oldStart = oldOffset;
+ final currentStart = currentOffset;
+ final old = StringBuffer();
+ final current = StringBuffer();
+ var exact = true;
+ while (index < operations.length &&
+ operations[index].kind != _OperationKind.equal) {
+ final changed = operations[index++];
+ exact = exact && changed.exact;
+ if (changed.kind == _OperationKind.delete) {
+ old.write(changed.line.text);
+ oldOffset += changed.line.text.length;
+ } else {
+ current.write(changed.line.text);
+ currentOffset += changed.line.text.length;
+ }
+ }
+ final oldText = old.toString();
+ final currentText = current.toString();
+ final intraline = _intralineRanges(oldText, currentText);
+ result.add(
+ SourceComparisonChange(
+ oldRange: SourceComparisonRange(oldStart, oldOffset),
+ currentRange: SourceComparisonRange(currentStart, currentOffset),
+ oldText: oldText,
+ currentText: currentText,
+ oldIntralineRange: intraline.oldRange,
+ currentIntralineRange: intraline.currentRange,
+ exact: exact,
+ ),
+ );
+ }
+ return result;
+}
+
+({SourceComparisonRange oldRange, SourceComparisonRange currentRange})
+_intralineRanges(String oldText, String currentText) {
+ var prefix = 0;
+ final limit = math.min(oldText.length, currentText.length);
+ while (prefix < limit &&
+ oldText.codeUnitAt(prefix) == currentText.codeUnitAt(prefix)) {
+ prefix++;
+ }
+ prefix = _safeUtf16Boundary(oldText, prefix, backwards: true);
+ prefix = math.min(
+ prefix,
+ _safeUtf16Boundary(currentText, prefix, backwards: true),
+ );
+ var oldSuffix = oldText.length;
+ var currentSuffix = currentText.length;
+ while (oldSuffix > prefix &&
+ currentSuffix > prefix &&
+ oldText.codeUnitAt(oldSuffix - 1) ==
+ currentText.codeUnitAt(currentSuffix - 1)) {
+ oldSuffix--;
+ currentSuffix--;
+ }
+ oldSuffix = _safeUtf16Boundary(oldText, oldSuffix, backwards: false);
+ currentSuffix = _safeUtf16Boundary(
+ currentText,
+ currentSuffix,
+ backwards: false,
+ );
+ return (
+ oldRange: SourceComparisonRange(prefix, oldSuffix),
+ currentRange: SourceComparisonRange(prefix, currentSuffix),
+ );
+}
+
+int _safeUtf16Boundary(String value, int offset, {required bool backwards}) {
+ if (offset <= 0 || offset >= value.length) return offset;
+ final before = value.codeUnitAt(offset - 1);
+ final after = value.codeUnitAt(offset);
+ final splitsSurrogate =
+ before >= 0xd800 &&
+ before <= 0xdbff &&
+ after >= 0xdc00 &&
+ after <= 0xdfff;
+ if (!splitsSurrogate) return offset;
+ return backwards ? offset - 1 : offset + 1;
+}
diff --git a/lib/src/editor/editor_text_context_menu.dart b/lib/src/editor/editor_text_context_menu.dart
index c9e24bc8..218d8c2a 100644
--- a/lib/src/editor/editor_text_context_menu.dart
+++ b/lib/src/editor/editor_text_context_menu.dart
@@ -13,6 +13,7 @@ Widget buildBusyMarkEditorTextContextMenu(
VoidCallback? onRefineWithAi,
VoidCallback? onCut,
VoidCallback? onCopy,
+ VoidCallback? onPaste,
VoidCallback? onCopyPlainText,
List> additionalItems = const [],
}) {
@@ -22,6 +23,7 @@ Widget buildBusyMarkEditorTextContextMenu(
onRefineWithAi: onRefineWithAi,
onCut: onCut,
onCopy: onCopy,
+ onPaste: onPaste,
onCopyPlainText: onCopyPlainText,
additionalItems: additionalItems,
);
@@ -34,6 +36,7 @@ class _BusyMarkEditorTextContextMenu extends StatefulWidget {
required this.onRefineWithAi,
required this.onCut,
required this.onCopy,
+ required this.onPaste,
required this.onCopyPlainText,
required this.additionalItems,
});
@@ -43,6 +46,7 @@ class _BusyMarkEditorTextContextMenu extends StatefulWidget {
final VoidCallback? onRefineWithAi;
final VoidCallback? onCut;
final VoidCallback? onCopy;
+ final VoidCallback? onPaste;
final VoidCallback? onCopyPlainText;
final List> additionalItems;
@@ -114,6 +118,7 @@ class _BusyMarkEditorTextContextMenuState
final callback = switch (item.type) {
ContextMenuButtonType.cut => widget.onCut ?? item.onPressed,
ContextMenuButtonType.copy => widget.onCopy ?? item.onPressed,
+ ContextMenuButtonType.paste => widget.onPaste ?? item.onPressed,
_ => item.onPressed,
};
if (_commandIdFor(item.type) case final commandId?) {
@@ -165,6 +170,24 @@ class _BusyMarkEditorTextContextMenuState
),
);
}
+ items.add(const PopupMenuDivider(height: BusyMarkSpacing.sm));
+ for (final entry in [
+ (BusyMarkCommandIds.clipboardHistory, BusyMarkGlyphs.copy),
+ (BusyMarkCommandIds.localHistory, BusyMarkGlyphs.documentHistory),
+ ]) {
+ final command = commands[entry.$1];
+ if (command != null) {
+ items.add(
+ BusyMarkPopupMenuItem(
+ value: () => unawaited(commands.execute(entry.$1)),
+ label: command.label(context),
+ icon: entry.$2,
+ shortcut: command.shortcut?.label,
+ enabled: command.enabled(),
+ ),
+ );
+ }
+ }
items.addAll(widget.additionalItems);
return items;
}
diff --git a/lib/src/editor/source/source_commands.dart b/lib/src/editor/source/source_commands.dart
index ba55b8e7..efb63f34 100644
--- a/lib/src/editor/source/source_commands.dart
+++ b/lib/src/editor/source/source_commands.dart
@@ -291,11 +291,15 @@ abstract final class SourceCommands {
TextEditingValue value, {
required bool block,
required String altPlaceholder,
+ String sourceReference = 'url',
}) {
final selection = _normalizedSelection(value);
final selected = selection.textInside(value.text).trim();
final alt = selected.isEmpty ? altPlaceholder : selected;
- final replacement = '';
+ final replacement = imageReference(
+ alt: alt,
+ sourceReference: sourceReference,
+ );
final inserted = block ? '\n$replacement\n' : replacement;
final altStart = selection.start + (block ? 3 : 2);
return _replaceSelection(
@@ -308,6 +312,18 @@ abstract final class SourceCommands {
);
}
+ static String imageReference({
+ required String alt,
+ required String sourceReference,
+ }) => '';
+
+ static String writersideImageReference({
+ required String alt,
+ required String sourceReference,
+ }) =>
+ '
';
+
static TextEditingValue insertTable(
TextEditingValue value, {
required String Function(int columnNumber) headerTextForColumn,
@@ -351,6 +367,12 @@ abstract final class SourceCommands {
}
}
+String _escapeXmlAttribute(String value) => value
+ .replaceAll('&', '&')
+ .replaceAll('"', '"')
+ .replaceAll('<', '<')
+ .replaceAll('>', '>');
+
({int start, int end, List lines}) _selectedLineRange(
TextEditingValue value,
) {
diff --git a/lib/src/editor/source/source_editor.dart b/lib/src/editor/source/source_editor.dart
index 9dec96e5..f1c30e91 100644
--- a/lib/src/editor/source/source_editor.dart
+++ b/lib/src/editor/source/source_editor.dart
@@ -1,10 +1,12 @@
import 'dart:async';
+import 'dart:io';
import 'dart:math' as math;
import 'package:flutter/foundation.dart' show setEquals;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart';
+import 'package:path/path.dart' as p;
import 'package:yaru/yaru.dart';
import '../../ai/ai_models.dart';
@@ -14,10 +16,15 @@ import '../../app/busymark_shortcuts.dart';
import '../../app/busymark_toast.dart';
import '../../app/command_registry.dart';
import '../../app/localization.dart';
+import '../../assets/asset_ingestion_service.dart';
+import '../../clipboard/clipboard_insertion.dart';
+import '../../clipboard/clipboard_models.dart';
import '../../core/diagnostic.dart';
+import '../../platform/rich_clipboard_service.dart';
import '../../search/search_replace_service.dart';
import '../document_text_geometry.dart';
import '../editor_text_context_menu.dart';
+import '../wysiwyg/wysiwyg_clipboard_fragment.dart';
import '../source_folding.dart';
import 'source_commands.dart';
import 'source_controller.dart';
@@ -81,6 +88,15 @@ class BusyMarkSourceEditor extends StatefulWidget {
this.onSessionChanged,
this.autocompleteContext = const SourceAutocompleteContext(),
this.onSymbolAction,
+ this.clipboardService,
+ this.clipboardInsertionRegistry,
+ this.onClipboardCaptured,
+ this.workspaceRoot,
+ this.writersideRoot,
+ this.imagesDir = 'images',
+ this.assetWorkspaceKind,
+ this.assetIngestionService = const AssetIngestionService(),
+ this.onAssetSaveRequired,
});
final String text;
@@ -110,6 +126,15 @@ class BusyMarkSourceEditor extends StatefulWidget {
final BusyMarkSourceSessionChanged? onSessionChanged;
final SourceAutocompleteContext autocompleteContext;
final void Function(SourceSymbolAction action, int offset)? onSymbolAction;
+ final RichClipboardService? clipboardService;
+ final BusyMarkClipboardInsertionRegistry? clipboardInsertionRegistry;
+ final ValueChanged? onClipboardCaptured;
+ final String? workspaceRoot;
+ final String? writersideRoot;
+ final String imagesDir;
+ final AssetWorkspaceKind? assetWorkspaceKind;
+ final AssetIngestionService assetIngestionService;
+ final VoidCallback? onAssetSaveRequired;
@override
State createState() => BusyMarkSourceEditorState();
@@ -126,6 +151,9 @@ class BusyMarkSourceEditorState extends State {
final _searchController = SourceSearchController();
final _searchWorker = SourceSearchWorker();
final _replacementWorker = SearchReplacementWorker();
+ bool _applyingSearchReplacement = false;
+ ({int start, int end})? _requestedSearchRange;
+ bool _searchNavigationFailed = false;
final _intrinsicWidthCache = SourceIntrinsicWidthCache();
final _lineLayoutCache = SourceLineLayoutCache();
final _autocompleteProvider = const SourceAutocompleteProvider();
@@ -142,6 +170,10 @@ class BusyMarkSourceEditorState extends State {
bool _sessionPublicationScheduled = false;
final _undoSessionId = ++_sourceEditorUndoSessionSequence;
var _undoGroupSequence = 0;
+ late final _SourceClipboardInsertionTarget _historyInsertionTarget;
+
+ RichClipboardService get _clipboard =>
+ widget.clipboardService ?? busyMarkRichClipboardService;
@override
void initState() {
@@ -150,6 +182,8 @@ class BusyMarkSourceEditorState extends State {
text: widget.text,
language: widget.language,
);
+ _historyInsertionTarget = _SourceClipboardInsertionTarget(this);
+ widget.clipboardInsertionRegistry?.register(_historyInsertionTarget);
_focusNode = FocusNode(onKeyEvent: _handleKeyEvent);
_scrollController = ScrollController();
_horizontalScrollController = ScrollController();
@@ -166,6 +200,11 @@ class BusyMarkSourceEditorState extends State {
@override
void didUpdateWidget(covariant BusyMarkSourceEditor oldWidget) {
super.didUpdateWidget(oldWidget);
+ if (oldWidget.clipboardInsertionRegistry !=
+ widget.clipboardInsertionRegistry) {
+ oldWidget.clipboardInsertionRegistry?.unregister(_historyInsertionTarget);
+ widget.clipboardInsertionRegistry?.register(_historyInsertionTarget);
+ }
if (widget.text != oldWidget.text ||
widget.searchOptions != oldWidget.searchOptions ||
widget.searchReplacement != oldWidget.searchReplacement) {
@@ -199,6 +238,8 @@ class BusyMarkSourceEditorState extends State {
if (widget.searchActive != oldWidget.searchActive ||
widget.searchOptions != oldWidget.searchOptions ||
authoritativeDocumentChanged) {
+ _requestedSearchRange = null;
+ _searchNavigationFailed = false;
_syncSearchOptions();
}
if (widget.wordWrap && !oldWidget.wordWrap) {
@@ -212,6 +253,7 @@ class BusyMarkSourceEditorState extends State {
@override
void dispose() {
+ widget.clipboardInsertionRegistry?.unregister(_historyInsertionTarget);
_searchDebounce?.cancel();
_foldRefreshDebounce?.cancel();
_searchWorker.dispose();
@@ -299,11 +341,35 @@ class BusyMarkSourceEditorState extends State {
_unfoldSourceRange(startOffset, endOffset);
final start = startOffset.clamp(0, _controller.fullText.length).toInt();
final end = endOffset.clamp(start, _controller.fullText.length).toInt();
+ _replacementWorker.cancel();
+ _requestedSearchRange = start < end ? (start: start, end: end) : null;
+ _searchNavigationFailed = false;
_focusNode.requestFocus();
_controller.fullSelection = TextSelection(
baseOffset: start,
extentOffset: end,
);
+ final result = _searchController.result;
+ final localIndex = result.matches.indexWhere(
+ (match) => match.fullStart == start && match.fullEnd == end,
+ );
+ if (localIndex >= 0) {
+ _requestedSearchRange = null;
+ _searchController.setCurrentMatchIndex(
+ result.firstMatchIndex + localIndex,
+ );
+ _controller.setSearchResult(_searchController.result);
+ setState(() {});
+ } else if (start < end) {
+ _scheduleSearch(
+ minimumFullOffset: start,
+ revealCurrentAfterRefresh: true,
+ wrapIfOffsetMissing: false,
+ );
+ } else {
+ _searchController.setCurrentMatchIndex(null);
+ _controller.setSearchResult(_searchController.result);
+ }
WidgetsBinding.instance.addPostFrameCallback((_) {
_animateScrollToLine(line);
WidgetsBinding.instance.addPostFrameCallback((_) {
@@ -372,6 +438,26 @@ class BusyMarkSourceEditorState extends State {
widget.onOpenSearch();
return KeyEventResult.handled;
}
+ if (commands.shortcutAccepts(
+ BusyMarkCommandIds.textCopy,
+ event,
+ keyboard,
+ )) {
+ unawaited(_copyOrCutSource(cut: false));
+ return KeyEventResult.handled;
+ }
+ if (commands.shortcutAccepts(BusyMarkCommandIds.textCut, event, keyboard)) {
+ unawaited(_copyOrCutSource(cut: true));
+ return KeyEventResult.handled;
+ }
+ if (commands.shortcutAccepts(
+ BusyMarkCommandIds.textPaste,
+ event,
+ keyboard,
+ )) {
+ unawaited(_pasteFromSystemClipboard());
+ return KeyEventResult.handled;
+ }
if (commands.shortcutAccepts(
BusyMarkCommandIds.textUndo,
event,
@@ -495,8 +581,25 @@ class BusyMarkSourceEditorState extends State {
BusyMarkContextCommandAction(
isCommandEnabled: (commandId) =>
!_hasActiveComposition &&
- commandId.startsWith('editor.'),
+ (commandId.startsWith('editor.') ||
+ {
+ BusyMarkCommandIds.textCopy,
+ BusyMarkCommandIds.textCut,
+ BusyMarkCommandIds.textPaste,
+ }.contains(commandId)),
onCommand: (commandId) {
+ if (commandId == BusyMarkCommandIds.textCopy) {
+ unawaited(_copyOrCutSource(cut: false));
+ return;
+ }
+ if (commandId == BusyMarkCommandIds.textCut) {
+ unawaited(_copyOrCutSource(cut: true));
+ return;
+ }
+ if (commandId == BusyMarkCommandIds.textPaste) {
+ unawaited(_pasteFromSystemClipboard());
+ return;
+ }
final name = commandId.substring(
'editor.'.length,
);
@@ -595,6 +698,12 @@ class BusyMarkSourceEditorState extends State {
onRefineWithAi: widget.onAiEdit == null
? null
: () => unawaited(_runAiEdit()),
+ onCopy: () =>
+ unawaited(_copyOrCutSource(cut: false)),
+ onCut: () =>
+ unawaited(_copyOrCutSource(cut: true)),
+ onPaste: () =>
+ unawaited(_pasteFromSystemClipboard()),
),
onChanged: (_) => _handleSourceChanged(),
),
@@ -635,6 +744,8 @@ class BusyMarkSourceEditorState extends State {
),
),
replacement: widget.searchReplacement,
+ canReplaceCurrent:
+ _requestedSearchRange == null && !_searchNavigationFailed,
onReplacementChanged:
widget.onSearchReplacementChanged ?? (_) {},
onReplaceCurrent: () => unawaited(_replaceCurrentSearchMatch()),
@@ -797,7 +908,26 @@ class BusyMarkSourceEditorState extends State {
return;
}
_searchController.acceptResult(result);
- if (minimumFullOffset != null && result.matches.isNotEmpty) {
+ final requestedRange = _requestedSearchRange;
+ if (requestedRange != null) {
+ _requestedSearchRange = null;
+ final localIndex = result.matches.indexWhere(
+ (match) =>
+ match.fullStart == requestedRange.start &&
+ match.fullEnd == requestedRange.end,
+ );
+ _searchNavigationFailed = localIndex < 0;
+ if (localIndex >= 0) {
+ _searchController.setCurrentMatchIndex(
+ result.firstMatchIndex + localIndex,
+ );
+ } else {
+ // A completed lookup with no exact match must not silently
+ // turn Replace Current into replacement of the first result.
+ _searchController.setCurrentMatchIndex(null);
+ }
+ } else if (minimumFullOffset != null &&
+ result.matches.isNotEmpty) {
_searchController.setCurrentMatchIndex(result.firstMatchIndex);
} else if (revealCurrentAfterRefresh &&
_searchController.result.currentMatch == null &&
@@ -837,6 +967,8 @@ class BusyMarkSourceEditorState extends State {
}
void _nextSearchMatch() {
+ _requestedSearchRange = null;
+ _searchNavigationFailed = false;
final result = _searchController.result;
if (result.totalMatchCount == 0) {
_revealSearchMatch(null);
@@ -849,6 +981,8 @@ class BusyMarkSourceEditorState extends State {
}
void _previousSearchMatch() {
+ _requestedSearchRange = null;
+ _searchNavigationFailed = false;
final result = _searchController.result;
if (result.totalMatchCount == 0) {
_revealSearchMatch(null);
@@ -880,7 +1014,9 @@ class BusyMarkSourceEditorState extends State {
}
Future _replaceCurrentSearchMatch({bool findNext = false}) async {
- if (_searchController.result.invalidRegex) {
+ if (_searchController.result.invalidRegex ||
+ _requestedSearchRange != null ||
+ _searchNavigationFailed) {
return;
}
if (_searchController.result.currentMatchIndex == null) {
@@ -906,6 +1042,7 @@ class BusyMarkSourceEditorState extends State {
!identical(document, _controller.document) ||
options != widget.searchOptions ||
replacement != widget.searchReplacement ||
+ _searchController.result.currentMatchIndex != currentIndex ||
preview.invalidRegex ||
preview.matches.isEmpty) {
return;
@@ -916,27 +1053,25 @@ class BusyMarkSourceEditorState extends State {
match.end,
match.replacement,
);
- _applyFullEditingValue(
- TextEditingValue(
- text: nextText,
- selection: TextSelection(
- baseOffset: match.start,
- extentOffset: match.start + match.replacement.length,
+ _applyingSearchReplacement = true;
+ try {
+ _applyFullEditingValue(
+ TextEditingValue(
+ text: nextText,
+ selection: TextSelection(
+ baseOffset: match.start,
+ extentOffset: match.start + match.replacement.length,
+ ),
),
- ),
- );
- final replacementEnd = match.start + match.replacement.length;
- if (findNext) {
- _refreshSearch(
- minimumFullOffset: replacementEnd,
- revealCurrentAfterRefresh: true,
- );
- } else {
- _refreshSearch(
- currentIndex: currentIndex,
- firstMatchIndex: _searchController.result.firstMatchIndex,
);
+ } finally {
+ _applyingSearchReplacement = false;
}
+ final replacementEnd = match.start + match.replacement.length;
+ _refreshSearch(
+ minimumFullOffset: findNext ? replacementEnd : match.start,
+ revealCurrentAfterRefresh: true,
+ );
}
Future _replaceAllSearchMatches() async {
@@ -986,10 +1121,8 @@ class BusyMarkSourceEditorState extends State {
});
return;
}
- final currentIndex = _searchController.result.currentMatchIndex;
if (match.hidden) {
_unfoldSourceRange(match.fullStart, match.fullEnd);
- _refreshSearch(currentIndex: currentIndex);
}
final line = _controller.document.lineIndex.lineNumberAtOffset(
match.fullStart,
@@ -1089,6 +1222,10 @@ class BusyMarkSourceEditorState extends State {
void _handleSourceChanged() {
_replacementWorker.cancel();
+ // Sidebar offsets belong to the pre-edit document. Once the user edits,
+ // resume normal search on the new text instead of awaiting the old range.
+ _requestedSearchRange = null;
+ _searchNavigationFailed = false;
final visibleEdit = _controller.lastVisibleEdit;
final selection = _controller.fullSelection;
final previousSelection =
@@ -1101,10 +1238,12 @@ class BusyMarkSourceEditorState extends State {
final currentSearchIndex = _searchController.result.currentMatchIndex;
final firstMatchIndex = _searchController.result.firstMatchIndex;
_scheduleFoldRefresh();
- _refreshSearch(
- currentIndex: currentSearchIndex,
- firstMatchIndex: firstMatchIndex,
- );
+ if (!_applyingSearchReplacement) {
+ _refreshSearch(
+ currentIndex: currentSearchIndex,
+ firstMatchIndex: firstMatchIndex,
+ );
+ }
final transactionalCallback = widget.onTransactionalChanged;
if (transactionalCallback == null) {
widget.onChanged(_controller.fullText, widget.filePath);
@@ -1415,15 +1554,7 @@ class BusyMarkSourceEditorState extends State {
unawaited(_runAiEdit());
break;
case BusyMarkEditorShortcutAction.copyPlainText:
- final value = _fullEditingValue();
- final selection = value.selection;
- if (selection.isValid && !selection.isCollapsed) {
- unawaited(
- Clipboard.setData(
- ClipboardData(text: selection.textInside(value.text)),
- ),
- );
- }
+ unawaited(_copyOrCutSource(cut: false));
break;
case BusyMarkEditorShortcutAction.bold:
_applyInlineCommand(SourceInlineCommand.bold);
@@ -1543,6 +1674,309 @@ class BusyMarkSourceEditorState extends State {
}
}
+ _SourceClipboardOperationTarget _captureClipboardTarget() =>
+ _SourceClipboardOperationTarget(
+ documentId: widget.documentId ?? widget.filePath ?? '',
+ filePath: widget.filePath,
+ language: widget.language,
+ text: _controller.fullText,
+ selection: _controller.fullSelection,
+ );
+
+ bool _isClipboardTargetCurrent(_SourceClipboardOperationTarget target) =>
+ mounted &&
+ target.documentId == (widget.documentId ?? widget.filePath ?? '') &&
+ target.filePath == widget.filePath &&
+ target.language == widget.language &&
+ target.text == _controller.fullText &&
+ target.selection == _controller.fullSelection;
+
+ Future _copyOrCutSource({required bool cut}) async {
+ if (_hasActiveComposition) return false;
+ final target = _captureClipboardTarget();
+ final selection = target.selection;
+ if (!selection.isValid || selection.isCollapsed) return false;
+ final start = selection.start.clamp(0, target.text.length).toInt();
+ final end = selection.end.clamp(start, target.text.length).toInt();
+ final selected = target.text.substring(start, end);
+ final success = await _clipboard.write(
+ RichClipboardData(
+ text: selected,
+ sourceText: selected,
+ origin: _clipboardOrigin,
+ ),
+ );
+ if (!success) {
+ if (mounted) {
+ BusyMarkToastOverlay.show(
+ context,
+ message: context.l10n.clipboardCopyFailed,
+ );
+ }
+ return false;
+ }
+ widget.onClipboardCaptured?.call(
+ BusyMarkClipboardCapture(
+ kind: BusyMarkClipboardContentKind.text,
+ text: selected,
+ sourceText: selected,
+ origin: _clipboardOrigin,
+ ),
+ );
+ if (cut && _isClipboardTargetCurrent(target)) {
+ _applyFullEditingValue(
+ TextEditingValue(
+ text: target.text.replaceRange(start, end, ''),
+ selection: TextSelection.collapsed(offset: start),
+ ),
+ );
+ }
+ return true;
+ }
+
+ Future _pasteFromSystemClipboard() async {
+ if (_hasActiveComposition) return ClipboardPasteResult.staleTarget;
+ final target = _captureClipboardTarget();
+ final data = await _clipboard.read();
+ if (!_isClipboardTargetCurrent(target)) {
+ return ClipboardPasteResult.staleTarget;
+ }
+ final text = data.sourceText ?? data.text;
+ if (text == null) return ClipboardPasteResult.unsupported;
+ final result = _insertClipboardText(target, text);
+ if (result == ClipboardPasteResult.inserted && !data.sessionOwned) {
+ widget.onClipboardCaptured?.call(
+ BusyMarkClipboardCapture(
+ kind: data.richFragment == null
+ ? BusyMarkClipboardContentKind.text
+ : BusyMarkClipboardContentKind.richText,
+ text: data.text,
+ sourceText: text,
+ html: data.html,
+ richFragment: data.richFragment,
+ origin: _clipboardOrigin,
+ external: true,
+ ),
+ );
+ }
+ return result;
+ }
+
+ Future _pasteHistoryPayload(
+ BusyMarkClipboardPayload payload, {
+ required bool plainText,
+ }) async {
+ if (_hasActiveComposition) {
+ return ClipboardPasteResult.unsupported;
+ }
+ final target = _captureClipboardTarget();
+ if (payload.kind == BusyMarkClipboardContentKind.image) {
+ if (plainText) {
+ final text = payload.text;
+ return text == null
+ ? ClipboardPasteResult.unsupported
+ : _insertClipboardText(target, text);
+ }
+ return _pasteHistoryImage(payload, target);
+ }
+ if (!plainText && payload.richFragment != null) {
+ final decoded = WysiwygClipboardFragment.decode(payload.richFragment!);
+ if (decoded != null) {
+ var fragment = decoded.rebase(widget.filePath ?? '');
+ var assets = const [];
+ if (decoded.mediaPaths.isNotEmpty) {
+ if (!payload.mediaComplete) return ClipboardPasteResult.unsupported;
+ final prepared = await _prepareRetainedClipboardMedia(
+ decoded,
+ payload,
+ target,
+ );
+ if (prepared == null) {
+ return _isClipboardTargetCurrent(target)
+ ? ClipboardPasteResult.unsupported
+ : ClipboardPasteResult.staleTarget;
+ }
+ fragment = prepared.fragment;
+ assets = prepared.assets;
+ }
+ final result = _insertClipboardText(target, fragment.markdown);
+ if (result != ClipboardPasteResult.inserted) {
+ await _deleteUncommittedClipboardAssets(assets);
+ }
+ return result;
+ }
+ }
+ final text = plainText ? payload.text : payload.preferredSourceText;
+ if (text == null) return ClipboardPasteResult.unsupported;
+ return _insertClipboardText(target, text);
+ }
+
+ Future _pasteHistoryImage(
+ BusyMarkClipboardPayload payload,
+ _SourceClipboardOperationTarget target,
+ ) async {
+ final bytes = payload.imageBytes;
+ if (bytes == null || bytes.isEmpty) {
+ return ClipboardPasteResult.unsupported;
+ }
+ if (target.language == SourceSyntaxLanguage.plain) {
+ return ClipboardPasteResult.unsupported;
+ }
+ final alt = context.l10n.image;
+ late final IngestedAsset asset;
+ try {
+ asset = await widget.assetIngestionService.ingestBytes(
+ bytes: bytes,
+ suggestedFileName: payload.imageDisplayName ?? 'clipboard-image.png',
+ request: _assetIngestionRequest,
+ origin: AssetIngestionOrigin.screenshotPaste,
+ );
+ } on AssetSaveRequiredException {
+ if (!_isClipboardTargetCurrent(target)) {
+ return ClipboardPasteResult.staleTarget;
+ }
+ widget.onAssetSaveRequired?.call();
+ return ClipboardPasteResult.unsupported;
+ } on AssetIngestionException {
+ return _isClipboardTargetCurrent(target)
+ ? ClipboardPasteResult.unsupported
+ : ClipboardPasteResult.staleTarget;
+ } on FileSystemException {
+ return _isClipboardTargetCurrent(target)
+ ? ClipboardPasteResult.unsupported
+ : ClipboardPasteResult.staleTarget;
+ }
+ if (!_isClipboardTargetCurrent(target)) {
+ await _deleteUncommittedClipboardAssets([asset]);
+ return ClipboardPasteResult.staleTarget;
+ }
+ final result = _insertClipboardText(target, switch (target.language) {
+ SourceSyntaxLanguage.markdown => SourceCommands.imageReference(
+ alt: alt,
+ sourceReference: asset.markdownPath,
+ ),
+ SourceSyntaxLanguage.xml => SourceCommands.writersideImageReference(
+ alt: alt,
+ sourceReference: asset.markdownPath,
+ ),
+ SourceSyntaxLanguage.plain => throw StateError(
+ 'Plain source cannot contain an image reference.',
+ ),
+ });
+ if (result != ClipboardPasteResult.inserted) {
+ await _deleteUncommittedClipboardAssets([asset]);
+ }
+ return result;
+ }
+
+ Future<({WysiwygClipboardFragment fragment, List assets})?>
+ _prepareRetainedClipboardMedia(
+ WysiwygClipboardFragment fragment,
+ BusyMarkClipboardPayload payload,
+ _SourceClipboardOperationTarget target,
+ ) async {
+ final destinations = {};
+ final assets = [];
+ try {
+ for (final entry in fragment.mediaPaths.entries) {
+ final bytes = payload.mediaBytes[entry.key];
+ if (bytes == null || bytes.isEmpty) {
+ await _deleteUncommittedClipboardAssets(assets);
+ return null;
+ }
+ final asset = await widget.assetIngestionService.ingestBytes(
+ bytes: bytes,
+ suggestedFileName: p.basename(entry.value),
+ request: _assetIngestionRequest,
+ origin: AssetIngestionOrigin.clipboardImageFile,
+ );
+ assets.add(asset);
+ if (!_isClipboardTargetCurrent(target)) {
+ await _deleteUncommittedClipboardAssets(assets);
+ return null;
+ }
+ destinations[entry.key] = asset.markdownPath;
+ }
+ return (
+ fragment: fragment.rebase(
+ widget.filePath ?? '',
+ mediaDestinations: destinations,
+ ),
+ assets: List.unmodifiable(assets),
+ );
+ } on AssetSaveRequiredException {
+ await _deleteUncommittedClipboardAssets(assets);
+ widget.onAssetSaveRequired?.call();
+ return null;
+ } on AssetIngestionException {
+ await _deleteUncommittedClipboardAssets(assets);
+ return null;
+ } on FileSystemException {
+ await _deleteUncommittedClipboardAssets(assets);
+ return null;
+ }
+ }
+
+ AssetIngestionRequest get _assetIngestionRequest => AssetIngestionRequest(
+ documentFilePath: widget.filePath ?? '',
+ workspaceKind:
+ widget.assetWorkspaceKind ??
+ (widget.writersideRoot != null
+ ? AssetWorkspaceKind.writerside
+ : widget.workspaceRoot != null
+ ? AssetWorkspaceKind.markdownWorkspace
+ : AssetWorkspaceKind.standalone),
+ workspaceRoot: widget.workspaceRoot,
+ writersideRoot: widget.writersideRoot,
+ imagesDir: widget.imagesDir,
+ );
+
+ Future _deleteUncommittedClipboardAssets(
+ Iterable assets,
+ ) async {
+ for (final asset in assets) {
+ if (asset.reusedExisting) continue;
+ try {
+ await File(asset.absolutePath).delete();
+ } on FileSystemException {
+ // Failed cancellation cleanup must not make the editor unusable.
+ }
+ }
+ }
+
+ ClipboardPasteResult _insertClipboardText(
+ _SourceClipboardOperationTarget target,
+ String text,
+ ) {
+ if (!_isClipboardTargetCurrent(target)) {
+ return ClipboardPasteResult.staleTarget;
+ }
+ final selection = target.selection.isValid
+ ? target.selection
+ : TextSelection.collapsed(offset: target.text.length);
+ final start = selection.start.clamp(0, target.text.length).toInt();
+ final end = selection.end.clamp(start, target.text.length).toInt();
+ _applyFullEditingValue(
+ TextEditingValue(
+ text: target.text.replaceRange(start, end, text),
+ selection: TextSelection.collapsed(offset: start + text.length),
+ ),
+ );
+ return ClipboardPasteResult.inserted;
+ }
+
+ BusyMarkClipboardOrigin get _clipboardOrigin {
+ final path = widget.filePath;
+ final documentId = widget.documentId ?? path ?? 'untitled';
+ return BusyMarkClipboardOrigin(
+ documentId: documentId,
+ documentName: path == null || p.basename(path).isEmpty
+ ? documentId
+ : p.basename(path),
+ documentPath: path,
+ );
+ }
+
void _applyInlineCommand(SourceInlineCommand command) {
_applyFullEditingValue(
SourceCommands.applyInlineCommand(
@@ -2115,6 +2549,7 @@ class _SourceSearchPanel extends StatefulWidget {
required this.onToggleWholeWord,
required this.onToggleRegex,
required this.replacement,
+ required this.canReplaceCurrent,
required this.onReplacementChanged,
required this.onReplaceCurrent,
required this.onReplaceAndFindNext,
@@ -2129,6 +2564,7 @@ class _SourceSearchPanel extends StatefulWidget {
final VoidCallback onToggleWholeWord;
final VoidCallback onToggleRegex;
final String replacement;
+ final bool canReplaceCurrent;
final ValueChanged onReplacementChanged;
final VoidCallback onReplaceCurrent;
final VoidCallback onReplaceAndFindNext;
@@ -2184,6 +2620,11 @@ class _SourceSearchPanelState extends State<_SourceSearchPanel> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
+ if (result.hasZeroLengthMatches)
+ Text(
+ context.l10n.sourceSearchZeroLengthUnsupported,
+ style: Theme.of(context).textTheme.labelSmall,
+ ),
Row(
mainAxisSize: MainAxisSize.min,
children: [
@@ -2261,14 +2702,16 @@ class _SourceSearchPanelState extends State<_SourceSearchPanel> {
_SearchPanelIconButton(
tooltip: context.l10n.sourceSearchReplaceCurrent,
icon: BusyMarkGlyphs.edit,
- onPressed: result.totalMatchCount == 0
+ onPressed:
+ result.totalMatchCount == 0 || !widget.canReplaceCurrent
? null
: widget.onReplaceCurrent,
),
_SearchPanelIconButton(
tooltip: context.l10n.sourceSearchReplaceAndFindNext,
icon: BusyMarkGlyphs.forwardFor(Directionality.of(context)),
- onPressed: result.totalMatchCount == 0
+ onPressed:
+ result.totalMatchCount == 0 || !widget.canReplaceCurrent
? null
: widget.onReplaceAndFindNext,
),
@@ -2479,6 +2922,70 @@ class _SourceSessionSnapshot {
}
}
+class _SourceClipboardOperationTarget {
+ const _SourceClipboardOperationTarget({
+ required this.documentId,
+ required this.filePath,
+ required this.language,
+ required this.text,
+ required this.selection,
+ });
+
+ final String documentId;
+ final String? filePath;
+ final SourceSyntaxLanguage language;
+ final String text;
+ final TextSelection selection;
+}
+
+class _SourceClipboardInsertionTarget
+ implements
+ BusyMarkClipboardInsertionTarget,
+ BusyMarkClipboardInsertionCapabilities {
+ const _SourceClipboardInsertionTarget(this.state);
+
+ final BusyMarkSourceEditorState state;
+
+ @override
+ String get documentId =>
+ state.widget.documentId ?? state.widget.filePath ?? 'untitled';
+
+ @override
+ String get documentName {
+ final path = state.widget.filePath;
+ return path == null || p.basename(path).isEmpty
+ ? documentId
+ : p.basename(path);
+ }
+
+ @override
+ String? get documentPath => state.widget.filePath;
+
+ @override
+ bool get editable => state.mounted && !state._hasActiveComposition;
+
+ @override
+ bool canPaste(BusyMarkClipboardPayload payload, {required bool plainText}) {
+ if (!editable) return false;
+ if (plainText) return payload.hasMeaningfulTextRepresentation;
+ if (payload.kind == BusyMarkClipboardContentKind.image) {
+ return state.widget.language != SourceSyntaxLanguage.plain &&
+ payload.imageBytes?.isNotEmpty == true;
+ }
+ if (payload.richFragment != null) return payload.mediaComplete;
+ return payload.preferredSourceText != null;
+ }
+
+ @override
+ Future paste(
+ BusyMarkClipboardPayload payload, {
+ required bool plainText,
+ }) => state._pasteHistoryPayload(payload, plainText: plainText);
+
+ @override
+ void requestEditorFocus() => state._focusNode.requestFocus();
+}
+
class _SourceEditorShortcutIntent extends Intent {
const _SourceEditorShortcutIntent(this.action);
diff --git a/lib/src/editor/source/source_search.dart b/lib/src/editor/source/source_search.dart
index 67395748..82d37974 100644
--- a/lib/src/editor/source/source_search.dart
+++ b/lib/src/editor/source/source_search.dart
@@ -76,6 +76,7 @@ class SourceSearchResult {
int? totalMatchCount,
this.firstMatchIndex = 0,
this.invalidRegex = false,
+ this.hasZeroLengthMatches = false,
}) : totalMatchCount = totalMatchCount ?? matches.length;
static final empty = SourceSearchResult(
@@ -93,6 +94,7 @@ class SourceSearchResult {
/// The complete-result index represented by [matches.first].
final int firstMatchIndex;
final bool invalidRegex;
+ final bool hasZeroLengthMatches;
SourceSearchMatch? get currentMatch {
final index = currentMatchIndex;
@@ -114,6 +116,7 @@ class SourceSearchResult {
totalMatchCount: totalMatchCount,
firstMatchIndex: firstMatchIndex,
invalidRegex: invalidRegex,
+ hasZeroLengthMatches: hasZeroLengthMatches,
);
}
@@ -125,6 +128,7 @@ class SourceSearchResult {
other.totalMatchCount == totalMatchCount &&
other.firstMatchIndex == firstMatchIndex &&
other.invalidRegex == invalidRegex &&
+ other.hasZeroLengthMatches == hasZeroLengthMatches &&
_matchesEqual(other.matches, matches);
}
@@ -135,6 +139,7 @@ class SourceSearchResult {
totalMatchCount,
firstMatchIndex,
invalidRegex,
+ hasZeroLengthMatches,
Object.hashAll(matches.map(_matchHash)),
);
}
@@ -263,6 +268,7 @@ class SourceSearchWorker {
int firstMatchIndex = 0,
int? minimumFullOffset,
int maximumMatches = sourceInteractiveSearchMatchLimit,
+ bool stopAfterMaximumMatches = false,
}) {
cancel();
final generation = ++_generation;
@@ -295,6 +301,7 @@ class SourceSearchWorker {
'firstMatchIndex': firstMatchIndex,
'minimumFullOffset': minimumFullOffset,
'maximumMatches': maximumMatches,
+ 'stopAfterMaximumMatches': stopAfterMaximumMatches,
};
Isolate.spawn>(
_sourceSearchWorkerMain,
@@ -373,9 +380,11 @@ void _sourceSearchWorkerMain(List