@wonderwhy-er/desktop-commander 0.2.38 → 0.2.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +53 -2
- package/dist/handlers/filesystem-handlers.d.ts +5 -0
- package/dist/handlers/filesystem-handlers.js +14 -2
- package/dist/remote-device/desktop-commander-integration.js +1 -1
- package/dist/search-manager.js +31 -38
- package/dist/server.js +8 -3
- package/dist/terminal-manager.js +4 -2
- package/dist/tools/edit.js +34 -1
- package/dist/tools/filesystem.js +91 -3
- package/dist/tools/improved-process-tools.js +2 -1
- package/dist/ui/config-editor/app.js +840 -0
- package/dist/ui/config-editor/array-modal.d.ts +19 -0
- package/dist/ui/config-editor/array-modal.js +185 -0
- package/dist/ui/config-editor/config-editor-runtime.js +65 -14096
- package/dist/ui/config-editor/main.js +2 -0
- package/dist/ui/config-editor/src/App.d.ts +43 -0
- package/dist/ui/config-editor/src/components/layout.d.ts +4 -0
- package/dist/ui/config-editor/src/components/layout.js +83 -0
- package/dist/ui/config-editor/src/components/toolbar.d.ts +1 -0
- package/dist/ui/config-editor/src/components/toolbar.js +21 -0
- package/dist/ui/config-editor/src/config-values.d.ts +6 -0
- package/dist/ui/config-editor/src/config-values.js +61 -0
- package/dist/ui/config-editor/src/contracts.d.ts +14 -0
- package/dist/ui/config-editor/src/contracts.js +3 -0
- package/dist/ui/config-editor/src/directory-browser.d.ts +6 -0
- package/dist/ui/config-editor/src/directory-browser.js +71 -0
- package/dist/ui/config-editor/src/layout.d.ts +5 -0
- package/dist/ui/config-editor/src/layout.js +90 -0
- package/dist/ui/config-editor/src/parsing.d.ts +5 -0
- package/dist/ui/config-editor/src/parsing.js +50 -0
- package/dist/ui/config-editor/src/toolbar.d.ts +1 -0
- package/dist/ui/config-editor/src/toolbar.js +18 -0
- package/dist/ui/config-editor/src/types.d.ts +17 -0
- package/dist/ui/config-editor/src/types.js +3 -0
- package/dist/ui/config-editor/src/utils/config-values.d.ts +9 -0
- package/dist/ui/config-editor/src/utils/config-values.js +61 -0
- package/dist/ui/config-editor/src/utils/directory-browser.d.ts +31 -0
- package/dist/ui/config-editor/src/utils/directory-browser.js +201 -0
- package/dist/ui/config-editor/src/utils/parsing.d.ts +8 -0
- package/dist/ui/config-editor/src/utils/parsing.js +50 -0
- package/dist/ui/config-editor/styles.css +2 -1
- package/dist/ui/file-preview/{src/app.d.ts → app.d.ts} +1 -1
- package/dist/ui/file-preview/app.js +2020 -0
- package/dist/ui/file-preview/components/code-viewer.d.ts +6 -0
- package/dist/ui/file-preview/components/code-viewer.js +73 -0
- package/dist/ui/file-preview/components/highlighting.d.ts +2 -0
- package/dist/ui/file-preview/components/highlighting.js +54 -0
- package/dist/ui/file-preview/components/html-renderer.d.ts +5 -0
- package/dist/ui/file-preview/components/html-renderer.js +47 -0
- package/dist/ui/file-preview/components/markdown-renderer.d.ts +1 -0
- package/dist/ui/file-preview/components/markdown-renderer.js +67 -0
- package/dist/ui/file-preview/components/toolbar.d.ts +6 -0
- package/dist/ui/file-preview/components/toolbar.js +75 -0
- package/dist/ui/file-preview/image-preview.d.ts +3 -0
- package/dist/ui/file-preview/image-preview.js +21 -0
- package/dist/ui/file-preview/main.js +5 -0
- package/dist/ui/file-preview/markdown/editor.d.ts +36 -0
- package/dist/ui/file-preview/markdown/editor.js +643 -0
- package/dist/ui/file-preview/markdown/linking.d.ts +9 -0
- package/dist/ui/file-preview/markdown/linking.js +210 -0
- package/dist/ui/file-preview/markdown/outline.d.ts +7 -0
- package/dist/ui/file-preview/markdown/outline.js +40 -0
- package/dist/ui/file-preview/markdown/preview.d.ts +8 -0
- package/dist/ui/file-preview/markdown/preview.js +33 -0
- package/dist/ui/file-preview/markdown/slugify.d.ts +3 -0
- package/dist/ui/file-preview/markdown/slugify.js +31 -0
- package/dist/ui/file-preview/markdown/toc.d.ts +11 -0
- package/dist/ui/file-preview/markdown/toc.js +75 -0
- package/dist/ui/file-preview/markdown/utils.d.ts +1 -0
- package/dist/ui/file-preview/markdown/utils.js +15 -0
- package/dist/ui/file-preview/markdown/workspace-controller.d.ts +25 -0
- package/dist/ui/file-preview/markdown/workspace-controller.js +40 -0
- package/dist/ui/file-preview/preview-runtime.js +384 -26533
- package/dist/ui/file-preview/shared/preview-file-types.d.ts +1 -1
- package/dist/ui/file-preview/src/App.d.ts +4 -0
- package/dist/ui/file-preview/src/App.js +564 -0
- package/dist/ui/file-preview/src/components/CodeViewer.d.ts +6 -0
- package/dist/ui/file-preview/src/components/CodeViewer.js +60 -0
- package/dist/ui/file-preview/src/components/HtmlRenderer.d.ts +8 -0
- package/dist/ui/file-preview/src/components/HtmlRenderer.js +45 -0
- package/dist/ui/file-preview/src/components/MarkdownRenderer.d.ts +1 -0
- package/dist/ui/file-preview/src/components/MarkdownRenderer.js +15 -0
- package/dist/ui/file-preview/src/components/editor-toolbar.d.ts +15 -0
- package/dist/ui/file-preview/src/components/editor-toolbar.js +384 -0
- package/dist/ui/file-preview/src/components/markdown-editor.d.ts +29 -0
- package/dist/ui/file-preview/src/components/markdown-editor.js +535 -0
- package/dist/ui/file-preview/src/components/markdown-renderer.js +47 -9
- package/dist/ui/file-preview/src/directory-controller.d.ts +8 -0
- package/dist/ui/file-preview/src/directory-controller.js +233 -0
- package/dist/ui/file-preview/src/document-layout.d.ts +20 -0
- package/dist/ui/file-preview/src/document-layout.js +109 -0
- package/dist/ui/file-preview/src/document-outline.d.ts +17 -0
- package/dist/ui/file-preview/src/document-outline.js +97 -0
- package/dist/ui/file-preview/src/document-workspace.d.ts +19 -0
- package/dist/ui/file-preview/src/document-workspace.js +33 -0
- package/dist/ui/file-preview/src/file-type-handlers.d.ts +10 -0
- package/dist/ui/file-preview/src/file-type-handlers.js +98 -0
- package/dist/ui/file-preview/src/host/external-actions.d.ts +19 -0
- package/dist/ui/file-preview/src/host/external-actions.js +94 -0
- package/dist/ui/file-preview/src/host/selection-context.d.ts +9 -0
- package/dist/ui/file-preview/src/host/selection-context.js +106 -0
- package/dist/ui/file-preview/src/markdown/block-merge.d.ts +25 -0
- package/dist/ui/file-preview/src/markdown/block-merge.js +86 -0
- package/dist/ui/file-preview/src/markdown/conflict-dialog.d.ts +40 -0
- package/dist/ui/file-preview/src/markdown/conflict-dialog.js +163 -0
- package/dist/ui/file-preview/src/markdown/controller.d.ts +38 -0
- package/dist/ui/file-preview/src/markdown/controller.js +921 -0
- package/dist/ui/file-preview/src/markdown/editor.d.ts +35 -0
- package/dist/ui/file-preview/src/markdown/editor.js +691 -0
- package/dist/ui/file-preview/src/markdown/link-modal.d.ts +13 -0
- package/dist/ui/file-preview/src/markdown/link-modal.js +213 -0
- package/dist/ui/file-preview/src/markdown/linking.d.ts +16 -0
- package/dist/ui/file-preview/src/markdown/linking.js +228 -0
- package/dist/ui/file-preview/src/markdown/outline.d.ts +2 -0
- package/dist/ui/file-preview/src/markdown/outline.js +16 -0
- package/dist/ui/file-preview/src/markdown/parser.d.ts +30 -0
- package/dist/ui/file-preview/src/markdown/parser.js +38 -0
- package/dist/ui/file-preview/src/markdown/preview.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown/preview.js +20 -0
- package/dist/ui/file-preview/src/markdown/raw-editor.d.ts +8 -0
- package/dist/ui/file-preview/src/markdown/raw-editor.js +61 -0
- package/dist/ui/file-preview/src/markdown/selection-toolbar.d.ts +14 -0
- package/dist/ui/file-preview/src/markdown/selection-toolbar.js +128 -0
- package/dist/ui/file-preview/src/markdown/slugify.d.ts +3 -0
- package/dist/ui/file-preview/src/markdown/slugify.js +31 -0
- package/dist/ui/file-preview/src/markdown/toc.d.ts +11 -0
- package/dist/ui/file-preview/src/markdown/toc.js +75 -0
- package/dist/ui/file-preview/src/markdown/utils.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown/utils.js +15 -0
- package/dist/ui/file-preview/src/markdown-workspace/editor.d.ts +36 -0
- package/dist/ui/file-preview/src/markdown-workspace/editor.js +643 -0
- package/dist/ui/file-preview/src/markdown-workspace/linking.d.ts +9 -0
- package/dist/ui/file-preview/src/markdown-workspace/linking.js +210 -0
- package/dist/ui/file-preview/src/markdown-workspace/outline.d.ts +7 -0
- package/dist/ui/file-preview/src/markdown-workspace/outline.js +40 -0
- package/dist/ui/file-preview/src/markdown-workspace/preview.d.ts +8 -0
- package/dist/ui/file-preview/src/markdown-workspace/preview.js +33 -0
- package/dist/ui/file-preview/src/markdown-workspace/slugify.d.ts +3 -0
- package/dist/ui/file-preview/src/markdown-workspace/slugify.js +31 -0
- package/dist/ui/file-preview/src/markdown-workspace/toc.d.ts +11 -0
- package/dist/ui/file-preview/src/markdown-workspace/toc.js +75 -0
- package/dist/ui/file-preview/src/markdown-workspace/utils.d.ts +1 -0
- package/dist/ui/file-preview/src/markdown-workspace/utils.js +15 -0
- package/dist/ui/file-preview/src/markdown-workspace/workspace-controller.d.ts +25 -0
- package/dist/ui/file-preview/src/markdown-workspace/workspace-controller.js +40 -0
- package/dist/ui/file-preview/src/model.d.ts +34 -0
- package/dist/ui/file-preview/src/panel-actions.d.ts +17 -0
- package/dist/ui/file-preview/src/panel-actions.js +182 -0
- package/dist/ui/file-preview/src/path-utils.d.ts +6 -0
- package/dist/ui/file-preview/src/path-utils.js +64 -0
- package/dist/ui/file-preview/src/payload-utils.d.ts +11 -0
- package/dist/ui/file-preview/src/payload-utils.js +94 -0
- package/dist/ui/file-preview/styles.css +1066 -233
- package/dist/ui/file-preview/types.d.ts +1 -0
- package/dist/ui/server-integration.d.ts +13 -0
- package/dist/ui/server-integration.js +31 -0
- package/dist/ui/shared/ToolHeader.d.ts +9 -0
- package/dist/ui/shared/ToolHeader.js +29 -0
- package/dist/ui/shared/app-bootstrap.d.ts +9 -0
- package/dist/ui/shared/app-bootstrap.js +15 -0
- package/dist/ui/shared/guards.d.ts +1 -0
- package/dist/ui/shared/guards.js +3 -0
- package/dist/ui/shared/host-lifecycle.d.ts +1 -0
- package/dist/ui/shared/host-lifecycle.js +8 -2
- package/dist/ui/shared/widget-state.d.ts +6 -1
- package/dist/ui/shared/widget-state.js +102 -4
- package/dist/utils/files/base.d.ts +2 -0
- package/dist/utils/open-browser.js +1 -1
- package/dist/utils/ui-call-context.d.ts +8 -0
- package/dist/utils/ui-call-context.js +72 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +6 -1
- package/dist/data/spec-kit-prompts.json +0 -123
- package/dist/handlers/macos-control-handlers.d.ts +0 -16
- package/dist/handlers/macos-control-handlers.js +0 -81
- package/dist/handlers/node-handlers.d.ts +0 -6
- package/dist/handlers/node-handlers.js +0 -73
- package/dist/handlers/test-crash-handler.d.ts +0 -11
- package/dist/handlers/test-crash-handler.js +0 -26
- package/dist/http-index.d.ts +0 -45
- package/dist/http-index.js +0 -51
- package/dist/http-server-auto-tunnel.js +0 -667
- package/dist/http-server-named-tunnel.d.ts +0 -2
- package/dist/http-server-named-tunnel.js +0 -167
- package/dist/http-server-tunnel.d.ts +0 -2
- package/dist/http-server-tunnel.js +0 -111
- package/dist/http-server.d.ts +0 -2
- package/dist/http-server.js +0 -270
- package/dist/index-oauth.d.ts +0 -2
- package/dist/index-oauth.js +0 -201
- package/dist/lib.d.ts +0 -10
- package/dist/lib.js +0 -10
- package/dist/oauth/auth-middleware.d.ts +0 -20
- package/dist/oauth/auth-middleware.js +0 -62
- package/dist/oauth/index.d.ts +0 -3
- package/dist/oauth/index.js +0 -3
- package/dist/oauth/oauth-manager.d.ts +0 -80
- package/dist/oauth/oauth-manager.js +0 -179
- package/dist/oauth/oauth-routes.d.ts +0 -3
- package/dist/oauth/oauth-routes.js +0 -377
- package/dist/oauth/provider.d.ts +0 -22
- package/dist/oauth/provider.js +0 -124
- package/dist/oauth/server.d.ts +0 -18
- package/dist/oauth/server.js +0 -160
- package/dist/oauth/types.d.ts +0 -54
- package/dist/oauth/types.js +0 -2
- package/dist/remote-device/templates/auth-success.d.ts +0 -1
- package/dist/remote-device/templates/auth-success.js +0 -30
- package/dist/setup.log +0 -275
- package/dist/test-setup.js +0 -14
- package/dist/tools/docx/builders/html-builder.d.ts +0 -17
- package/dist/tools/docx/builders/html-builder.js +0 -92
- package/dist/tools/docx/builders/image.d.ts +0 -14
- package/dist/tools/docx/builders/image.js +0 -84
- package/dist/tools/docx/builders/index.d.ts +0 -11
- package/dist/tools/docx/builders/index.js +0 -11
- package/dist/tools/docx/builders/markdown-builder.d.ts +0 -2
- package/dist/tools/docx/builders/markdown-builder.js +0 -260
- package/dist/tools/docx/builders/paragraph.d.ts +0 -12
- package/dist/tools/docx/builders/paragraph.js +0 -29
- package/dist/tools/docx/builders/table.d.ts +0 -10
- package/dist/tools/docx/builders/table.js +0 -138
- package/dist/tools/docx/builders/utils.d.ts +0 -5
- package/dist/tools/docx/builders/utils.js +0 -18
- package/dist/tools/docx/constants.d.ts +0 -32
- package/dist/tools/docx/constants.js +0 -61
- package/dist/tools/docx/converters/markdown-to-html.d.ts +0 -17
- package/dist/tools/docx/converters/markdown-to-html.js +0 -111
- package/dist/tools/docx/create.d.ts +0 -21
- package/dist/tools/docx/create.js +0 -386
- package/dist/tools/docx/dom.d.ts +0 -139
- package/dist/tools/docx/dom.js +0 -448
- package/dist/tools/docx/errors.d.ts +0 -28
- package/dist/tools/docx/errors.js +0 -48
- package/dist/tools/docx/extractors/images.d.ts +0 -14
- package/dist/tools/docx/extractors/images.js +0 -40
- package/dist/tools/docx/extractors/metadata.d.ts +0 -14
- package/dist/tools/docx/extractors/metadata.js +0 -64
- package/dist/tools/docx/extractors/sections.d.ts +0 -14
- package/dist/tools/docx/extractors/sections.js +0 -61
- package/dist/tools/docx/html.d.ts +0 -17
- package/dist/tools/docx/html.js +0 -111
- package/dist/tools/docx/index.d.ts +0 -10
- package/dist/tools/docx/index.js +0 -10
- package/dist/tools/docx/markdown.d.ts +0 -84
- package/dist/tools/docx/markdown.js +0 -507
- package/dist/tools/docx/modify.d.ts +0 -28
- package/dist/tools/docx/modify.js +0 -271
- package/dist/tools/docx/operations/handlers/index.d.ts +0 -39
- package/dist/tools/docx/operations/handlers/index.js +0 -152
- package/dist/tools/docx/operations/html-manipulator.d.ts +0 -24
- package/dist/tools/docx/operations/html-manipulator.js +0 -352
- package/dist/tools/docx/operations/index.d.ts +0 -14
- package/dist/tools/docx/operations/index.js +0 -61
- package/dist/tools/docx/operations/operation-handlers.d.ts +0 -3
- package/dist/tools/docx/operations/operation-handlers.js +0 -67
- package/dist/tools/docx/operations/preprocessor.d.ts +0 -14
- package/dist/tools/docx/operations/preprocessor.js +0 -44
- package/dist/tools/docx/operations/xml-replacer.d.ts +0 -9
- package/dist/tools/docx/operations/xml-replacer.js +0 -35
- package/dist/tools/docx/operations.d.ts +0 -13
- package/dist/tools/docx/operations.js +0 -13
- package/dist/tools/docx/ops/delete-paragraph-at-body-index.d.ts +0 -11
- package/dist/tools/docx/ops/delete-paragraph-at-body-index.js +0 -23
- package/dist/tools/docx/ops/header-replace-text-exact.d.ts +0 -13
- package/dist/tools/docx/ops/header-replace-text-exact.js +0 -55
- package/dist/tools/docx/ops/index.d.ts +0 -17
- package/dist/tools/docx/ops/index.js +0 -70
- package/dist/tools/docx/ops/insert-image-after-text.d.ts +0 -24
- package/dist/tools/docx/ops/insert-image-after-text.js +0 -128
- package/dist/tools/docx/ops/insert-paragraph-after-text.d.ts +0 -12
- package/dist/tools/docx/ops/insert-paragraph-after-text.js +0 -74
- package/dist/tools/docx/ops/insert-table-after-text.d.ts +0 -19
- package/dist/tools/docx/ops/insert-table-after-text.js +0 -57
- package/dist/tools/docx/ops/replace-hyperlink-url.d.ts +0 -12
- package/dist/tools/docx/ops/replace-hyperlink-url.js +0 -37
- package/dist/tools/docx/ops/replace-paragraph-at-body-index.d.ts +0 -9
- package/dist/tools/docx/ops/replace-paragraph-at-body-index.js +0 -25
- package/dist/tools/docx/ops/replace-paragraph-text-exact.d.ts +0 -21
- package/dist/tools/docx/ops/replace-paragraph-text-exact.js +0 -36
- package/dist/tools/docx/ops/replace-table-cell-text.d.ts +0 -25
- package/dist/tools/docx/ops/replace-table-cell-text.js +0 -85
- package/dist/tools/docx/ops/set-color-for-paragraph-exact.d.ts +0 -9
- package/dist/tools/docx/ops/set-color-for-paragraph-exact.js +0 -24
- package/dist/tools/docx/ops/set-color-for-style.d.ts +0 -13
- package/dist/tools/docx/ops/set-color-for-style.js +0 -31
- package/dist/tools/docx/ops/set-paragraph-style-at-body-index.d.ts +0 -8
- package/dist/tools/docx/ops/set-paragraph-style-at-body-index.js +0 -57
- package/dist/tools/docx/ops/table-set-cell-text.d.ts +0 -9
- package/dist/tools/docx/ops/table-set-cell-text.js +0 -40
- package/dist/tools/docx/parsers/image-extractor.d.ts +0 -18
- package/dist/tools/docx/parsers/image-extractor.js +0 -61
- package/dist/tools/docx/parsers/index.d.ts +0 -9
- package/dist/tools/docx/parsers/index.js +0 -9
- package/dist/tools/docx/parsers/paragraph-parser.d.ts +0 -2
- package/dist/tools/docx/parsers/paragraph-parser.js +0 -88
- package/dist/tools/docx/parsers/table-parser.d.ts +0 -9
- package/dist/tools/docx/parsers/table-parser.js +0 -72
- package/dist/tools/docx/parsers/xml-parser.d.ts +0 -25
- package/dist/tools/docx/parsers/xml-parser.js +0 -71
- package/dist/tools/docx/parsers/zip-reader.d.ts +0 -23
- package/dist/tools/docx/parsers/zip-reader.js +0 -52
- package/dist/tools/docx/read.d.ts +0 -27
- package/dist/tools/docx/read.js +0 -308
- package/dist/tools/docx/relationships.d.ts +0 -22
- package/dist/tools/docx/relationships.js +0 -76
- package/dist/tools/docx/structure.d.ts +0 -25
- package/dist/tools/docx/structure.js +0 -102
- package/dist/tools/docx/styled-html-parser.d.ts +0 -23
- package/dist/tools/docx/styled-html-parser.js +0 -1262
- package/dist/tools/docx/types.d.ts +0 -213
- package/dist/tools/docx/types.js +0 -5
- package/dist/tools/docx/utils/escaping.d.ts +0 -13
- package/dist/tools/docx/utils/escaping.js +0 -26
- package/dist/tools/docx/utils/images.d.ts +0 -9
- package/dist/tools/docx/utils/images.js +0 -26
- package/dist/tools/docx/utils/index.d.ts +0 -12
- package/dist/tools/docx/utils/index.js +0 -17
- package/dist/tools/docx/utils/markdown.d.ts +0 -13
- package/dist/tools/docx/utils/markdown.js +0 -32
- package/dist/tools/docx/utils/paths.d.ts +0 -15
- package/dist/tools/docx/utils/paths.js +0 -27
- package/dist/tools/docx/utils/versioning.d.ts +0 -25
- package/dist/tools/docx/utils/versioning.js +0 -55
- package/dist/tools/docx/utils.d.ts +0 -101
- package/dist/tools/docx/utils.js +0 -299
- package/dist/tools/docx/validate.d.ts +0 -33
- package/dist/tools/docx/validate.js +0 -49
- package/dist/tools/docx/validators.d.ts +0 -13
- package/dist/tools/docx/validators.js +0 -40
- package/dist/tools/docx/write.d.ts +0 -17
- package/dist/tools/docx/write.js +0 -88
- package/dist/tools/docx/xml-view-test.js +0 -63
- package/dist/tools/docx/xml-view.d.ts +0 -56
- package/dist/tools/docx/xml-view.js +0 -169
- package/dist/tools/docx/zip.d.ts +0 -21
- package/dist/tools/docx/zip.js +0 -35
- package/dist/tools/macos-control/ax-adapter.d.ts +0 -55
- package/dist/tools/macos-control/ax-adapter.js +0 -438
- package/dist/tools/macos-control/cdp-adapter.d.ts +0 -23
- package/dist/tools/macos-control/cdp-adapter.js +0 -402
- package/dist/tools/macos-control/orchestrator.d.ts +0 -77
- package/dist/tools/macos-control/orchestrator.js +0 -136
- package/dist/tools/macos-control/role-aliases.d.ts +0 -5
- package/dist/tools/macos-control/role-aliases.js +0 -34
- package/dist/tools/macos-control/types.d.ts +0 -129
- package/dist/tools/pdf-processor.d.ts +0 -1
- package/dist/tools/pdf-processor.js +0 -3
- package/dist/tools/search.d.ts +0 -32
- package/dist/tools/search.js +0 -202
- package/dist/ui/file-preview/src/app.js +0 -714
- package/dist/utils/crash-logger.d.ts +0 -18
- package/dist/utils/crash-logger.js +0 -44
- package/dist/utils/dedent.d.ts +0 -8
- package/dist/utils/dedent.js +0 -38
- /package/dist/ui/config-editor/{src/app.d.ts → app.d.ts} +0 -0
- /package/dist/{http-server-auto-tunnel.d.ts → ui/config-editor/main.d.ts} +0 -0
- /package/dist/ui/config-editor/src/{app.js → App.js} +0 -0
- /package/dist/{test-docx.d.ts → ui/file-preview/main.d.ts} +0 -0
- /package/dist/ui/file-preview/src/components/{toolbar.d.ts → Toolbar.d.ts} +0 -0
- /package/dist/ui/file-preview/src/components/{toolbar.js → Toolbar.js} +0 -0
- /package/dist/{tools/docx/xml-view-test.d.ts → ui/file-preview/src/model.js} +0 -0
- /package/dist/{tools/macos-control → ui/file-preview}/types.js +0 -0
|
@@ -0,0 +1,921 @@
|
|
|
1
|
+
import { attachDocumentOutline, renderDocumentOutline } from '../document-outline.js';
|
|
2
|
+
import { getDocumentFullscreenAvailability, parseReadRange, shouldAutoLoadDocumentOnEnterFullscreen, stripReadStatusLine } from '../document-workspace.js';
|
|
3
|
+
import { assertSuccessfulEditBlockResult, extractRenderPayload, extractToolText } from '../payload-utils.js';
|
|
4
|
+
import { getAncestorDirectories, getParentDirectory, toPosixRelativePath } from '../path-utils.js';
|
|
5
|
+
import { mountMarkdownEditor, renderMarkdownEditorShell } from './editor.js';
|
|
6
|
+
import { resolveMarkdownLink } from './linking.js';
|
|
7
|
+
import { extractMarkdownOutline } from './outline.js';
|
|
8
|
+
import { getRenderedMarkdownCopyText } from './preview.js';
|
|
9
|
+
import { slugifyMarkdownHeading } from './slugify.js';
|
|
10
|
+
import { getFileExtensionForAnalytics } from '../payload-utils.js';
|
|
11
|
+
function areOutlineItemsEqual(left, right) {
|
|
12
|
+
if (left.length !== right.length) {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return left.every((item, index) => {
|
|
16
|
+
const other = right[index];
|
|
17
|
+
return item.id === other.id
|
|
18
|
+
&& item.text === other.text
|
|
19
|
+
&& item.level === other.level
|
|
20
|
+
&& item.line === other.line;
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
function splitListingLines(text) {
|
|
24
|
+
return text.split('\n').map((line) => line.trim()).filter(Boolean);
|
|
25
|
+
}
|
|
26
|
+
function parseFileSearchResults(text) {
|
|
27
|
+
return text.split('\n')
|
|
28
|
+
.map((line) => line.trim())
|
|
29
|
+
.filter((line) => line.startsWith('📁 '))
|
|
30
|
+
.map((line) => line.slice(3).trim());
|
|
31
|
+
}
|
|
32
|
+
function stripMarkdownExtension(filePath) {
|
|
33
|
+
return filePath.replace(/\.md$/i, '');
|
|
34
|
+
}
|
|
35
|
+
function computeDiffHunks(oldLines, newLines) {
|
|
36
|
+
const oldLength = oldLines.length;
|
|
37
|
+
const newLength = newLines.length;
|
|
38
|
+
if (oldLength * newLength > 1000000) {
|
|
39
|
+
return [{ oldStart: 0, oldEnd: oldLength, newStart: 0, newEnd: newLength }];
|
|
40
|
+
}
|
|
41
|
+
const dp = Array.from({ length: oldLength + 1 }, () => Array(newLength + 1).fill(0));
|
|
42
|
+
for (let i = 1; i <= oldLength; i += 1) {
|
|
43
|
+
for (let j = 1; j <= newLength; j += 1) {
|
|
44
|
+
dp[i][j] = oldLines[i - 1] === newLines[j - 1]
|
|
45
|
+
? dp[i - 1][j - 1] + 1
|
|
46
|
+
: Math.max(dp[i - 1][j], dp[i][j - 1]);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const matches = [];
|
|
50
|
+
let oldIndex = oldLength;
|
|
51
|
+
let newIndex = newLength;
|
|
52
|
+
while (oldIndex > 0 && newIndex > 0) {
|
|
53
|
+
if (oldLines[oldIndex - 1] === newLines[newIndex - 1]) {
|
|
54
|
+
matches.unshift([oldIndex - 1, newIndex - 1]);
|
|
55
|
+
oldIndex -= 1;
|
|
56
|
+
newIndex -= 1;
|
|
57
|
+
}
|
|
58
|
+
else if (dp[oldIndex - 1][newIndex] >= dp[oldIndex][newIndex - 1]) {
|
|
59
|
+
oldIndex -= 1;
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
newIndex -= 1;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const hunks = [];
|
|
66
|
+
let previousOld = 0;
|
|
67
|
+
let previousNew = 0;
|
|
68
|
+
for (const [matchOld, matchNew] of matches) {
|
|
69
|
+
if (matchOld > previousOld || matchNew > previousNew) {
|
|
70
|
+
hunks.push({ oldStart: previousOld, oldEnd: matchOld, newStart: previousNew, newEnd: matchNew });
|
|
71
|
+
}
|
|
72
|
+
previousOld = matchOld + 1;
|
|
73
|
+
previousNew = matchNew + 1;
|
|
74
|
+
}
|
|
75
|
+
if (previousOld < oldLength || previousNew < newLength) {
|
|
76
|
+
hunks.push({ oldStart: previousOld, oldEnd: oldLength, newStart: previousNew, newEnd: newLength });
|
|
77
|
+
}
|
|
78
|
+
return hunks;
|
|
79
|
+
}
|
|
80
|
+
function mergeCloseHunks(hunks, minGap) {
|
|
81
|
+
if (hunks.length <= 1) {
|
|
82
|
+
return hunks;
|
|
83
|
+
}
|
|
84
|
+
const merged = [{ ...hunks[0] }];
|
|
85
|
+
for (let index = 1; index < hunks.length; index += 1) {
|
|
86
|
+
const previous = merged[merged.length - 1];
|
|
87
|
+
const current = hunks[index];
|
|
88
|
+
if (current.oldStart - previous.oldEnd < minGap) {
|
|
89
|
+
previous.oldEnd = current.oldEnd;
|
|
90
|
+
previous.newEnd = current.newEnd;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
merged.push({ ...current });
|
|
94
|
+
}
|
|
95
|
+
return merged;
|
|
96
|
+
}
|
|
97
|
+
function computeEditBlocks(oldText, newText) {
|
|
98
|
+
if (oldText === newText) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
const oldLines = oldText.split('\n');
|
|
102
|
+
const newLines = newText.split('\n');
|
|
103
|
+
const hunks = computeDiffHunks(oldLines, newLines);
|
|
104
|
+
if (hunks.length === 0) {
|
|
105
|
+
return [];
|
|
106
|
+
}
|
|
107
|
+
const context = 3;
|
|
108
|
+
const merged = mergeCloseHunks(hunks, context * 2 + 1);
|
|
109
|
+
const totalChanged = merged.reduce((sum, hunk) => sum + (hunk.oldEnd - hunk.oldStart), 0);
|
|
110
|
+
if (totalChanged > oldLines.length * 0.7) {
|
|
111
|
+
return [{ old_string: oldText, new_string: newText }];
|
|
112
|
+
}
|
|
113
|
+
return merged.map((hunk) => {
|
|
114
|
+
const contextBefore = Math.max(0, hunk.oldStart - context);
|
|
115
|
+
const contextAfter = Math.min(oldLines.length, hunk.oldEnd + context);
|
|
116
|
+
const oldBlock = oldLines.slice(contextBefore, contextAfter).join('\n');
|
|
117
|
+
const newBlock = [
|
|
118
|
+
...oldLines.slice(contextBefore, hunk.oldStart),
|
|
119
|
+
...newLines.slice(hunk.newStart, hunk.newEnd),
|
|
120
|
+
...oldLines.slice(hunk.oldEnd, contextAfter),
|
|
121
|
+
].join('\n');
|
|
122
|
+
return { old_string: oldBlock, new_string: newBlock };
|
|
123
|
+
}).filter((block) => block.old_string !== block.new_string);
|
|
124
|
+
}
|
|
125
|
+
function isToolErrorResult(value) {
|
|
126
|
+
return typeof value === 'object' && value !== null;
|
|
127
|
+
}
|
|
128
|
+
function isMissingFileErrorResult(result) {
|
|
129
|
+
if (!isToolErrorResult(result) || result.isError !== true) {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
const message = extractToolText(result)?.toLowerCase() ?? '';
|
|
133
|
+
return message.includes('not found')
|
|
134
|
+
|| message.includes('no such file')
|
|
135
|
+
|| message.includes('enoent');
|
|
136
|
+
}
|
|
137
|
+
export function createMarkdownController(dependencies) {
|
|
138
|
+
let workspaceState;
|
|
139
|
+
let markdownEditorHandle;
|
|
140
|
+
let markdownTocHandle;
|
|
141
|
+
let autosaveTimer = null;
|
|
142
|
+
const AUTOSAVE_DEBOUNCE_MS = 1000;
|
|
143
|
+
function scheduleAutosave() {
|
|
144
|
+
if (autosaveTimer !== null) {
|
|
145
|
+
clearTimeout(autosaveTimer);
|
|
146
|
+
}
|
|
147
|
+
autosaveTimer = setTimeout(() => {
|
|
148
|
+
autosaveTimer = null;
|
|
149
|
+
void saveDocument();
|
|
150
|
+
}, AUTOSAVE_DEBOUNCE_MS);
|
|
151
|
+
}
|
|
152
|
+
function cancelAutosave() {
|
|
153
|
+
if (autosaveTimer !== null) {
|
|
154
|
+
clearTimeout(autosaveTimer);
|
|
155
|
+
autosaveTimer = null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function disposeHandles() {
|
|
159
|
+
cancelAutosave();
|
|
160
|
+
markdownEditorHandle?.destroy();
|
|
161
|
+
markdownEditorHandle = undefined;
|
|
162
|
+
markdownTocHandle?.dispose();
|
|
163
|
+
markdownTocHandle = undefined;
|
|
164
|
+
}
|
|
165
|
+
function clear() {
|
|
166
|
+
workspaceState = undefined;
|
|
167
|
+
disposeHandles();
|
|
168
|
+
}
|
|
169
|
+
function readPayloadContent(payload) {
|
|
170
|
+
return stripReadStatusLine(payload.content);
|
|
171
|
+
}
|
|
172
|
+
function syncStateFromContent(state, content, options = {}) {
|
|
173
|
+
const nextDraftContent = options.keepDraft ? state.draftContent : content;
|
|
174
|
+
state.sourceContent = content;
|
|
175
|
+
state.fullDocumentContent = content;
|
|
176
|
+
state.draftContent = nextDraftContent;
|
|
177
|
+
state.outline = extractMarkdownOutline(content);
|
|
178
|
+
state.dirty = nextDraftContent !== content;
|
|
179
|
+
state.fileDeleted = false;
|
|
180
|
+
if (!state.outline.some((item) => item.id === state.activeHeadingId)) {
|
|
181
|
+
state.activeHeadingId = state.outline[0]?.id ?? null;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
async function callReadFile(filePath, length, offset) {
|
|
185
|
+
const rawResult = await dependencies.callTool?.('read_file', {
|
|
186
|
+
path: filePath,
|
|
187
|
+
...(typeof length === 'number' ? { offset: offset ?? 0, length } : {}),
|
|
188
|
+
});
|
|
189
|
+
return { rawResult, payload: extractRenderPayload(rawResult) ?? null };
|
|
190
|
+
}
|
|
191
|
+
async function readPayload(filePath, length, offset) {
|
|
192
|
+
return (await callReadFile(filePath, length, offset)).payload;
|
|
193
|
+
}
|
|
194
|
+
async function ensureCompletePayload(payload) {
|
|
195
|
+
const range = parseReadRange(payload.content);
|
|
196
|
+
if (!range?.isPartial) {
|
|
197
|
+
return payload;
|
|
198
|
+
}
|
|
199
|
+
return (await readPayload(payload.filePath, range.totalLines)) ?? payload;
|
|
200
|
+
}
|
|
201
|
+
async function readCompletePayload(filePath) {
|
|
202
|
+
const payload = await readPayload(filePath);
|
|
203
|
+
if (!payload) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
return ensureCompletePayload(payload);
|
|
207
|
+
}
|
|
208
|
+
function getState(payload) {
|
|
209
|
+
const cleanedContent = stripReadStatusLine(payload.content);
|
|
210
|
+
if (!workspaceState || workspaceState.filePath !== payload.filePath || workspaceState.sourceContent !== cleanedContent) {
|
|
211
|
+
const outline = extractMarkdownOutline(cleanedContent);
|
|
212
|
+
workspaceState = {
|
|
213
|
+
filePath: payload.filePath,
|
|
214
|
+
sourceContent: cleanedContent,
|
|
215
|
+
fullDocumentContent: cleanedContent,
|
|
216
|
+
draftContent: cleanedContent,
|
|
217
|
+
outline,
|
|
218
|
+
mode: 'edit',
|
|
219
|
+
dirty: false,
|
|
220
|
+
activeHeadingId: outline[0]?.id ?? null,
|
|
221
|
+
pendingAnchor: null,
|
|
222
|
+
notice: null,
|
|
223
|
+
error: null,
|
|
224
|
+
saving: false,
|
|
225
|
+
loadingDocument: false,
|
|
226
|
+
editorView: 'markdown',
|
|
227
|
+
editorScrollTop: 0,
|
|
228
|
+
saveIndicator: 'idle',
|
|
229
|
+
fileDeleted: false,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
return workspaceState;
|
|
233
|
+
}
|
|
234
|
+
function isUndoAvailable(state) {
|
|
235
|
+
return state.draftContent !== state.fullDocumentContent;
|
|
236
|
+
}
|
|
237
|
+
function buildBody(payload) {
|
|
238
|
+
const state = getState(payload);
|
|
239
|
+
const outline = state.outline;
|
|
240
|
+
const isFullscreen = dependencies.getCurrentDisplayMode() === 'fullscreen';
|
|
241
|
+
const tocHtml = isFullscreen ? renderDocumentOutline(outline, state.activeHeadingId) : '';
|
|
242
|
+
if (!state.activeHeadingId && outline.length > 0) {
|
|
243
|
+
state.activeHeadingId = outline[0].id;
|
|
244
|
+
}
|
|
245
|
+
const notice = [state.error, state.notice]
|
|
246
|
+
.find((value) => typeof value === 'string' && value.trim().length > 0);
|
|
247
|
+
return {
|
|
248
|
+
notice,
|
|
249
|
+
html: `
|
|
250
|
+
<div class="panel-content markdown-content markdown-content--workspace">
|
|
251
|
+
<div class="markdown-workspace markdown-workspace--edit${tocHtml ? ' markdown-workspace--with-toc' : ''}">
|
|
252
|
+
${tocHtml}
|
|
253
|
+
<section class="markdown-workspace-main markdown-workspace-main--editor">
|
|
254
|
+
${renderMarkdownEditorShell({ view: state.editorView })}
|
|
255
|
+
</section>
|
|
256
|
+
</div>
|
|
257
|
+
</div>
|
|
258
|
+
`,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
async function resolveLinkSearchRoot(filePath) {
|
|
262
|
+
const ancestors = getAncestorDirectories(filePath);
|
|
263
|
+
const markers = new Set(['[DIR] .git', '[DIR] .obsidian', '[FILE] package.json', '[FILE] pnpm-workspace.yaml', '[FILE] turbo.json']);
|
|
264
|
+
for (const ancestor of ancestors) {
|
|
265
|
+
try {
|
|
266
|
+
const result = await dependencies.callTool?.('list_directory', { path: ancestor, depth: 1 });
|
|
267
|
+
const text = extractToolText(result) ?? '';
|
|
268
|
+
const entries = splitListingLines(text);
|
|
269
|
+
if (entries.some((entry) => markers.has(entry))) {
|
|
270
|
+
return ancestor;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
catch {
|
|
274
|
+
// Ignore and continue up the tree.
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return getParentDirectory(filePath);
|
|
278
|
+
}
|
|
279
|
+
async function searchLinkTargets(filePath, query) {
|
|
280
|
+
const trimmedQuery = query.trim();
|
|
281
|
+
if (trimmedQuery.length === 0) {
|
|
282
|
+
return [];
|
|
283
|
+
}
|
|
284
|
+
const rootPath = await resolveLinkSearchRoot(filePath);
|
|
285
|
+
const result = await dependencies.callTool?.('start_search', {
|
|
286
|
+
path: rootPath,
|
|
287
|
+
pattern: trimmedQuery,
|
|
288
|
+
searchType: 'files',
|
|
289
|
+
filePattern: '*.md',
|
|
290
|
+
maxResults: 20,
|
|
291
|
+
earlyTermination: false,
|
|
292
|
+
literalSearch: true,
|
|
293
|
+
});
|
|
294
|
+
const text = extractToolText(result) ?? '';
|
|
295
|
+
const filePaths = parseFileSearchResults(text);
|
|
296
|
+
const currentDirectory = getParentDirectory(filePath);
|
|
297
|
+
return filePaths.map((targetPath) => {
|
|
298
|
+
const normalized = targetPath.replace(/\\/g, '/');
|
|
299
|
+
const fileName = normalized.split('/').pop() ?? normalized;
|
|
300
|
+
const title = stripMarkdownExtension(fileName);
|
|
301
|
+
const relativePath = toPosixRelativePath(currentDirectory, normalized);
|
|
302
|
+
const wikiPath = stripMarkdownExtension(relativePath.startsWith('./') ? relativePath.slice(2) : relativePath);
|
|
303
|
+
return {
|
|
304
|
+
path: normalized,
|
|
305
|
+
title,
|
|
306
|
+
wikiPath,
|
|
307
|
+
relativePath,
|
|
308
|
+
};
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
async function loadLinkHeadings(currentPayloadPath, targetPath) {
|
|
312
|
+
if (targetPath === currentPayloadPath && workspaceState) {
|
|
313
|
+
return workspaceState.outline.map((item) => ({ id: item.id, text: item.text }));
|
|
314
|
+
}
|
|
315
|
+
const payload = await readCompletePayload(targetPath);
|
|
316
|
+
if (!payload) {
|
|
317
|
+
return [];
|
|
318
|
+
}
|
|
319
|
+
return extractMarkdownOutline(readPayloadContent(payload)).map((item) => ({ id: item.id, text: item.text }));
|
|
320
|
+
}
|
|
321
|
+
function findHeading(anchor) {
|
|
322
|
+
const trimmedAnchor = anchor.trim();
|
|
323
|
+
if (!trimmedAnchor) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return document.getElementById(trimmedAnchor) ?? document.getElementById(slugifyMarkdownHeading(trimmedAnchor));
|
|
327
|
+
}
|
|
328
|
+
function scrollHeadingIntoView(anchor) {
|
|
329
|
+
const heading = findHeading(anchor);
|
|
330
|
+
if (!heading) {
|
|
331
|
+
return false;
|
|
332
|
+
}
|
|
333
|
+
const scrollParents = [];
|
|
334
|
+
let current = heading.parentElement;
|
|
335
|
+
while (current) {
|
|
336
|
+
const style = window.getComputedStyle(current);
|
|
337
|
+
const overflowY = style.overflowY;
|
|
338
|
+
const isScrollable = (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay')
|
|
339
|
+
&& current.scrollHeight > current.clientHeight;
|
|
340
|
+
if (isScrollable) {
|
|
341
|
+
scrollParents.push(current);
|
|
342
|
+
}
|
|
343
|
+
current = current.parentElement;
|
|
344
|
+
}
|
|
345
|
+
heading.scrollIntoView({ block: 'start', inline: 'nearest' });
|
|
346
|
+
for (const parent of scrollParents) {
|
|
347
|
+
const parentRect = parent.getBoundingClientRect();
|
|
348
|
+
const headingRect = heading.getBoundingClientRect();
|
|
349
|
+
const nextTop = Math.max(parent.scrollTop + (headingRect.top - parentRect.top) - 24, 0);
|
|
350
|
+
parent.scrollTop = nextTop;
|
|
351
|
+
}
|
|
352
|
+
const rootScroller = document.scrollingElement;
|
|
353
|
+
if (rootScroller) {
|
|
354
|
+
const rootRectTop = heading.getBoundingClientRect().top;
|
|
355
|
+
const nextRootTop = Math.max(rootScroller.scrollTop + rootRectTop - 24, 0);
|
|
356
|
+
rootScroller.scrollTop = nextRootTop;
|
|
357
|
+
}
|
|
358
|
+
heading.setAttribute('tabindex', '-1');
|
|
359
|
+
heading.focus({ preventScroll: true });
|
|
360
|
+
if (workspaceState) {
|
|
361
|
+
workspaceState.activeHeadingId = heading.id || slugifyMarkdownHeading(anchor);
|
|
362
|
+
}
|
|
363
|
+
return true;
|
|
364
|
+
}
|
|
365
|
+
function applyPendingAnchor() {
|
|
366
|
+
const pendingAnchor = workspaceState?.pendingAnchor;
|
|
367
|
+
if (!workspaceState || !pendingAnchor) {
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
370
|
+
workspaceState.pendingAnchor = null;
|
|
371
|
+
if (!scrollHeadingIntoView(pendingAnchor)) {
|
|
372
|
+
workspaceState.error = `Heading not found: ${pendingAnchor}`;
|
|
373
|
+
dependencies.rerender();
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
function flashSaveStatus(label, statusClass, timeoutMs, beforeClear) {
|
|
377
|
+
dependencies.updateSaveStatus(label, statusClass);
|
|
378
|
+
window.setTimeout(() => {
|
|
379
|
+
if (beforeClear && !beforeClear()) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
dependencies.updateSaveStatus('', '');
|
|
383
|
+
}, timeoutMs);
|
|
384
|
+
}
|
|
385
|
+
async function refreshFromDisk(payload) {
|
|
386
|
+
try {
|
|
387
|
+
const range = parseReadRange(payload.content);
|
|
388
|
+
const { rawResult, payload: freshPayload } = range?.isPartial
|
|
389
|
+
? await callReadFile(payload.filePath, range.toLine - range.fromLine + 1, range.readOffset)
|
|
390
|
+
: await callReadFile(payload.filePath);
|
|
391
|
+
if (!freshPayload) {
|
|
392
|
+
if (isMissingFileErrorResult(rawResult)) {
|
|
393
|
+
if (workspaceState) {
|
|
394
|
+
workspaceState.fileDeleted = true;
|
|
395
|
+
}
|
|
396
|
+
dependencies.updateSaveStatus('File deleted', 'saved');
|
|
397
|
+
}
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
const freshContent = readPayloadContent(freshPayload);
|
|
401
|
+
const currentContent = readPayloadContent(payload);
|
|
402
|
+
if (freshContent === currentContent) {
|
|
403
|
+
return;
|
|
404
|
+
}
|
|
405
|
+
// refreshFromDisk only runs at mount (no file watcher in this app),
|
|
406
|
+
// so disk-vs-payload mismatch means the host sent a stale cached
|
|
407
|
+
// payload — trust the disk read and reload silently.
|
|
408
|
+
dependencies.storePayloadOverride(freshPayload);
|
|
409
|
+
workspaceState = undefined;
|
|
410
|
+
dependencies.rerender();
|
|
411
|
+
}
|
|
412
|
+
catch {
|
|
413
|
+
// Silently fall back to host payload.
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
async function loadFullDocument(payload, options = {}) {
|
|
417
|
+
const state = getState(payload);
|
|
418
|
+
const range = parseReadRange(payload.content);
|
|
419
|
+
if (!range?.isPartial) {
|
|
420
|
+
if (options.keepEditMode) {
|
|
421
|
+
state.mode = 'edit';
|
|
422
|
+
state.editorView = 'markdown';
|
|
423
|
+
state.notice = null;
|
|
424
|
+
state.error = null;
|
|
425
|
+
state.draftContent = state.sourceContent;
|
|
426
|
+
state.dirty = false;
|
|
427
|
+
dependencies.rerender();
|
|
428
|
+
}
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
state.loadingDocument = true;
|
|
432
|
+
state.notice = 'Loading full document…';
|
|
433
|
+
state.error = null;
|
|
434
|
+
dependencies.rerender();
|
|
435
|
+
try {
|
|
436
|
+
const nextPayload = await readPayload(payload.filePath, range.totalLines);
|
|
437
|
+
if (!nextPayload) {
|
|
438
|
+
state.error = 'Failed to load the full document.';
|
|
439
|
+
state.notice = null;
|
|
440
|
+
state.loadingDocument = false;
|
|
441
|
+
dependencies.rerender();
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
dependencies.syncPayload?.(nextPayload);
|
|
445
|
+
const nextState = getState(nextPayload);
|
|
446
|
+
nextState.loadingDocument = false;
|
|
447
|
+
nextState.notice = null;
|
|
448
|
+
nextState.error = null;
|
|
449
|
+
syncStateFromContent(nextState, nextState.sourceContent);
|
|
450
|
+
if (options.keepEditMode) {
|
|
451
|
+
nextState.mode = 'edit';
|
|
452
|
+
nextState.editorView = 'markdown';
|
|
453
|
+
dependencies.rerender();
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
state.loadingDocument = false;
|
|
458
|
+
state.notice = null;
|
|
459
|
+
state.error = 'Failed to load the full document.';
|
|
460
|
+
dependencies.rerender();
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
async function navigateLink(payload, href) {
|
|
464
|
+
const state = getState(payload);
|
|
465
|
+
if (state.dirty) {
|
|
466
|
+
const shouldDiscard = window.confirm('Discard unsaved changes and follow this link?');
|
|
467
|
+
if (!shouldDiscard) {
|
|
468
|
+
return;
|
|
469
|
+
}
|
|
470
|
+
}
|
|
471
|
+
const resolvedLink = resolveMarkdownLink(payload.filePath, href);
|
|
472
|
+
state.notice = null;
|
|
473
|
+
state.error = null;
|
|
474
|
+
if (resolvedLink.kind === 'external' && resolvedLink.url) {
|
|
475
|
+
const opened = await dependencies.openExternalLink?.(resolvedLink.url);
|
|
476
|
+
if (!opened) {
|
|
477
|
+
try {
|
|
478
|
+
window.open(resolvedLink.url, '_blank', 'noopener');
|
|
479
|
+
}
|
|
480
|
+
catch { /* sandbox may block */ }
|
|
481
|
+
}
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
if (resolvedLink.kind === 'anchor' && resolvedLink.anchor) {
|
|
485
|
+
if (!scrollHeadingIntoView(resolvedLink.anchor) && workspaceState) {
|
|
486
|
+
workspaceState.error = `Heading not found: ${resolvedLink.anchor}`;
|
|
487
|
+
dependencies.rerender();
|
|
488
|
+
}
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
if (resolvedLink.kind === 'file' && resolvedLink.targetPath) {
|
|
492
|
+
const hostHandled = await dependencies.openExternalLink?.(resolvedLink.targetPath);
|
|
493
|
+
if (hostHandled) {
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const nextPayload = await readPayload(resolvedLink.targetPath);
|
|
497
|
+
if (!nextPayload) {
|
|
498
|
+
if (workspaceState) {
|
|
499
|
+
workspaceState.error = `Unable to open ${resolvedLink.targetPath}.`;
|
|
500
|
+
dependencies.rerender();
|
|
501
|
+
}
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
dependencies.syncPayload?.(nextPayload);
|
|
505
|
+
const nextState = getState(nextPayload);
|
|
506
|
+
nextState.pendingAnchor = resolvedLink.anchor ?? null;
|
|
507
|
+
nextState.error = null;
|
|
508
|
+
nextState.notice = null;
|
|
509
|
+
dependencies.rerender();
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
async function requestEditMode(payload) {
|
|
513
|
+
const state = getState(payload);
|
|
514
|
+
state.error = null;
|
|
515
|
+
state.notice = null;
|
|
516
|
+
if (shouldAutoLoadDocumentOnEnterFullscreen(payload.content)) {
|
|
517
|
+
await loadFullDocument(payload, { keepEditMode: true });
|
|
518
|
+
return;
|
|
519
|
+
}
|
|
520
|
+
state.mode = 'edit';
|
|
521
|
+
state.draftContent = state.fullDocumentContent;
|
|
522
|
+
state.dirty = false;
|
|
523
|
+
state.editorView = 'markdown';
|
|
524
|
+
dependencies.setExpanded(true);
|
|
525
|
+
dependencies.rerender();
|
|
526
|
+
}
|
|
527
|
+
async function requestFullscreen() {
|
|
528
|
+
const fullscreenAvailability = getDocumentFullscreenAvailability({
|
|
529
|
+
availableDisplayModes: dependencies.getAvailableDisplayModes(),
|
|
530
|
+
});
|
|
531
|
+
if (!fullscreenAvailability.canFullscreen) {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
const nextMode = await dependencies.requestDisplayMode?.('fullscreen');
|
|
535
|
+
return nextMode === 'fullscreen';
|
|
536
|
+
}
|
|
537
|
+
function revertEditing() {
|
|
538
|
+
if (!workspaceState) {
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
const filePath = workspaceState.filePath;
|
|
542
|
+
workspaceState.draftContent = workspaceState.fullDocumentContent;
|
|
543
|
+
workspaceState.dirty = false;
|
|
544
|
+
workspaceState.error = null;
|
|
545
|
+
workspaceState.notice = null;
|
|
546
|
+
dependencies.rerender();
|
|
547
|
+
flashSaveStatus('Reverted', 'saved', 1500);
|
|
548
|
+
dependencies.trackUiEvent?.('markdown_reverted', {
|
|
549
|
+
file_extension: getFileExtensionForAnalytics(filePath),
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
async function saveDocument() {
|
|
553
|
+
if (!workspaceState || workspaceState.saving || !workspaceState.dirty || workspaceState.fileDeleted) {
|
|
554
|
+
return;
|
|
555
|
+
}
|
|
556
|
+
cancelAutosave();
|
|
557
|
+
const state = workspaceState;
|
|
558
|
+
state.saving = true;
|
|
559
|
+
state.saveIndicator = 'saving';
|
|
560
|
+
state.error = null;
|
|
561
|
+
state.notice = null;
|
|
562
|
+
try {
|
|
563
|
+
const blocks = computeEditBlocks(state.fullDocumentContent, state.draftContent);
|
|
564
|
+
if (blocks.length === 0) {
|
|
565
|
+
state.saving = false;
|
|
566
|
+
state.saveIndicator = 'idle';
|
|
567
|
+
state.dirty = false;
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
// Try each hunk independently. Previously the loop threw on the
|
|
571
|
+
// first soft-failure, which left earlier hunks already written to
|
|
572
|
+
// disk while the UI claimed "Save failed" — making it look like the
|
|
573
|
+
// editor had silently overwritten external changes. Now we track
|
|
574
|
+
// per-hunk outcomes so we can give the user an honest accounting.
|
|
575
|
+
let appliedCount = 0;
|
|
576
|
+
const skippedHunks = [];
|
|
577
|
+
let lastHardError = null;
|
|
578
|
+
for (const block of blocks) {
|
|
579
|
+
try {
|
|
580
|
+
const editResult = await dependencies.callTool?.('edit_block', {
|
|
581
|
+
file_path: state.filePath,
|
|
582
|
+
old_string: block.old_string,
|
|
583
|
+
new_string: block.new_string,
|
|
584
|
+
expected_replacements: 1,
|
|
585
|
+
});
|
|
586
|
+
assertSuccessfulEditBlockResult(editResult);
|
|
587
|
+
appliedCount++;
|
|
588
|
+
}
|
|
589
|
+
catch (hunkError) {
|
|
590
|
+
// A per-hunk failure is almost always "old_string not on
|
|
591
|
+
// disk" because the file changed there. Record it and keep
|
|
592
|
+
// going so other hunks still get their chance.
|
|
593
|
+
skippedHunks.push(block);
|
|
594
|
+
lastHardError = hunkError;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
if (skippedHunks.length > 0) {
|
|
598
|
+
// Partial (or total) failure. Let the catch branch take it —
|
|
599
|
+
// throw an error carrying counts so the catch can decide how
|
|
600
|
+
// to communicate and how to resync baseline vs. disk.
|
|
601
|
+
const err = new Error(`${appliedCount} of ${blocks.length} edit${blocks.length === 1 ? '' : 's'} applied; ` +
|
|
602
|
+
`${skippedHunks.length} could not land because the text changed on disk.`);
|
|
603
|
+
err.appliedCount = appliedCount;
|
|
604
|
+
err.skippedCount = skippedHunks.length;
|
|
605
|
+
err.totalCount = blocks.length;
|
|
606
|
+
err.underlyingError = lastHardError;
|
|
607
|
+
throw err;
|
|
608
|
+
}
|
|
609
|
+
state.fullDocumentContent = state.draftContent;
|
|
610
|
+
state.sourceContent = state.draftContent;
|
|
611
|
+
state.outline = extractMarkdownOutline(state.sourceContent);
|
|
612
|
+
state.dirty = false;
|
|
613
|
+
state.saving = false;
|
|
614
|
+
state.saveIndicator = 'saved';
|
|
615
|
+
if (!state.outline.some((item) => item.id === state.activeHeadingId)) {
|
|
616
|
+
state.activeHeadingId = state.outline[0]?.id ?? null;
|
|
617
|
+
}
|
|
618
|
+
const savedContent = state.draftContent;
|
|
619
|
+
const currentPayload = dependencies.getCurrentPayload();
|
|
620
|
+
if (currentPayload) {
|
|
621
|
+
const statusLineMatch = currentPayload.content.match(/^(\[Reading [^\]]+\]\r?\n(?:\r?\n)?)/);
|
|
622
|
+
const statusLine = statusLineMatch?.[1] ?? '';
|
|
623
|
+
dependencies.storePayloadOverride({ ...currentPayload, content: statusLine + savedContent });
|
|
624
|
+
}
|
|
625
|
+
const revert = document.getElementById('revert-markdown');
|
|
626
|
+
if (revert) {
|
|
627
|
+
revert.disabled = !isUndoAvailable(state);
|
|
628
|
+
}
|
|
629
|
+
flashSaveStatus('Saved', 'saved', 1800, () => {
|
|
630
|
+
if (!state.dirty && !state.saving) {
|
|
631
|
+
state.saveIndicator = 'idle';
|
|
632
|
+
return true;
|
|
633
|
+
}
|
|
634
|
+
return false;
|
|
635
|
+
});
|
|
636
|
+
dependencies.trackUiEvent?.('markdown_saved', {
|
|
637
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
638
|
+
blocks: blocks.length,
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
catch (error) {
|
|
642
|
+
state.saving = false;
|
|
643
|
+
state.saveIndicator = 'idle';
|
|
644
|
+
// Pull per-hunk counts from the synthetic error thrown by the save
|
|
645
|
+
// loop, when this catch is reached via a partial/total hunk failure.
|
|
646
|
+
const errWithCounts = error;
|
|
647
|
+
const appliedCount = typeof errWithCounts.appliedCount === 'number' ? errWithCounts.appliedCount : 0;
|
|
648
|
+
const skippedCount = typeof errWithCounts.skippedCount === 'number' ? errWithCounts.skippedCount : 0;
|
|
649
|
+
const totalCount = typeof errWithCounts.totalCount === 'number' ? errWithCounts.totalCount : 0;
|
|
650
|
+
const isPartialSuccess = appliedCount > 0 && skippedCount > 0;
|
|
651
|
+
const isTotalFailure = appliedCount === 0 && skippedCount > 0;
|
|
652
|
+
const freshPayload = await readCompletePayload(state.filePath).catch(() => null);
|
|
653
|
+
let reloadedFromDisk = false;
|
|
654
|
+
let freshContentForDialog = null;
|
|
655
|
+
if (freshPayload) {
|
|
656
|
+
const freshContent = readPayloadContent(freshPayload);
|
|
657
|
+
if (freshContent !== state.fullDocumentContent) {
|
|
658
|
+
syncStateFromContent(state, freshContent, { keepDraft: true });
|
|
659
|
+
dependencies.storePayloadOverride(freshPayload);
|
|
660
|
+
reloadedFromDisk = true;
|
|
661
|
+
freshContentForDialog = freshContent;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
state.notice = null;
|
|
665
|
+
if (isPartialSuccess) {
|
|
666
|
+
// Some hunks landed on disk, some didn't. The user would be
|
|
667
|
+
// misled by a "Save failed" message — and would be misled by
|
|
668
|
+
// a conflict dialog claiming nothing was saved. Tell them the
|
|
669
|
+
// truth: N saved, M skipped, with the skipped lines preserved
|
|
670
|
+
// in their draft so they can re-try or edit around the
|
|
671
|
+
// external change.
|
|
672
|
+
state.error = (`${appliedCount} of ${totalCount} edit${totalCount === 1 ? '' : 's'} saved. ` +
|
|
673
|
+
`${skippedCount} ${skippedCount === 1 ? 'edit' : 'edits'} did not apply because that text changed on disk — ` +
|
|
674
|
+
`your draft still has them; save again to merge.`);
|
|
675
|
+
dependencies.rerender();
|
|
676
|
+
flashSaveStatus('Saved (partial)', 'saved', 3000);
|
|
677
|
+
dependencies.trackUiEvent?.('markdown_save_partial', {
|
|
678
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
679
|
+
applied: appliedCount,
|
|
680
|
+
skipped: skippedCount,
|
|
681
|
+
total: totalCount,
|
|
682
|
+
});
|
|
683
|
+
}
|
|
684
|
+
else if (isTotalFailure && reloadedFromDisk && dependencies.showConflictDialog && freshContentForDialog !== null) {
|
|
685
|
+
// No hunks landed and disk has changed. Genuine conflict — show
|
|
686
|
+
// the modal so the user can pick keep-mine / use-disk.
|
|
687
|
+
const savedFreshContent = freshContentForDialog;
|
|
688
|
+
const normalized = state.filePath.replace(/\\/g, '/');
|
|
689
|
+
const displayName = normalized.split('/').pop() || state.filePath;
|
|
690
|
+
state.error = null;
|
|
691
|
+
dependencies.rerender();
|
|
692
|
+
dependencies.trackUiEvent?.('markdown_save_conflict_shown', {
|
|
693
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
694
|
+
});
|
|
695
|
+
dependencies.showConflictDialog({
|
|
696
|
+
fileName: displayName,
|
|
697
|
+
onUseDiskVersion: () => {
|
|
698
|
+
if (workspaceState === state) {
|
|
699
|
+
syncStateFromContent(state, savedFreshContent);
|
|
700
|
+
dependencies.rerender();
|
|
701
|
+
}
|
|
702
|
+
dependencies.trackUiEvent?.('markdown_save_conflict_resolved', {
|
|
703
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
704
|
+
action: 'use_disk',
|
|
705
|
+
});
|
|
706
|
+
},
|
|
707
|
+
onSaveMyChanges: () => {
|
|
708
|
+
dependencies.trackUiEvent?.('markdown_save_conflict_resolved', {
|
|
709
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
710
|
+
action: 'save_mine',
|
|
711
|
+
});
|
|
712
|
+
// Re-run saveDocument. computeEditBlocks will diff against
|
|
713
|
+
// the fresh sourceContent that keepDraft: true left in place,
|
|
714
|
+
// so hunks the user actually modified win over disk on
|
|
715
|
+
// those specific lines and disk-only changes elsewhere are
|
|
716
|
+
// preserved.
|
|
717
|
+
void saveDocument();
|
|
718
|
+
},
|
|
719
|
+
onCancel: () => {
|
|
720
|
+
if (workspaceState === state) {
|
|
721
|
+
state.error = 'File changed on disk. Save again to merge your edits, or reopen to discard them.';
|
|
722
|
+
dependencies.rerender();
|
|
723
|
+
}
|
|
724
|
+
dependencies.trackUiEvent?.('markdown_save_conflict_resolved', {
|
|
725
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
726
|
+
action: 'dismissed',
|
|
727
|
+
});
|
|
728
|
+
},
|
|
729
|
+
});
|
|
730
|
+
}
|
|
731
|
+
else {
|
|
732
|
+
// Fallback: unexpected error that isn't a per-hunk soft-fail
|
|
733
|
+
// (e.g. read_file failure during resync, or an exception before
|
|
734
|
+
// the save loop started). Use a generic inline message.
|
|
735
|
+
state.error = reloadedFromDisk
|
|
736
|
+
? 'File changed on disk. Save again to merge your edits, or reopen the file to discard them.'
|
|
737
|
+
: error instanceof Error ? error.message : 'Save failed.';
|
|
738
|
+
dependencies.rerender();
|
|
739
|
+
flashSaveStatus('Save failed', 'saving', 3000);
|
|
740
|
+
}
|
|
741
|
+
dependencies.trackUiEvent?.('markdown_save_failed', {
|
|
742
|
+
file_extension: getFileExtensionForAnalytics(state.filePath),
|
|
743
|
+
reloaded_from_disk: reloadedFromDisk,
|
|
744
|
+
applied: appliedCount,
|
|
745
|
+
skipped: skippedCount,
|
|
746
|
+
total: totalCount,
|
|
747
|
+
});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
function setEditorView(payload, view) {
|
|
751
|
+
const state = getState(payload);
|
|
752
|
+
const wrapper = document.querySelector('.panel-content-wrapper');
|
|
753
|
+
state.editorScrollTop = wrapper?.scrollTop ?? 0;
|
|
754
|
+
const previousView = state.editorView;
|
|
755
|
+
state.editorView = view;
|
|
756
|
+
state.notice = null;
|
|
757
|
+
state.error = null;
|
|
758
|
+
dependencies.rerender();
|
|
759
|
+
if (previousView !== view) {
|
|
760
|
+
dependencies.trackUiEvent?.('markdown_view_toggled', {
|
|
761
|
+
file_extension: getFileExtensionForAnalytics(payload.filePath),
|
|
762
|
+
view,
|
|
763
|
+
});
|
|
764
|
+
}
|
|
765
|
+
if (typeof state.editorScrollTop === 'number') {
|
|
766
|
+
window.requestAnimationFrame(() => {
|
|
767
|
+
const nextWrapper = document.querySelector('.panel-content-wrapper');
|
|
768
|
+
if (nextWrapper) {
|
|
769
|
+
nextWrapper.scrollTop = state.editorScrollTop;
|
|
770
|
+
}
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
function attachHandlers(payload) {
|
|
775
|
+
const state = getState(payload);
|
|
776
|
+
const wrapper = document.querySelector('.panel-content-wrapper');
|
|
777
|
+
const outline = state.outline;
|
|
778
|
+
const fileExtension = getFileExtensionForAnalytics(payload.filePath);
|
|
779
|
+
let editStartedFired = false;
|
|
780
|
+
{
|
|
781
|
+
const editorRoot = document.getElementById('markdown-editor-root');
|
|
782
|
+
if (editorRoot) {
|
|
783
|
+
markdownEditorHandle = mountMarkdownEditor({
|
|
784
|
+
target: editorRoot,
|
|
785
|
+
value: state.draftContent,
|
|
786
|
+
view: state.editorView,
|
|
787
|
+
initialScrollTop: state.editorScrollTop,
|
|
788
|
+
currentFilePath: payload.filePath,
|
|
789
|
+
searchLinks: (query) => searchLinkTargets(payload.filePath, query),
|
|
790
|
+
loadHeadings: (targetPath) => loadLinkHeadings(payload.filePath, targetPath),
|
|
791
|
+
onChange: (value) => {
|
|
792
|
+
state.draftContent = value;
|
|
793
|
+
state.dirty = value !== state.fullDocumentContent;
|
|
794
|
+
if (state.dirty && !editStartedFired) {
|
|
795
|
+
editStartedFired = true;
|
|
796
|
+
dependencies.trackUiEvent?.('markdown_edit_started', {
|
|
797
|
+
file_extension: fileExtension,
|
|
798
|
+
view: state.editorView,
|
|
799
|
+
});
|
|
800
|
+
}
|
|
801
|
+
if (state.dirty) {
|
|
802
|
+
scheduleAutosave();
|
|
803
|
+
}
|
|
804
|
+
const nextOutline = extractMarkdownOutline(value);
|
|
805
|
+
if (!areOutlineItemsEqual(state.outline, nextOutline)) {
|
|
806
|
+
state.outline = nextOutline;
|
|
807
|
+
if (!state.outline.some((item) => item.id === state.activeHeadingId)) {
|
|
808
|
+
state.activeHeadingId = state.outline[0]?.id ?? null;
|
|
809
|
+
}
|
|
810
|
+
markdownTocHandle?.refresh(state.outline, state.activeHeadingId);
|
|
811
|
+
}
|
|
812
|
+
if (state.dirty && state.saveIndicator === 'saved') {
|
|
813
|
+
state.saveIndicator = 'idle';
|
|
814
|
+
}
|
|
815
|
+
const revert = document.getElementById('revert-markdown');
|
|
816
|
+
if (revert) {
|
|
817
|
+
revert.disabled = !isUndoAvailable(state);
|
|
818
|
+
}
|
|
819
|
+
},
|
|
820
|
+
onBlur: () => {
|
|
821
|
+
cancelAutosave();
|
|
822
|
+
void saveDocument();
|
|
823
|
+
},
|
|
824
|
+
});
|
|
825
|
+
markdownEditorHandle.focus();
|
|
826
|
+
}
|
|
827
|
+
const revertButton = document.getElementById('revert-markdown');
|
|
828
|
+
revertButton?.addEventListener('click', () => {
|
|
829
|
+
revertEditing();
|
|
830
|
+
});
|
|
831
|
+
const rawModeButton = document.getElementById('markdown-mode-raw');
|
|
832
|
+
rawModeButton?.addEventListener('click', () => {
|
|
833
|
+
setEditorView(payload, 'raw');
|
|
834
|
+
});
|
|
835
|
+
const previewModeButton = document.getElementById('markdown-mode-markdown');
|
|
836
|
+
previewModeButton?.addEventListener('click', () => {
|
|
837
|
+
setEditorView(payload, 'markdown');
|
|
838
|
+
});
|
|
839
|
+
}
|
|
840
|
+
const expandButton = document.getElementById('expand-fullscreen');
|
|
841
|
+
expandButton?.addEventListener('click', () => {
|
|
842
|
+
void requestFullscreen();
|
|
843
|
+
});
|
|
844
|
+
if (wrapper) {
|
|
845
|
+
wrapper.addEventListener('click', (event) => {
|
|
846
|
+
const target = event.target;
|
|
847
|
+
const link = target?.closest('a[href]');
|
|
848
|
+
if (!link || !link.closest('.markdown-doc')) {
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
const href = link.getAttribute('href');
|
|
852
|
+
if (!href) {
|
|
853
|
+
return;
|
|
854
|
+
}
|
|
855
|
+
event.preventDefault();
|
|
856
|
+
void navigateLink(payload, href);
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
const tocShell = document.querySelector('.document-outline-shell');
|
|
860
|
+
if (tocShell && wrapper) {
|
|
861
|
+
markdownTocHandle = attachDocumentOutline({
|
|
862
|
+
shell: tocShell,
|
|
863
|
+
outline,
|
|
864
|
+
scrollContainer: wrapper,
|
|
865
|
+
onSelect: (headingId) => {
|
|
866
|
+
const selectedHeading = state.outline.find((item) => item.id === headingId);
|
|
867
|
+
if (selectedHeading && typeof selectedHeading.line === 'number') {
|
|
868
|
+
markdownEditorHandle?.revealLine(selectedHeading.line, selectedHeading.id);
|
|
869
|
+
state.activeHeadingId = selectedHeading.id;
|
|
870
|
+
}
|
|
871
|
+
},
|
|
872
|
+
}) ?? undefined;
|
|
873
|
+
}
|
|
874
|
+
window.setTimeout(() => {
|
|
875
|
+
applyPendingAnchor();
|
|
876
|
+
}, 0);
|
|
877
|
+
}
|
|
878
|
+
function getCopyText(payload) {
|
|
879
|
+
const state = getState(payload);
|
|
880
|
+
const source = state.draftContent;
|
|
881
|
+
return state.editorView === 'raw'
|
|
882
|
+
? source
|
|
883
|
+
: (getRenderedMarkdownCopyText(source) || source);
|
|
884
|
+
}
|
|
885
|
+
async function handleInlineExitFromFullscreen(originalPayload) {
|
|
886
|
+
const wasDirty = workspaceState?.saveIndicator === 'saved' || workspaceState?.dirty;
|
|
887
|
+
if (workspaceState) {
|
|
888
|
+
workspaceState.notice = null;
|
|
889
|
+
workspaceState.editorView = 'markdown';
|
|
890
|
+
}
|
|
891
|
+
if (wasDirty && originalPayload) {
|
|
892
|
+
const range = parseReadRange(originalPayload.content);
|
|
893
|
+
if (range?.isPartial) {
|
|
894
|
+
const freshPayload = await readPayload(originalPayload.filePath, range.toLine - range.fromLine + 1, range.readOffset);
|
|
895
|
+
if (freshPayload) {
|
|
896
|
+
return freshPayload;
|
|
897
|
+
}
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
return undefined;
|
|
901
|
+
}
|
|
902
|
+
return {
|
|
903
|
+
attachHandlers,
|
|
904
|
+
buildBody,
|
|
905
|
+
clear,
|
|
906
|
+
disposeHandles,
|
|
907
|
+
ensureCompletePayload,
|
|
908
|
+
getCopyText,
|
|
909
|
+
getState,
|
|
910
|
+
handleInlineExitFromFullscreen,
|
|
911
|
+
isUndoAvailable,
|
|
912
|
+
readCompletePayload,
|
|
913
|
+
readPayload,
|
|
914
|
+
readPayloadContent,
|
|
915
|
+
refreshFromDisk,
|
|
916
|
+
requestEditMode,
|
|
917
|
+
requestFullscreen,
|
|
918
|
+
saveDocument,
|
|
919
|
+
setEditorView,
|
|
920
|
+
};
|
|
921
|
+
}
|