@wonderwhy-er/desktop-commander 0.2.37 → 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 +290 -100
- package/dist/command-manager.js +6 -3
- package/dist/config-field-definitions.d.ts +41 -0
- package/dist/config-field-definitions.js +37 -0
- package/dist/config-manager.d.ts +2 -0
- package/dist/config-manager.js +22 -2
- package/dist/handlers/filesystem-handlers.d.ts +5 -0
- package/dist/handlers/filesystem-handlers.js +19 -12
- package/dist/remote-device/desktop-commander-integration.js +1 -1
- package/dist/remote-device/remote-channel.js +1 -1
- package/dist/search-manager.js +31 -38
- package/dist/server.js +11 -4
- package/dist/terminal-manager.js +4 -2
- package/dist/tools/config.d.ts +71 -0
- package/dist/tools/config.js +117 -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/tools/schemas.d.ts +3 -0
- package/dist/tools/schemas.js +1 -0
- package/dist/types.d.ts +0 -1
- package/dist/ui/config-editor/app.d.ts +43 -0
- 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 +150 -0
- package/dist/ui/config-editor/index.html +13 -0
- 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/App.js +840 -0
- package/dist/ui/config-editor/src/array-modal.d.ts +19 -0
- package/dist/ui/config-editor/src/array-modal.js +185 -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/main.js +2 -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 +587 -0
- package/dist/ui/file-preview/app.d.ts +8 -0
- 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 +399 -13969
- package/dist/ui/file-preview/shared/preview-file-types.d.ts +1 -1
- package/dist/ui/file-preview/shared/preview-file-types.js +3 -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/html-renderer.d.ts +1 -5
- package/dist/ui/file-preview/src/components/html-renderer.js +11 -27
- 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 +1144 -277
- package/dist/ui/file-preview/types.d.ts +1 -0
- package/dist/ui/file-preview/types.js +1 -0
- package/dist/ui/resources.d.ts +7 -0
- package/dist/ui/resources.js +16 -2
- 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/compact-row.d.ts +11 -0
- package/dist/ui/shared/compact-row.js +18 -0
- package/dist/ui/shared/guards.d.ts +1 -0
- package/dist/ui/shared/guards.js +3 -0
- package/dist/ui/shared/host-context.d.ts +15 -0
- package/dist/ui/shared/host-context.js +51 -0
- package/dist/ui/shared/host-lifecycle.d.ts +1 -0
- package/dist/ui/shared/host-lifecycle.js +8 -2
- package/dist/ui/shared/tool-bridge.d.ts +30 -0
- package/dist/ui/shared/tool-bridge.js +137 -0
- package/dist/ui/shared/tool-shell.d.ts +9 -0
- package/dist/ui/shared/tool-shell.js +46 -4
- package/dist/ui/shared/ui-event-tracker.d.ts +9 -0
- package/dist/ui/shared/ui-event-tracker.js +27 -0
- package/dist/ui/shared/widget-state.d.ts +6 -1
- package/dist/ui/shared/widget-state.js +102 -4
- package/dist/utils/capture.js +3 -3
- 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 +13 -4
- package/dist/data/spec-kit-prompts.json +0 -123
- 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/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/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.d.ts +0 -4
- package/dist/ui/file-preview/src/app.js +0 -800
- 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/{http-server-auto-tunnel.d.ts → ui/config-editor/main.d.ts} +0 -0
- /package/dist/{test-docx.d.ts → ui/config-editor/src/main.d.ts} +0 -0
- /package/dist/{tools/docx/xml-view-test.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/pdf-processor.d.ts → ui/file-preview/src/model.js} +0 -0
|
@@ -1,800 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Top-level controller for the File Preview app. It routes structured content into the appropriate renderer, handles host events, and coordinates user-facing state changes.
|
|
3
|
-
*/
|
|
4
|
-
import { formatJsonIfPossible, inferLanguageFromPath, renderCodeViewer } from './components/code-viewer.js';
|
|
5
|
-
import { renderHtmlPreview } from './components/html-renderer.js';
|
|
6
|
-
import { renderMarkdown } from './components/markdown-renderer.js';
|
|
7
|
-
import { escapeHtml } from './components/highlighting.js';
|
|
8
|
-
import { isAllowedImageMimeType, normalizeImageMimeType } from './image-preview.js';
|
|
9
|
-
import { createWindowRpcClient, isTrustedParentMessageSource } from '../../shared/rpc-client.js';
|
|
10
|
-
import { createToolShellController } from '../../shared/tool-shell.js';
|
|
11
|
-
import { createUiHostLifecycle } from '../../shared/host-lifecycle.js';
|
|
12
|
-
import { createUiThemeAdapter } from '../../shared/theme-adaptation.js';
|
|
13
|
-
import { createWidgetStateStorage } from '../../shared/widget-state.js';
|
|
14
|
-
let isExpanded = false;
|
|
15
|
-
let previewShownFired = false;
|
|
16
|
-
let onRender;
|
|
17
|
-
let trackUiEvent;
|
|
18
|
-
let rpcCallTool;
|
|
19
|
-
let rpcUpdateContext;
|
|
20
|
-
let shellController;
|
|
21
|
-
function getFileExtensionForAnalytics(filePath) {
|
|
22
|
-
const normalizedPath = filePath.trim().replace(/\\/g, '/');
|
|
23
|
-
const fileName = normalizedPath.split('/').pop() ?? normalizedPath;
|
|
24
|
-
const dotIndex = fileName.lastIndexOf('.');
|
|
25
|
-
if (dotIndex <= 0 || dotIndex === fileName.length - 1) {
|
|
26
|
-
return 'none';
|
|
27
|
-
}
|
|
28
|
-
return fileName.slice(dotIndex + 1).toLowerCase();
|
|
29
|
-
}
|
|
30
|
-
function isObject(value) {
|
|
31
|
-
return typeof value === 'object' && value !== null;
|
|
32
|
-
}
|
|
33
|
-
function isPreviewStructuredContent(value) {
|
|
34
|
-
if (!isObject(value)) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
return (typeof value.fileName === 'string' &&
|
|
38
|
-
typeof value.filePath === 'string' &&
|
|
39
|
-
typeof value.fileType === 'string' &&
|
|
40
|
-
typeof value.content === 'string');
|
|
41
|
-
}
|
|
42
|
-
function readStructuredContentFromWindow() {
|
|
43
|
-
const candidates = [
|
|
44
|
-
window.__DC_FILE_PREVIEW__,
|
|
45
|
-
window.__MCP_TOOL_RESULT__,
|
|
46
|
-
window.toolResult,
|
|
47
|
-
window.structuredContent
|
|
48
|
-
];
|
|
49
|
-
for (const candidate of candidates) {
|
|
50
|
-
if (!isObject(candidate)) {
|
|
51
|
-
continue;
|
|
52
|
-
}
|
|
53
|
-
if (isPreviewStructuredContent(candidate.structuredContent)) {
|
|
54
|
-
return candidate.structuredContent;
|
|
55
|
-
}
|
|
56
|
-
if (isPreviewStructuredContent(candidate)) {
|
|
57
|
-
return candidate;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
function extractStructuredContent(value) {
|
|
63
|
-
if (!isObject(value)) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
if (isPreviewStructuredContent(value.structuredContent)) {
|
|
67
|
-
return value.structuredContent;
|
|
68
|
-
}
|
|
69
|
-
if (isPreviewStructuredContent(value)) {
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
function extractToolText(value) {
|
|
75
|
-
if (!isObject(value)) {
|
|
76
|
-
return undefined;
|
|
77
|
-
}
|
|
78
|
-
const content = value.content;
|
|
79
|
-
if (!Array.isArray(content)) {
|
|
80
|
-
return undefined;
|
|
81
|
-
}
|
|
82
|
-
for (const item of content) {
|
|
83
|
-
if (!isObject(item)) {
|
|
84
|
-
continue;
|
|
85
|
-
}
|
|
86
|
-
if (item.type === 'text' && typeof item.text === 'string' && item.text.trim().length > 0) {
|
|
87
|
-
return item.text;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
return undefined;
|
|
91
|
-
}
|
|
92
|
-
function extractToolTextFromEvent(value) {
|
|
93
|
-
if (!isObject(value)) {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
const direct = extractToolText(value);
|
|
97
|
-
if (direct) {
|
|
98
|
-
return direct;
|
|
99
|
-
}
|
|
100
|
-
if (isObject(value.result)) {
|
|
101
|
-
const nested = extractToolText(value.result);
|
|
102
|
-
if (nested) {
|
|
103
|
-
return nested;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
if (isObject(value.params)) {
|
|
107
|
-
const paramsText = extractToolText(value.params);
|
|
108
|
-
if (paramsText) {
|
|
109
|
-
return paramsText;
|
|
110
|
-
}
|
|
111
|
-
if (isObject(value.params.result)) {
|
|
112
|
-
return extractToolText(value.params.result);
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
return undefined;
|
|
116
|
-
}
|
|
117
|
-
function isLikelyUrl(filePath) {
|
|
118
|
-
return /^https?:\/\//i.test(filePath);
|
|
119
|
-
}
|
|
120
|
-
function buildBreadcrumb(filePath) {
|
|
121
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
122
|
-
const parts = normalized.split('/').filter(Boolean);
|
|
123
|
-
// Show last 3-4 meaningful segments as breadcrumb
|
|
124
|
-
const tail = parts.slice(-4);
|
|
125
|
-
return tail.map(p => escapeHtml(p)).join(' <span class="breadcrumb-sep">›</span> ');
|
|
126
|
-
}
|
|
127
|
-
function getParentDirectory(filePath) {
|
|
128
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
129
|
-
const lastSlash = normalized.lastIndexOf('/');
|
|
130
|
-
if (lastSlash <= 0) {
|
|
131
|
-
return filePath;
|
|
132
|
-
}
|
|
133
|
-
return normalized.slice(0, lastSlash);
|
|
134
|
-
}
|
|
135
|
-
function shellQuote(value) {
|
|
136
|
-
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
137
|
-
}
|
|
138
|
-
function encodePowerShellCommand(script) {
|
|
139
|
-
// PowerShell -EncodedCommand expects UTF-16LE bytes.
|
|
140
|
-
const utf16leBytes = [];
|
|
141
|
-
for (let index = 0; index < script.length; index += 1) {
|
|
142
|
-
const codeUnit = script.charCodeAt(index);
|
|
143
|
-
utf16leBytes.push(codeUnit & 0xff, codeUnit >> 8);
|
|
144
|
-
}
|
|
145
|
-
let binary = '';
|
|
146
|
-
for (const byte of utf16leBytes) {
|
|
147
|
-
binary += String.fromCharCode(byte);
|
|
148
|
-
}
|
|
149
|
-
return btoa(binary);
|
|
150
|
-
}
|
|
151
|
-
function buildOpenInFolderCommand(filePath) {
|
|
152
|
-
const trimmedPath = filePath.trim();
|
|
153
|
-
if (!trimmedPath || isLikelyUrl(trimmedPath)) {
|
|
154
|
-
return undefined;
|
|
155
|
-
}
|
|
156
|
-
const userAgent = navigator.userAgent.toLowerCase();
|
|
157
|
-
if (userAgent.includes('win')) {
|
|
158
|
-
const escapedForPowerShell = trimmedPath.replace(/'/g, "''");
|
|
159
|
-
const script = `Start-Process -FilePath explorer.exe -ArgumentList @('/select,','${escapedForPowerShell}')`;
|
|
160
|
-
return `powershell.exe -NoProfile -NonInteractive -EncodedCommand ${encodePowerShellCommand(script)}`;
|
|
161
|
-
}
|
|
162
|
-
if (userAgent.includes('mac')) {
|
|
163
|
-
return `open -R ${shellQuote(trimmedPath)}`;
|
|
164
|
-
}
|
|
165
|
-
return `xdg-open ${shellQuote(getParentDirectory(trimmedPath))}`;
|
|
166
|
-
}
|
|
167
|
-
function renderRawFallback(source) {
|
|
168
|
-
return `<pre class="code-viewer"><code class="hljs language-text">${escapeHtml(source)}</code></pre>`;
|
|
169
|
-
}
|
|
170
|
-
function stripReadStatusLine(content) {
|
|
171
|
-
// Remove the synthetic read status header shown by read_file pagination.
|
|
172
|
-
return content.replace(/^\[Reading [^\]]+\]\r?\n?/, '');
|
|
173
|
-
}
|
|
174
|
-
function renderImageBody(payload) {
|
|
175
|
-
const mimeType = normalizeImageMimeType(payload.mimeType);
|
|
176
|
-
if (!isAllowedImageMimeType(mimeType)) {
|
|
177
|
-
return {
|
|
178
|
-
notice: 'Preview is unavailable for this image format.',
|
|
179
|
-
html: '<div class="panel-content source-content"></div>'
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
if (!payload.imageData || payload.imageData.trim().length === 0) {
|
|
183
|
-
return {
|
|
184
|
-
notice: 'Preview is unavailable because image data is missing.',
|
|
185
|
-
html: '<div class="panel-content source-content"></div>'
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
const src = `data:${mimeType};base64,${payload.imageData}`;
|
|
189
|
-
return {
|
|
190
|
-
html: `<div class="panel-content image-content"><div class="image-preview"><img src="${escapeHtml(src)}" alt="${escapeHtml(payload.fileName)}" loading="eager" decoding="async"></div></div>`
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
function countContentLines(content) {
|
|
194
|
-
const cleaned = stripReadStatusLine(content);
|
|
195
|
-
if (cleaned === '')
|
|
196
|
-
return 0;
|
|
197
|
-
const lines = cleaned.split('\n');
|
|
198
|
-
return lines[lines.length - 1] === '' ? lines.length - 1 : lines.length;
|
|
199
|
-
}
|
|
200
|
-
function parseReadRange(content) {
|
|
201
|
-
// Parse "[Reading N lines from line M (total: T lines, R remaining)]"
|
|
202
|
-
// or "[Reading N lines from start (total: T lines, R remaining)]"
|
|
203
|
-
const match = content.match(/^\[Reading (\d+) lines from (?:line )?(\d+|start) \(total: (\d+) lines/);
|
|
204
|
-
if (!match)
|
|
205
|
-
return undefined;
|
|
206
|
-
const count = parseInt(match[1], 10);
|
|
207
|
-
const from = match[2] === 'start' ? 1 : parseInt(match[2], 10);
|
|
208
|
-
const total = parseInt(match[3], 10);
|
|
209
|
-
return {
|
|
210
|
-
fromLine: from,
|
|
211
|
-
toLine: from + count - 1,
|
|
212
|
-
totalLines: total,
|
|
213
|
-
isPartial: count < total
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
function renderBody(payload, htmlMode, startLine = 1) {
|
|
217
|
-
const cleanedContent = stripReadStatusLine(payload.content);
|
|
218
|
-
if (payload.fileType === 'image') {
|
|
219
|
-
return renderImageBody(payload);
|
|
220
|
-
}
|
|
221
|
-
if (payload.fileType === 'unsupported') {
|
|
222
|
-
return {
|
|
223
|
-
notice: 'Preview is not available for this file type.',
|
|
224
|
-
html: '<div class="panel-content source-content"></div>'
|
|
225
|
-
};
|
|
226
|
-
}
|
|
227
|
-
if (payload.fileType === 'html') {
|
|
228
|
-
return renderHtmlPreview(cleanedContent, htmlMode);
|
|
229
|
-
}
|
|
230
|
-
if (payload.fileType !== 'markdown') {
|
|
231
|
-
const detectedLanguage = inferLanguageFromPath(payload.filePath);
|
|
232
|
-
const formatted = formatJsonIfPossible(cleanedContent, payload.filePath);
|
|
233
|
-
return {
|
|
234
|
-
notice: formatted.notice,
|
|
235
|
-
html: `<div class="panel-content source-content">${renderCodeViewer(formatted.content, detectedLanguage, startLine)}</div>`
|
|
236
|
-
};
|
|
237
|
-
}
|
|
238
|
-
try {
|
|
239
|
-
return {
|
|
240
|
-
html: `<div class="panel-content markdown-content"><article class="markdown markdown-doc">${renderMarkdown(cleanedContent)}</article></div>`
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
catch {
|
|
244
|
-
return {
|
|
245
|
-
notice: 'Markdown renderer failed. Showing raw source instead.',
|
|
246
|
-
html: `<div class="panel-content source-content">${renderRawFallback(cleanedContent)}</div>`
|
|
247
|
-
};
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
function attachCopyHandler(payload) {
|
|
251
|
-
const copyButton = document.getElementById('copy-source');
|
|
252
|
-
if (!copyButton) {
|
|
253
|
-
return;
|
|
254
|
-
}
|
|
255
|
-
const fallbackCopy = (text) => {
|
|
256
|
-
const textArea = document.createElement('textarea');
|
|
257
|
-
textArea.value = text;
|
|
258
|
-
textArea.setAttribute('readonly', '');
|
|
259
|
-
textArea.style.position = 'fixed';
|
|
260
|
-
textArea.style.top = '-9999px';
|
|
261
|
-
document.body.appendChild(textArea);
|
|
262
|
-
textArea.select();
|
|
263
|
-
const success = document.execCommand('copy');
|
|
264
|
-
document.body.removeChild(textArea);
|
|
265
|
-
return success;
|
|
266
|
-
};
|
|
267
|
-
const setButtonState = (label, revertMs) => {
|
|
268
|
-
copyButton.setAttribute('title', label);
|
|
269
|
-
copyButton.setAttribute('aria-label', label);
|
|
270
|
-
copyButton.textContent = label;
|
|
271
|
-
if (revertMs) {
|
|
272
|
-
setTimeout(() => {
|
|
273
|
-
copyButton.textContent = 'Copy';
|
|
274
|
-
copyButton.setAttribute('title', 'Copy source');
|
|
275
|
-
copyButton.setAttribute('aria-label', 'Copy source');
|
|
276
|
-
}, revertMs);
|
|
277
|
-
}
|
|
278
|
-
};
|
|
279
|
-
const copyTextData = async (text) => {
|
|
280
|
-
try {
|
|
281
|
-
if (navigator.clipboard?.writeText) {
|
|
282
|
-
await navigator.clipboard.writeText(text);
|
|
283
|
-
return true;
|
|
284
|
-
}
|
|
285
|
-
return fallbackCopy(text);
|
|
286
|
-
}
|
|
287
|
-
catch {
|
|
288
|
-
return fallbackCopy(text);
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
copyButton.addEventListener('click', async () => {
|
|
292
|
-
trackUiEvent?.('copy_clicked', {
|
|
293
|
-
file_type: payload.fileType,
|
|
294
|
-
file_extension: getFileExtensionForAnalytics(payload.filePath)
|
|
295
|
-
});
|
|
296
|
-
const cleanedContent = stripReadStatusLine(payload.content);
|
|
297
|
-
const copied = await copyTextData(cleanedContent);
|
|
298
|
-
setButtonState(copied ? 'Copied!' : 'Copy failed', 1500);
|
|
299
|
-
});
|
|
300
|
-
}
|
|
301
|
-
function attachHtmlToggleHandler(container, payload, htmlMode) {
|
|
302
|
-
const toggleButton = document.getElementById('toggle-html-mode');
|
|
303
|
-
if (!toggleButton || payload.fileType !== 'html') {
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
306
|
-
toggleButton.addEventListener('click', () => {
|
|
307
|
-
const nextMode = htmlMode === 'rendered' ? 'source' : 'rendered';
|
|
308
|
-
trackUiEvent?.('html_view_toggled', {
|
|
309
|
-
file_type: payload.fileType,
|
|
310
|
-
file_extension: getFileExtensionForAnalytics(payload.filePath)
|
|
311
|
-
});
|
|
312
|
-
renderApp(container, payload, nextMode, isExpanded);
|
|
313
|
-
});
|
|
314
|
-
}
|
|
315
|
-
function attachOpenInFolderHandler(payload) {
|
|
316
|
-
const openButton = document.getElementById('open-in-folder');
|
|
317
|
-
if (!openButton) {
|
|
318
|
-
return;
|
|
319
|
-
}
|
|
320
|
-
const command = buildOpenInFolderCommand(payload.filePath);
|
|
321
|
-
if (!command) {
|
|
322
|
-
openButton.disabled = true;
|
|
323
|
-
return;
|
|
324
|
-
}
|
|
325
|
-
openButton.addEventListener('click', async () => {
|
|
326
|
-
trackUiEvent?.('open_in_folder', {
|
|
327
|
-
file_type: payload.fileType,
|
|
328
|
-
file_extension: getFileExtensionForAnalytics(payload.filePath)
|
|
329
|
-
});
|
|
330
|
-
try {
|
|
331
|
-
await rpcCallTool?.('start_process', {
|
|
332
|
-
command,
|
|
333
|
-
timeout_ms: 12000
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
catch {
|
|
337
|
-
// Keep UI stable if opening folder fails.
|
|
338
|
-
}
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
function attachLoadAllHandler(container, payload, htmlMode) {
|
|
342
|
-
const beforeBtn = document.getElementById('load-before');
|
|
343
|
-
const afterBtn = document.getElementById('load-after');
|
|
344
|
-
if (!beforeBtn && !afterBtn) {
|
|
345
|
-
return;
|
|
346
|
-
}
|
|
347
|
-
const range = parseReadRange(payload.content);
|
|
348
|
-
if (!range?.isPartial)
|
|
349
|
-
return;
|
|
350
|
-
const currentContent = stripReadStatusLine(payload.content);
|
|
351
|
-
const loadLines = async (btn, direction) => {
|
|
352
|
-
const originalText = btn.textContent;
|
|
353
|
-
btn.textContent = 'Loading…';
|
|
354
|
-
btn.disabled = true;
|
|
355
|
-
trackUiEvent?.(direction === 'before' ? 'load_lines_before' : 'load_lines_after', {
|
|
356
|
-
file_type: payload.fileType,
|
|
357
|
-
file_extension: getFileExtensionForAnalytics(payload.filePath)
|
|
358
|
-
});
|
|
359
|
-
try {
|
|
360
|
-
// Load only the missing portion
|
|
361
|
-
const readArgs = direction === 'before'
|
|
362
|
-
? { path: payload.filePath, offset: 0, length: range.fromLine - 1 }
|
|
363
|
-
: { path: payload.filePath, offset: range.toLine };
|
|
364
|
-
const result = await rpcCallTool?.('read_file', readArgs);
|
|
365
|
-
const resultObj = result;
|
|
366
|
-
const newText = resultObj?.content?.[0]?.text;
|
|
367
|
-
if (newText && typeof newText === 'string') {
|
|
368
|
-
const cleanNew = stripReadStatusLine(newText);
|
|
369
|
-
// Merge: prepend or append the new lines
|
|
370
|
-
const merged = direction === 'before'
|
|
371
|
-
? cleanNew + (cleanNew.endsWith('\n') ? '' : '\n') + currentContent
|
|
372
|
-
: currentContent + (currentContent.endsWith('\n') ? '' : '\n') + cleanNew;
|
|
373
|
-
// Build updated status line reflecting the new range
|
|
374
|
-
const newFrom = direction === 'before' ? 1 : range.fromLine;
|
|
375
|
-
const newTo = direction === 'after' ? range.totalLines : range.toLine;
|
|
376
|
-
const lineCount = newTo - newFrom + 1;
|
|
377
|
-
const remaining = range.totalLines - newTo;
|
|
378
|
-
const isStillPartial = newFrom > 1 || newTo < range.totalLines;
|
|
379
|
-
const statusLine = isStillPartial
|
|
380
|
-
? `[Reading ${lineCount} lines from ${newFrom === 1 ? 'start' : `line ${newFrom}`} (total: ${range.totalLines} lines, ${remaining} remaining)]\n`
|
|
381
|
-
: '';
|
|
382
|
-
const mergedPayload = {
|
|
383
|
-
...payload,
|
|
384
|
-
content: statusLine + merged
|
|
385
|
-
};
|
|
386
|
-
renderApp(container, mergedPayload, htmlMode, isExpanded);
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
btn.textContent = 'Failed to load';
|
|
390
|
-
setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 2000);
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
catch {
|
|
394
|
-
btn.textContent = 'Failed to load';
|
|
395
|
-
setTimeout(() => { btn.textContent = originalText; btn.disabled = false; }, 2000);
|
|
396
|
-
}
|
|
397
|
-
};
|
|
398
|
-
beforeBtn?.addEventListener('click', () => void loadLines(beforeBtn, 'before'));
|
|
399
|
-
afterBtn?.addEventListener('click', () => void loadLines(afterBtn, 'after'));
|
|
400
|
-
}
|
|
401
|
-
/**
|
|
402
|
-
* Tracks native text selection and pushes it to the host via ui/update-model-context.
|
|
403
|
-
*
|
|
404
|
-
* How it works:
|
|
405
|
-
* 1. User drags to select text anywhere in the preview (markdown, code, HTML).
|
|
406
|
-
* 2. The selectionchange event fires; we extract the selected string.
|
|
407
|
-
* 3. We call rpcUpdateContext() which sends a ui/update-model-context JSON-RPC
|
|
408
|
-
* request to the host with the selected text + file path (+ line numbers for code).
|
|
409
|
-
* 4. The host stores this as widget context.
|
|
410
|
-
* 5. The LLM can access it by calling read_widget_context(tool_name="desktop-commander:read_file").
|
|
411
|
-
*
|
|
412
|
-
* Note: as of Feb 2025, Claude does NOT auto-inject ui/update-model-context into
|
|
413
|
-
* the LLM's context window. The LLM must actively call read_widget_context to see
|
|
414
|
-
* the selection. A floating tooltip near the selection tells the user this is working.
|
|
415
|
-
*/
|
|
416
|
-
let selectionAbortController = null;
|
|
417
|
-
function attachTextSelectionHandler(payload) {
|
|
418
|
-
const contentWrapper = document.querySelector('.panel-content-wrapper');
|
|
419
|
-
if (!contentWrapper)
|
|
420
|
-
return;
|
|
421
|
-
// Abort any previous selectionchange listener to avoid leaking listeners/closures
|
|
422
|
-
if (selectionAbortController) {
|
|
423
|
-
selectionAbortController.abort();
|
|
424
|
-
selectionAbortController = null;
|
|
425
|
-
}
|
|
426
|
-
selectionAbortController = new AbortController();
|
|
427
|
-
let hintEl = null;
|
|
428
|
-
let lastSelectedText = '';
|
|
429
|
-
let hideTimer = null;
|
|
430
|
-
function positionHint(selection) {
|
|
431
|
-
if (!hintEl)
|
|
432
|
-
return;
|
|
433
|
-
const range = selection.getRangeAt(0);
|
|
434
|
-
const rect = range.getBoundingClientRect();
|
|
435
|
-
const wrapperRect = contentWrapper.getBoundingClientRect();
|
|
436
|
-
// Position above the selection, centered horizontally
|
|
437
|
-
let left = rect.left + rect.width / 2 - wrapperRect.left;
|
|
438
|
-
let top = rect.top - wrapperRect.top + contentWrapper.scrollTop - 32;
|
|
439
|
-
// Clamp within wrapper bounds
|
|
440
|
-
const hintWidth = hintEl.offsetWidth || 200;
|
|
441
|
-
left = Math.max(8, Math.min(left - hintWidth / 2, contentWrapper.clientWidth - hintWidth - 8));
|
|
442
|
-
top = Math.max(4, top);
|
|
443
|
-
hintEl.style.left = `${left}px`;
|
|
444
|
-
hintEl.style.top = `${top}px`;
|
|
445
|
-
}
|
|
446
|
-
function showHint(selection) {
|
|
447
|
-
if (hideTimer) {
|
|
448
|
-
clearTimeout(hideTimer);
|
|
449
|
-
hideTimer = null;
|
|
450
|
-
}
|
|
451
|
-
if (!hintEl) {
|
|
452
|
-
hintEl = document.createElement('div');
|
|
453
|
-
hintEl.className = 'selection-hint';
|
|
454
|
-
hintEl.textContent = 'AI can see your selection';
|
|
455
|
-
contentWrapper.appendChild(hintEl);
|
|
456
|
-
}
|
|
457
|
-
hintEl.classList.add('visible');
|
|
458
|
-
positionHint(selection);
|
|
459
|
-
}
|
|
460
|
-
function hideHint() {
|
|
461
|
-
if (!hintEl)
|
|
462
|
-
return;
|
|
463
|
-
hintEl.classList.remove('visible');
|
|
464
|
-
hideTimer = setTimeout(() => { hintEl?.remove(); hintEl = null; }, 200);
|
|
465
|
-
}
|
|
466
|
-
function getLineInfo(selection) {
|
|
467
|
-
const anchorRow = selection.anchorNode?.parentElement?.closest('.code-line');
|
|
468
|
-
const focusRow = selection.focusNode?.parentElement?.closest('.code-line');
|
|
469
|
-
if (anchorRow && focusRow) {
|
|
470
|
-
const a = parseInt(anchorRow.dataset.line ?? '', 10);
|
|
471
|
-
const f = parseInt(focusRow.dataset.line ?? '', 10);
|
|
472
|
-
if (!isNaN(a) && !isNaN(f)) {
|
|
473
|
-
const low = Math.min(a, f);
|
|
474
|
-
const high = Math.max(a, f);
|
|
475
|
-
return low === high ? `line ${low}` : `lines ${low}–${high}`;
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
|
-
return '';
|
|
479
|
-
}
|
|
480
|
-
document.addEventListener('selectionchange', () => {
|
|
481
|
-
const selection = document.getSelection();
|
|
482
|
-
if (!selection || selection.isCollapsed) {
|
|
483
|
-
if (lastSelectedText) {
|
|
484
|
-
lastSelectedText = '';
|
|
485
|
-
rpcUpdateContext?.('');
|
|
486
|
-
hideHint();
|
|
487
|
-
}
|
|
488
|
-
return;
|
|
489
|
-
}
|
|
490
|
-
const text = selection.toString().trim();
|
|
491
|
-
if (!text || text === lastSelectedText)
|
|
492
|
-
return;
|
|
493
|
-
// Only act on selections within our content area
|
|
494
|
-
const anchorInContent = contentWrapper.contains(selection.anchorNode);
|
|
495
|
-
const focusInContent = contentWrapper.contains(selection.focusNode);
|
|
496
|
-
if (!anchorInContent && !focusInContent) {
|
|
497
|
-
if (lastSelectedText) {
|
|
498
|
-
lastSelectedText = '';
|
|
499
|
-
rpcUpdateContext?.('');
|
|
500
|
-
hideHint();
|
|
501
|
-
}
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
lastSelectedText = text;
|
|
505
|
-
const lineInfo = getLineInfo(selection);
|
|
506
|
-
const locationPart = lineInfo ? ` (${lineInfo})` : '';
|
|
507
|
-
const context = `User selected text from file ${payload.filePath}${locationPart}:\n\`\`\`\n${text}\n\`\`\``;
|
|
508
|
-
rpcUpdateContext?.(context);
|
|
509
|
-
showHint(selection);
|
|
510
|
-
trackUiEvent?.('text_selected', {
|
|
511
|
-
file_type: payload.fileType,
|
|
512
|
-
file_extension: getFileExtensionForAnalytics(payload.filePath),
|
|
513
|
-
char_count: text.length
|
|
514
|
-
});
|
|
515
|
-
}, { signal: selectionAbortController.signal });
|
|
516
|
-
}
|
|
517
|
-
function renderStatusState(container, message) {
|
|
518
|
-
container.innerHTML = `
|
|
519
|
-
<main class="shell">
|
|
520
|
-
<div class="compact-row compact-row--status">
|
|
521
|
-
<span class="compact-label">${escapeHtml(message)}</span>
|
|
522
|
-
</div>
|
|
523
|
-
</main>
|
|
524
|
-
`;
|
|
525
|
-
document.body.classList.add('dc-ready');
|
|
526
|
-
}
|
|
527
|
-
function renderLoadingState(container) {
|
|
528
|
-
container.innerHTML = `
|
|
529
|
-
<main class="shell">
|
|
530
|
-
<div class="compact-row compact-row--loading">
|
|
531
|
-
<span class="compact-label">Preparing preview…</span>
|
|
532
|
-
</div>
|
|
533
|
-
</main>
|
|
534
|
-
`;
|
|
535
|
-
document.body.classList.add('dc-ready');
|
|
536
|
-
}
|
|
537
|
-
export function renderApp(container, payload, htmlMode = 'rendered', expandedState = false) {
|
|
538
|
-
isExpanded = expandedState;
|
|
539
|
-
shellController?.dispose();
|
|
540
|
-
shellController = undefined;
|
|
541
|
-
if (!payload) {
|
|
542
|
-
renderStatusState(container, 'No preview available for this response.');
|
|
543
|
-
onRender?.();
|
|
544
|
-
return;
|
|
545
|
-
}
|
|
546
|
-
const canCopy = payload.fileType !== 'unsupported' && payload.fileType !== 'image';
|
|
547
|
-
const canOpenInFolder = !isLikelyUrl(payload.filePath);
|
|
548
|
-
const fileExtension = getFileExtensionForAnalytics(payload.filePath);
|
|
549
|
-
const supportsPreview = payload.fileType !== 'unsupported';
|
|
550
|
-
const range = parseReadRange(payload.content);
|
|
551
|
-
const body = renderBody(payload, htmlMode, range?.fromLine ?? 1);
|
|
552
|
-
const notice = body.notice ? `<div class="notice">${body.notice}</div>` : '';
|
|
553
|
-
const breadcrumb = buildBreadcrumb(payload.filePath);
|
|
554
|
-
const lineCount = range ? range.toLine - range.fromLine + 1 : countContentLines(payload.content);
|
|
555
|
-
const fileTypeLabel = payload.fileType === 'markdown' ? 'MARKDOWN'
|
|
556
|
-
: payload.fileType === 'html' ? 'HTML'
|
|
557
|
-
: payload.fileType === 'image' ? 'IMAGE'
|
|
558
|
-
: fileExtension !== 'none' ? fileExtension.toUpperCase()
|
|
559
|
-
: 'TEXT';
|
|
560
|
-
const compactLabel = range?.isPartial
|
|
561
|
-
? `View lines ${range.fromLine}–${range.toLine}`
|
|
562
|
-
: 'View file';
|
|
563
|
-
const footerLabel = range?.isPartial
|
|
564
|
-
? `${escapeHtml(fileTypeLabel)} • LINES ${range.fromLine}–${range.toLine} OF ${range.totalLines}`
|
|
565
|
-
: `${escapeHtml(fileTypeLabel)} • ${lineCount} LINE${lineCount !== 1 ? 'S' : ''}`;
|
|
566
|
-
const htmlToggle = payload.fileType === 'html'
|
|
567
|
-
? `<button class="panel-action" id="toggle-html-mode">${htmlMode === 'rendered' ? 'Source' : 'Rendered'}</button>`
|
|
568
|
-
: '';
|
|
569
|
-
const copyIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>`;
|
|
570
|
-
const folderIcon = `<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>`;
|
|
571
|
-
// Content-area banners for missing lines
|
|
572
|
-
const hasMissingBefore = range?.isPartial && range.fromLine > 1;
|
|
573
|
-
const hasMissingAfter = range?.isPartial && range.toLine < range.totalLines && (range.totalLines - range.toLine) > 1;
|
|
574
|
-
const loadBeforeBanner = hasMissingBefore
|
|
575
|
-
? `<button class="load-lines-banner" id="load-before">↑ Load lines 1–${range.fromLine - 1}</button>`
|
|
576
|
-
: '';
|
|
577
|
-
const loadAfterBanner = hasMissingAfter
|
|
578
|
-
? `<button class="load-lines-banner" id="load-after">↓ Load lines ${range.toLine + 1}–${range.totalLines}</button>`
|
|
579
|
-
: '';
|
|
580
|
-
container.innerHTML = `
|
|
581
|
-
<main id="tool-shell" class="shell tool-shell ${isExpanded ? 'expanded' : 'collapsed'}">
|
|
582
|
-
<div class="compact-row compact-row--ready" id="compact-toggle" role="button" tabindex="0" aria-expanded="${isExpanded}">
|
|
583
|
-
<svg class="compact-chevron" viewBox="0 0 24 24" aria-hidden="true"><path d="M10 6l6 6-6 6z"/></svg>
|
|
584
|
-
<span class="compact-label">${compactLabel}</span>
|
|
585
|
-
<span class="compact-filename">${escapeHtml(payload.fileName)}</span>
|
|
586
|
-
</div>
|
|
587
|
-
<section class="panel">
|
|
588
|
-
<div class="panel-topbar">
|
|
589
|
-
<span class="panel-breadcrumb" title="${escapeHtml(payload.filePath)}">${breadcrumb}</span>
|
|
590
|
-
<span class="panel-topbar-actions">
|
|
591
|
-
${htmlToggle}
|
|
592
|
-
${canOpenInFolder ? `<button class="panel-action" id="open-in-folder">${folderIcon} Open in folder</button>` : ''}
|
|
593
|
-
${canCopy && supportsPreview ? `<button class="panel-action" id="copy-source" title="Copy source" aria-label="Copy source">${copyIcon} Copy</button>` : ''}
|
|
594
|
-
</span>
|
|
595
|
-
</div>
|
|
596
|
-
${notice}
|
|
597
|
-
<div class="panel-content-wrapper">
|
|
598
|
-
${loadBeforeBanner}
|
|
599
|
-
${body.html}
|
|
600
|
-
${loadAfterBanner}
|
|
601
|
-
</div>
|
|
602
|
-
<div class="panel-footer">
|
|
603
|
-
<span>${footerLabel}</span>
|
|
604
|
-
</div>
|
|
605
|
-
</section>
|
|
606
|
-
</main>
|
|
607
|
-
`;
|
|
608
|
-
document.body.classList.add('dc-ready');
|
|
609
|
-
attachCopyHandler(payload);
|
|
610
|
-
attachHtmlToggleHandler(container, payload, htmlMode);
|
|
611
|
-
attachOpenInFolderHandler(payload);
|
|
612
|
-
attachLoadAllHandler(container, payload, htmlMode);
|
|
613
|
-
attachTextSelectionHandler(payload);
|
|
614
|
-
// Compact row click toggles expand/collapse
|
|
615
|
-
const compactRow = document.getElementById('compact-toggle');
|
|
616
|
-
const handleCompactClick = () => {
|
|
617
|
-
shellController?.toggle();
|
|
618
|
-
};
|
|
619
|
-
const handleCompactKeydown = (e) => {
|
|
620
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
621
|
-
e.preventDefault();
|
|
622
|
-
shellController?.toggle();
|
|
623
|
-
}
|
|
624
|
-
};
|
|
625
|
-
compactRow?.addEventListener('click', handleCompactClick);
|
|
626
|
-
compactRow?.addEventListener('keydown', handleCompactKeydown);
|
|
627
|
-
shellController = createToolShellController({
|
|
628
|
-
shell: document.getElementById('tool-shell'),
|
|
629
|
-
toggleButton: null, // No separate toggle button; compact row handles it
|
|
630
|
-
initialExpanded: isExpanded,
|
|
631
|
-
onToggle: (expanded) => {
|
|
632
|
-
isExpanded = expanded;
|
|
633
|
-
compactRow?.setAttribute('aria-expanded', String(expanded));
|
|
634
|
-
trackUiEvent?.(expanded ? 'expand' : 'collapse', {
|
|
635
|
-
file_type: payload.fileType,
|
|
636
|
-
file_extension: fileExtension
|
|
637
|
-
});
|
|
638
|
-
},
|
|
639
|
-
onScrollAfterExpand: () => {
|
|
640
|
-
trackUiEvent?.('scroll_after_expand', {
|
|
641
|
-
file_type: payload.fileType,
|
|
642
|
-
file_extension: fileExtension
|
|
643
|
-
});
|
|
644
|
-
},
|
|
645
|
-
onRender
|
|
646
|
-
});
|
|
647
|
-
onRender?.();
|
|
648
|
-
if (!previewShownFired) {
|
|
649
|
-
previewShownFired = true;
|
|
650
|
-
trackUiEvent?.('preview_shown', {
|
|
651
|
-
file_type: payload.fileType,
|
|
652
|
-
file_extension: fileExtension
|
|
653
|
-
});
|
|
654
|
-
}
|
|
655
|
-
}
|
|
656
|
-
export function bootstrapApp() {
|
|
657
|
-
const container = document.getElementById('app');
|
|
658
|
-
if (!container) {
|
|
659
|
-
return;
|
|
660
|
-
}
|
|
661
|
-
renderLoadingState(container);
|
|
662
|
-
const rpcClient = createWindowRpcClient({
|
|
663
|
-
targetWindow: window.parent,
|
|
664
|
-
timeoutMs: 15000,
|
|
665
|
-
isTrustedSource: (source) => isTrustedParentMessageSource(source, window.parent)
|
|
666
|
-
});
|
|
667
|
-
const hostLifecycle = createUiHostLifecycle(rpcClient, {
|
|
668
|
-
appName: 'Desktop Commander File Preview',
|
|
669
|
-
appVersion: '1.0.0'
|
|
670
|
-
});
|
|
671
|
-
const themeAdapter = createUiThemeAdapter();
|
|
672
|
-
rpcCallTool = (name, args) => (rpcClient.request('tools/call', {
|
|
673
|
-
name,
|
|
674
|
-
arguments: args
|
|
675
|
-
}));
|
|
676
|
-
rpcUpdateContext = (text) => {
|
|
677
|
-
const params = text
|
|
678
|
-
? { content: [{ type: 'text', text }] }
|
|
679
|
-
: { content: [] };
|
|
680
|
-
rpcClient.request('ui/update-model-context', params).catch(() => {
|
|
681
|
-
// Host may not support ui/update-model-context yet
|
|
682
|
-
});
|
|
683
|
-
};
|
|
684
|
-
trackUiEvent = (event, params = {}) => {
|
|
685
|
-
void rpcCallTool?.('track_ui_event', {
|
|
686
|
-
event,
|
|
687
|
-
component: 'file_preview',
|
|
688
|
-
params: {
|
|
689
|
-
tool_name: 'read_file',
|
|
690
|
-
...params
|
|
691
|
-
}
|
|
692
|
-
}).catch(() => {
|
|
693
|
-
// Analytics failures should not impact UX.
|
|
694
|
-
});
|
|
695
|
-
};
|
|
696
|
-
onRender = () => {
|
|
697
|
-
hostLifecycle.notifyRender();
|
|
698
|
-
};
|
|
699
|
-
// ChatGPT widget state persistence (other hosts use standard ui/notifications/tool-result)
|
|
700
|
-
const widgetState = createWidgetStateStorage(isPreviewStructuredContent);
|
|
701
|
-
onRender?.();
|
|
702
|
-
themeAdapter.applyFromData(window.__MCP_HOST_CONTEXT__);
|
|
703
|
-
const renderAndSync = (payload) => {
|
|
704
|
-
if (payload) {
|
|
705
|
-
widgetState.write(payload); // Persist for refresh recovery (cross-host)
|
|
706
|
-
}
|
|
707
|
-
renderApp(container, payload, 'rendered', false);
|
|
708
|
-
};
|
|
709
|
-
let initialStateResolved = false;
|
|
710
|
-
const resolveInitialState = (payload, message) => {
|
|
711
|
-
if (initialStateResolved) {
|
|
712
|
-
return;
|
|
713
|
-
}
|
|
714
|
-
initialStateResolved = true;
|
|
715
|
-
if (payload) {
|
|
716
|
-
renderAndSync(payload);
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
renderStatusState(container, message ?? 'No preview available for this response.');
|
|
720
|
-
onRender?.();
|
|
721
|
-
};
|
|
722
|
-
// Try to restore from widget state first (ChatGPT only - survives refresh)
|
|
723
|
-
const cachedPayload = widgetState.read();
|
|
724
|
-
if (cachedPayload) {
|
|
725
|
-
window.setTimeout(() => {
|
|
726
|
-
resolveInitialState(cachedPayload);
|
|
727
|
-
}, 50);
|
|
728
|
-
}
|
|
729
|
-
// Then check window globals
|
|
730
|
-
const initialPayload = readStructuredContentFromWindow();
|
|
731
|
-
if (initialPayload) {
|
|
732
|
-
window.setTimeout(() => {
|
|
733
|
-
resolveInitialState(initialPayload);
|
|
734
|
-
}, 140);
|
|
735
|
-
}
|
|
736
|
-
// Timeout fallback: if no data arrives after retry, show helpful message
|
|
737
|
-
window.setTimeout(() => {
|
|
738
|
-
if (!initialStateResolved) {
|
|
739
|
-
resolveInitialState(undefined, 'Preview unavailable after page refresh (known issue, fix in progress). Switch threads or re-run the tool.');
|
|
740
|
-
}
|
|
741
|
-
}, 8000);
|
|
742
|
-
window.addEventListener('message', (event) => {
|
|
743
|
-
try {
|
|
744
|
-
if (rpcClient.handleMessageEvent(event)) {
|
|
745
|
-
return;
|
|
746
|
-
}
|
|
747
|
-
if (!isTrustedParentMessageSource(event.source, window.parent)) {
|
|
748
|
-
return;
|
|
749
|
-
}
|
|
750
|
-
if (!isObject(event.data)) {
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
themeAdapter.applyFromData(event.data);
|
|
754
|
-
if (event.data.method === 'ui/notifications/tool-result') {
|
|
755
|
-
const params = event.data.params;
|
|
756
|
-
const candidate = isObject(params) && isObject(params.result) ? params.result : params;
|
|
757
|
-
const payload = extractStructuredContent(candidate);
|
|
758
|
-
const message = extractToolTextFromEvent(event.data) ?? extractToolText(candidate);
|
|
759
|
-
if (!initialStateResolved) {
|
|
760
|
-
if (payload) {
|
|
761
|
-
renderLoadingState(container);
|
|
762
|
-
onRender?.();
|
|
763
|
-
window.setTimeout(() => resolveInitialState(payload), 120);
|
|
764
|
-
return;
|
|
765
|
-
}
|
|
766
|
-
if (message) {
|
|
767
|
-
resolveInitialState(undefined, message);
|
|
768
|
-
}
|
|
769
|
-
return;
|
|
770
|
-
}
|
|
771
|
-
if (payload) {
|
|
772
|
-
renderAndSync(payload);
|
|
773
|
-
}
|
|
774
|
-
else if (message) {
|
|
775
|
-
renderStatusState(container, message);
|
|
776
|
-
onRender?.();
|
|
777
|
-
}
|
|
778
|
-
return;
|
|
779
|
-
}
|
|
780
|
-
const payload = extractStructuredContent(event.data);
|
|
781
|
-
if (payload) {
|
|
782
|
-
if (!initialStateResolved) {
|
|
783
|
-
resolveInitialState(payload);
|
|
784
|
-
return;
|
|
785
|
-
}
|
|
786
|
-
renderAndSync(payload);
|
|
787
|
-
}
|
|
788
|
-
}
|
|
789
|
-
catch {
|
|
790
|
-
renderStatusState(container, 'Preview failed to render.');
|
|
791
|
-
onRender?.();
|
|
792
|
-
}
|
|
793
|
-
});
|
|
794
|
-
hostLifecycle.observeResize();
|
|
795
|
-
window.addEventListener('beforeunload', () => {
|
|
796
|
-
shellController?.dispose();
|
|
797
|
-
rpcClient.dispose();
|
|
798
|
-
}, { once: true });
|
|
799
|
-
hostLifecycle.initialize();
|
|
800
|
-
}
|