shadow-claw 1.23.3 → 1.24.0
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 +42 -5
- package/bin/assert-version-bump.mjs +60 -38
- package/bin/assert-version-bump.test.mjs +74 -11
- package/bin/cli.mjs +166 -0
- package/bin/cli.test.mjs +60 -0
- package/bin/commands/backup.mjs +70 -0
- package/bin/commands/clients.mjs +41 -0
- package/bin/commands/peer-id.mjs +76 -0
- package/bin/commands/peer-id.test.mjs +104 -0
- package/bin/commands/send.mjs +51 -0
- package/bin/commands/tasks.mjs +59 -0
- package/bin/commands/webrtc-listen.mjs +254 -0
- package/bin/prerender-dsd-shell/prerender-dsd-shell.test.mjs +46 -0
- package/bin/rolldown.config.test.mjs +401 -8
- package/bin/utils/control-client.mjs +243 -0
- package/bin/utils/control-client.test.mjs +83 -0
- package/bin/utils/ulid.mjs +45 -0
- package/bin/utils/webrtc-control-client.mjs +699 -0
- package/bin/utils/webrtc-control-client.test.mjs +318 -0
- package/dist/electron/main.cjs +1 -1
- package/dist/public/AGENTS.md +5 -3
- package/dist/public/README.md +42 -5
- package/dist/public/agent.worker.js +6 -6
- package/dist/public/{app-routes-B0ePT49v.js → app-routes-Bs-riIvr.js} +1 -1
- package/dist/public/backup-controller-C_s9in2t.js +1 -0
- package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +1 -0
- package/dist/public/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +1 -0
- package/dist/public/components/settings/shadow-claw-storage/shadow-claw-storage.html +1 -1
- package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +1 -1
- package/dist/public/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
- package/dist/public/components/shadow-claw-files/shadow-claw-files.css +1 -1
- package/dist/public/components/shadow-claw-files/shadow-claw-files.html +1 -1
- package/dist/public/components/shadow-claw-settings/shadow-claw-settings.html +1 -1
- package/dist/public/config-CrHchneq.js +1 -0
- package/dist/public/{configurePeerJs-B7kNU8AQ.js → configurePeerJs-CxpZeDJJ.js} +1 -1
- package/dist/public/{connections-xd_66YVU.js → connections-DvbgdkaB.js} +1 -1
- package/dist/public/{custom-element-security-CZ92anB8.js → custom-element-security-NCqQ5lmC.js} +1 -1
- package/dist/public/docs/README.md +3 -1
- package/dist/public/docs/architecture/overview.md +12 -12
- package/dist/public/docs/architecture/storage.md +5 -2
- package/dist/public/docs/decisions/bundled-typescript-architecture.md +1 -1
- package/dist/public/docs/decisions/indexeddb-and-opfs-storage.md +6 -3
- package/dist/public/docs/decisions/native-web-components-and-signals.md +1 -1
- package/dist/public/docs/example/article/index.html +2 -2
- package/dist/public/docs/guides/configuring-messaging-channels.md +41 -6
- package/dist/public/docs/guides/server-development-configuration.md +24 -0
- package/dist/public/docs/publishing/index.html +19 -76
- package/dist/public/docs/skill-creator/index.html +2 -2
- package/dist/public/docs/subsystems/backup.md +56 -0
- package/dist/public/docs/subsystems/cli.md +124 -0
- package/dist/public/docs/subsystems/control-plane.md +158 -0
- package/dist/public/docs/subsystems/notifications.md +54 -2
- package/dist/public/docs/subsystems/reactive-ui.md +1 -0
- package/dist/public/downloadGroupFile-By0GR6eV.js +1 -0
- package/dist/public/e2e/README.md +5 -1
- package/dist/public/{e2e-bridge-DkS5xFGX.js → e2e-bridge-IGWsRl4i.js} +1 -1
- package/dist/public/file-viewer-D0eflgt6.js +1 -0
- package/dist/public/files/main/~/docs/publishing-to-github-pages.md +17 -82
- package/dist/public/getAllTasks-vpBlgGsc.js +1 -0
- package/dist/public/getGroupDir-P1h9wl6S.js +1 -0
- package/dist/public/{git-BkjrsRXX.js → git-7Vq_SJSp.js} +2 -2
- package/dist/public/{iframe-sanitizer-L6Jyu__Z.js → iframe-sanitizer-y2Z1pPwY.js} +1 -1
- package/dist/public/index.html +2 -2
- package/dist/public/index.js +1 -1
- package/dist/public/{initChatSplitResize-BslPXAXS.js → initChatSplitResize-CzV2E7Ul.js} +1 -1
- package/dist/public/initControlPlane-C9jpnWev.js +1 -0
- package/dist/public/llms.txt +1 -1
- package/dist/public/main/index.html +2 -2
- package/dist/public/main/memory/index.html +2 -2
- package/dist/public/{markdown-6MBrIo-S.js → markdown-BnpKxSxl.js} +1 -1
- package/dist/public/mcp-reconnect-9emsqGRe.js +1 -0
- package/dist/public/{memoryStorage-CjGseKI1.js → memoryStorage-C0KvLNUp.js} +1 -1
- package/dist/public/{orchestrator-B9KmIj8Q.js → orchestrator-3h9xxHUm.js} +676 -676
- package/dist/public/orchestrator-nJj59RqD.js +15 -0
- package/dist/public/pages/main/~/docs/publishing-to-github-pages.md +17 -82
- package/dist/public/{peerjs-Dv8JNm7v.js → peerjs-BP4laMXj.js} +1 -1
- package/dist/public/{push-client-Dqq06wbB.js → push-client-CrjEXK3U.js} +1 -1
- package/dist/public/service-worker/fetch-proxy.js +1 -1
- package/dist/public/service-worker/push-handler.js +1 -1
- package/dist/public/service-worker.js +1 -1
- package/dist/public/{shadow-claw-Bd1rfuVC.js → shadow-claw-CbwETBWf.js} +2 -2
- package/dist/public/{shadow-claw-accounts-DL2l3oM0.js → shadow-claw-accounts-B4sBTQEj.js} +3 -3
- package/dist/public/{shadow-claw-channels-BGXDsQ38.js → shadow-claw-channels-BDWG5Qa3.js} +1 -1
- package/dist/public/{shadow-claw-chat-NXg6zz_l.js → shadow-claw-chat-Crv0DTCY.js} +6 -6
- package/dist/public/shadow-claw-control-plane-CX1IGbh9.js +199 -0
- package/dist/public/{shadow-claw-conversations-C8eqGpP9.js → shadow-claw-conversations-_9qAaRXk.js} +1 -1
- package/dist/public/shadow-claw-dialog-BrOwAOdk.js +1 -0
- package/dist/public/shadow-claw-file-viewer-Blf6yGVD.js +577 -0
- package/dist/public/shadow-claw-files-CYySBDCl.js +840 -0
- package/dist/public/{shadow-claw-git-C0ntz7aX.js → shadow-claw-git-v8S0JjuR.js} +3 -3
- package/dist/public/{shadow-claw-integrations-DCXV2r69.js → shadow-claw-integrations-D7ufpwhX.js} +1 -1
- package/dist/public/{shadow-claw-llm-CudgFQsS.js → shadow-claw-llm-GWgoVfhI.js} +2 -2
- package/dist/public/{shadow-claw-mcp-remote-B8RkpX28.js → shadow-claw-mcp-remote-kY4LMFNN.js} +1 -1
- package/dist/public/{shadow-claw-networking-BLiRsrsK.js → shadow-claw-networking-qHbP95z9.js} +1 -1
- package/dist/public/{shadow-claw-notifications-CQhQcjtL.js → shadow-claw-notifications-YyHOK2Fr.js} +1 -1
- package/dist/public/{shadow-claw-page-header-NeRD0tKU.js → shadow-claw-page-header-DMKbkv3s.js} +1 -1
- package/dist/public/{shadow-claw-pages-DCHnFh6r.js → shadow-claw-pages-DFZHhzFZ.js} +3 -3
- package/dist/public/{shadow-claw-settings-VAfjKFQg.js → shadow-claw-settings-DhxrKIfA.js} +10 -3
- package/dist/public/shadow-claw-storage-BFynbP0j.js +219 -0
- package/dist/public/{shadow-claw-task-server-Dk33N_1G.js → shadow-claw-task-server-3BIeb5oZ.js} +25 -4
- package/dist/public/{shadow-claw-tasks-xA1VfmWN.js → shadow-claw-tasks-Caxc9atL.js} +4 -4
- package/dist/public/{shadow-claw-terminal-Du2r0ciq.js → shadow-claw-terminal-Beb5qF-U.js} +1 -1
- package/dist/public/{shadow-claw-tools-CZj4rq7l.js → shadow-claw-tools-BfJx3A9W.js} +2 -2
- package/dist/public/{shadow-claw-webvm-JYUkWICG.js → shadow-claw-webvm-BCS8ykeP.js} +1 -1
- package/dist/public/static-main/~/docs/publishing-to-github-pages.md +17 -82
- package/dist/public/static-main-manifest.json +1 -1
- package/dist/public/{syncWebMcpRegistration-DfpvASnh.js → syncWebMcpRegistration-B_M2fCjH.js} +1 -1
- package/dist/server.js +286 -189
- package/package.json +2 -1
- package/src/__mocks__/jszip.cjs +15 -5
- package/src/components/settings/shadow-claw-accounts/shadow-claw-accounts.test.ts +35 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.css +117 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.html +81 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.test.ts +127 -0
- package/src/components/settings/shadow-claw-control-plane/shadow-claw-control-plane.ts +292 -0
- package/src/components/settings/shadow-claw-git/shadow-claw-git.test.ts +72 -0
- package/src/components/settings/shadow-claw-integrations/connection-test-auth.test.ts +20 -30
- package/src/components/settings/shadow-claw-integrations/shadow-claw-integrations.test.ts +116 -0
- package/src/components/settings/shadow-claw-llm/model-ranking.test.ts +143 -51
- package/src/components/settings/shadow-claw-llm/shadow-claw-llm.test.ts +307 -4
- package/src/components/settings/shadow-claw-llm/shadow-claw-llm.ts +8 -8
- package/src/components/settings/shadow-claw-mcp-remote/shadow-claw-mcp-remote.test.ts +31 -0
- package/src/components/settings/shadow-claw-peerjs/shadow-claw-peerjs.test.ts +186 -0
- package/src/components/settings/shadow-claw-storage/shadow-claw-storage.html +17 -0
- package/src/components/settings/shadow-claw-storage/shadow-claw-storage.test.ts +120 -0
- package/src/components/settings/shadow-claw-storage/shadow-claw-storage.ts +56 -0
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.css +23 -2
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.html +1 -1
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.test.ts +19 -0
- package/src/components/settings/shadow-claw-task-server/shadow-claw-task-server.ts +2 -1
- package/src/components/shadow-claw/utils/initChatSplitResize.test.ts +180 -13
- package/src/components/shadow-claw/utils/setupEffects.test.ts +39 -0
- package/src/components/shadow-claw-a2ui/catalog/basic/shared.test.ts +117 -0
- package/src/components/shadow-claw-a2ui/shadow-claw-a2ui.test.ts +1159 -1
- package/src/components/shadow-claw-chat/shadow-claw-chat.test.ts +1523 -19
- package/src/components/shadow-claw-chat/utils/escapeHtml.test.ts +13 -0
- package/src/components/shadow-claw-chat/utils/getPeerChatDisplayStatus.test.ts +20 -0
- package/src/components/shadow-claw-conversations/shadow-claw-conversations.test.ts +494 -0
- package/src/components/shadow-claw-dialog/shadow-claw-dialog.ts +13 -2
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.test.ts +378 -0
- package/src/components/shadow-claw-file-viewer/shadow-claw-file-viewer.ts +11 -9
- package/src/components/shadow-claw-files/shadow-claw-files.css +41 -17
- package/src/components/shadow-claw-files/shadow-claw-files.html +35 -0
- package/src/components/shadow-claw-files/shadow-claw-files.test.ts +627 -1
- package/src/components/shadow-claw-files/shadow-claw-files.ts +170 -6
- package/src/components/shadow-claw-pages/utils/resolveWorkspaceLinkPath.test.ts +31 -0
- package/src/components/shadow-claw-pages/utils/resolveWorkspaceLinkPath.ts +2 -1
- package/src/components/shadow-claw-pages/utils/rewriteWorkspacePreviewHtml.test.ts +15 -0
- package/src/components/shadow-claw-pdf-viewer/utils/installGetOrInsertComputedPolyfill.test.ts +65 -0
- package/src/components/shadow-claw-settings/shadow-claw-settings.html +7 -0
- package/src/components/shadow-claw-settings/shadow-claw-settings.test.ts +221 -0
- package/src/components/shadow-claw-settings/shadow-claw-settings.ts +2 -0
- package/src/components/shadow-claw-tasks/shadow-claw-tasks.test.ts +407 -0
- package/src/components/shadow-claw-terminal/utils/consumeEscapeSequence.test.ts +58 -0
- package/src/components/shadow-claw-tools/shadow-claw-tools.test.ts +80 -0
- package/src/config/config.test.ts +9 -0
- package/src/config/config.ts +15 -0
- package/src/core/app-routes.test.ts +56 -0
- package/src/core/app-routes.ts +8 -1
- package/src/core/backup-controller.test.ts +101 -0
- package/src/core/backup-controller.ts +137 -0
- package/src/core/control-plane-client.test.ts +408 -0
- package/src/core/control-plane-client.ts +425 -0
- package/src/core/orchestrator/orchestrator.ts +4 -0
- package/src/core/orchestrator/utils/compactContext.test.ts +78 -0
- package/src/core/orchestrator/utils/operations/channel.test.ts +254 -9
- package/src/core/orchestrator/utils/operations/provider.test.ts +326 -34
- package/src/core/orchestrator/utils/operations/vm.test.ts +114 -2
- package/src/core/orchestrator/utils/syncProxyConfigToServiceWorker.test.ts +60 -0
- package/src/core/utils/initControlPlane.test.ts +276 -0
- package/src/core/utils/initControlPlane.ts +437 -0
- package/src/core/utils/initializeApp.ts +18 -0
- package/src/db/getSession.test.ts +13 -1
- package/src/db/openDatabase.test.ts +10 -1
- package/src/db/openDatabase.ts +3 -1
- package/src/server/app.test.ts +12 -0
- package/src/server/app.ts +18 -8
- package/src/server/client-registry.test.ts +250 -0
- package/src/server/client-registry.ts +477 -0
- package/src/server/config.ts +15 -0
- package/src/server/control-plane-types.ts +120 -0
- package/src/server/control-plane.test.ts +472 -0
- package/src/server/control-plane.ts +581 -0
- package/src/server/routes/backup.test.ts +263 -0
- package/src/server/routes/backup.ts +262 -0
- package/src/server/routes/integrations-email.test.ts +532 -0
- package/src/server/routes/llamafile.test.ts +172 -48
- package/src/server/routes/mesh-llm.test.ts +192 -0
- package/src/server/routes/ollama.test.ts +49 -0
- package/src/server/server-peer.test.ts +86 -0
- package/src/server/server-peer.ts +330 -0
- package/src/server/server.ts +35 -0
- package/src/server/services/llamafile-manager.test.ts +230 -6
- package/src/server/services/transformers-runtime.test.ts +186 -18
- package/src/server/webrtc.test.ts +21 -0
- package/src/server/webrtc.ts +72 -0
- package/src/service-worker/fetch-proxy.test.ts +64 -1
- package/src/service-worker/push-handler.ts +39 -0
- package/src/shell/vm.test.ts +744 -12
- package/src/storage/ensureMainGroupIndex.test.ts +153 -0
- package/src/storage/getGroupFile.test.ts +66 -0
- package/src/storage/memoryStorage.test.ts +82 -2
- package/src/storage/migrateLegacyOpfs.test.ts +327 -0
- package/src/storage/migrateLegacyOpfs.ts +117 -0
- package/src/storage/writeGroupFileBytes.test.ts +143 -0
- package/src/stores/chat-ui.test.ts +20 -0
- package/src/stores/orchestrator.test.ts +134 -0
- package/src/stores/toast.test.ts +50 -0
- package/src/stores/tools.test.ts +147 -10
- package/src/subsystems/channels/imessage.test.ts +59 -0
- package/src/subsystems/channels/peerjs.test.ts +500 -62
- package/src/subsystems/channels/peerjs.ts +71 -4
- package/src/subsystems/channels/telegram.test.ts +177 -0
- package/src/subsystems/email/catalog.test.ts +1 -0
- package/src/subsystems/email/connections.test.ts +39 -1
- package/src/subsystems/git/credentials.test.ts +160 -825
- package/src/subsystems/git/git.test.ts +463 -0
- package/src/subsystems/notifications/__mocks__/node-sqlite.cjs +12 -1
- package/src/subsystems/notifications/push-routes.test.ts +33 -0
- package/src/subsystems/notifications/push-routes.ts +23 -0
- package/src/subsystems/notifications/push-store.ts +1 -1
- package/src/subsystems/providers/builtin-ai-tasks.test.ts +74 -0
- package/src/subsystems/providers/litert-lm-provider.test.ts +212 -51
- package/src/subsystems/providers/model-registry.test.ts +115 -0
- package/src/subsystems/providers/providers.test.ts +94 -1
- package/src/subsystems/providers/transformers-js-provider.test.ts +279 -0
- package/src/subsystems/providers/utils/createModelCacheFetch.test.ts +2 -2
- package/src/subsystems/providers/utils/downloadModelToCache.test.ts +2 -2
- package/src/subsystems/providers/utils/loadModelStream.test.ts +2 -2
- package/src/subsystems/skills/activateSkill.test.ts +101 -0
- package/src/subsystems/skills/discoverSkills.test.ts +251 -0
- package/src/subsystems/tools/cron.test.ts +64 -0
- package/src/subsystems/tools/declarative.test.ts +255 -89
- package/src/testing/assets/v86.9pfs/libv86.mjs +63 -0
- package/src/testing/assets/v86.ext2/libv86.mjs +3 -0
- package/src/testing/assets_missing_v86/assets/v86.9pfs/libv86.mjs +1 -0
- package/src/testing/jest-setup.ts +328 -40
- package/src/testing/mock-v86.mjs +49 -0
- package/src/ui/a2ui/types.ts +4 -3
- package/src/ui/a2ui/utils/normaliseComponentsToMap.test.ts +102 -0
- package/src/ui/a2ui/utils/resolveChildIds.test.ts +63 -0
- package/src/ui/a2ui/utils/resolveDynamicNumber.test.ts +13 -1
- package/src/worker/tools/bash/utils/waitForVMReady.test.ts +40 -0
- package/src/worker/tools/email/email.test.ts +193 -8
- package/src/worker/tools/email/utils/asEmailAttachmentInputs.test.ts +47 -0
- package/src/worker/tools/git/git.test.ts +214 -3
- package/src/worker/tools/rooms/invite-to-room.test.ts +54 -0
- package/src/worker/tools/rooms/leave-room.test.ts +40 -0
- package/src/worker/tools/rooms/list-room-members.test.ts +75 -0
- package/src/worker/tools/tasks/tasks-utils.test.ts +22 -0
- package/src/worker/tools/ui/ask-user.test.ts +50 -0
- package/src/worker/tools/ui/clear-chat.test.ts +19 -0
- package/src/worker/tools/ui/get-current-time.test.ts +19 -0
- package/src/worker/tools/ui/javascript.test.ts +116 -0
- package/src/worker/tools/ui/list-components.test.ts +10 -0
- package/src/worker/tools/ui/list-tool-profiles.test.ts +42 -0
- package/src/worker/tools/ui/manage-tools.test.ts +34 -0
- package/src/worker/tools/ui/render-component.test.ts +346 -0
- package/src/worker/tools/ui/send-notification.test.ts +40 -0
- package/src/worker/tools/ui/utils/getAllowFullInternetAccess.test.ts +31 -0
- package/src/worker/tools/ui/utils/parseBooleanConfig.test.ts +25 -0
- package/src/worker/tools/workspace/attach-file.test.ts +84 -0
- package/src/worker/tools/workspace/copy-file.test.ts +69 -0
- package/src/worker/tools/workspace/create-directory.test.ts +40 -0
- package/src/worker/tools/workspace/delete-file.test.ts +73 -0
- package/src/worker/tools/workspace/diff-files.test.ts +94 -0
- package/src/worker/tools/workspace/list-files.test.ts +36 -0
- package/src/worker/tools/workspace/move-file.test.ts +66 -0
- package/src/worker/tools/workspace/open-file.test.ts +57 -0
- package/src/worker/tools/workspace/patch-file.test.ts +81 -0
- package/src/worker/tools/workspace/read-file.test.ts +116 -0
- package/src/worker/tools/workspace/send-file.test.ts +82 -0
- package/src/worker/tools/workspace/update-memory.test.ts +29 -0
- package/src/worker/tools/workspace/write-file.test.ts +28 -0
- package/src/worker/utils/rate-limit.test.ts +199 -0
- package/src/worker/utils/tool-state.test.ts +29 -0
- package/src/worker/utils/transformers-js-utils.test.ts +59 -0
- package/dist/public/config-AL77QAx4.js +0 -1
- package/dist/public/downloadGroupFile-BABv_1mJ.js +0 -1
- package/dist/public/file-viewer-CCSABMsE.js +0 -1
- package/dist/public/mcp-reconnect-DoicazQd.js +0 -1
- package/dist/public/orchestrator-4su7iyWj.js +0 -15
- package/dist/public/shadow-claw-dialog-ByyHgxM9.js +0 -1
- package/dist/public/shadow-claw-file-viewer-9Qkwc30T.js +0 -577
- package/dist/public/shadow-claw-files-aDyq6Hv7.js +0 -781
- package/dist/public/shadow-claw-storage-Cf31np81.js +0 -202
|
@@ -1385,6 +1385,111 @@ describe("shadow-claw-file-viewer", () => {
|
|
|
1385
1385
|
expect(navigate).toHaveBeenCalledWith("/#Pages?path=docs/linked.md");
|
|
1386
1386
|
});
|
|
1387
1387
|
|
|
1388
|
+
it("navigates relative image link in root via route URL", async () => {
|
|
1389
|
+
const component = new ShadowClawFileViewer();
|
|
1390
|
+
component.db = {} as any;
|
|
1391
|
+
|
|
1392
|
+
const navigate = jest.fn();
|
|
1393
|
+
Object.defineProperty(window as any, "navigation", {
|
|
1394
|
+
configurable: true,
|
|
1395
|
+
value: { navigate },
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
(fileViewerStore as any).file = {
|
|
1399
|
+
path: "index.md",
|
|
1400
|
+
name: "index.md",
|
|
1401
|
+
kind: "text",
|
|
1402
|
+
content: "# index",
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1405
|
+
const body = document.createElement("div");
|
|
1406
|
+
const link = document.createElement("a");
|
|
1407
|
+
link.setAttribute("href", "01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg");
|
|
1408
|
+
body.appendChild(link);
|
|
1409
|
+
|
|
1410
|
+
const event = new MouseEvent("click", {
|
|
1411
|
+
bubbles: true,
|
|
1412
|
+
cancelable: true,
|
|
1413
|
+
button: 0,
|
|
1414
|
+
});
|
|
1415
|
+
Object.defineProperty(event, "target", {
|
|
1416
|
+
configurable: true,
|
|
1417
|
+
value: link,
|
|
1418
|
+
});
|
|
1419
|
+
|
|
1420
|
+
await component.handlePreviewLinkClick(event);
|
|
1421
|
+
|
|
1422
|
+
expect(event.defaultPrevented).toBe(true);
|
|
1423
|
+
expect(navigate).toHaveBeenCalledWith(
|
|
1424
|
+
"/files/test-group/01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg",
|
|
1425
|
+
);
|
|
1426
|
+
});
|
|
1427
|
+
|
|
1428
|
+
it("rewrites relative asset hrefs in markdown preview HTML", () => {
|
|
1429
|
+
const component = new ShadowClawFileViewer();
|
|
1430
|
+
const inputHtml =
|
|
1431
|
+
'<p><a href="01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg">image</a></p>';
|
|
1432
|
+
const rewritten = component.rewriteWorkspacePreviewHtml(
|
|
1433
|
+
inputHtml,
|
|
1434
|
+
"index.md",
|
|
1435
|
+
);
|
|
1436
|
+
expect(rewritten).toContain(
|
|
1437
|
+
'href="/files/test-group/01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg"',
|
|
1438
|
+
);
|
|
1439
|
+
});
|
|
1440
|
+
|
|
1441
|
+
it("rewrites relative asset hrefs when subpath base path is active", () => {
|
|
1442
|
+
document.querySelectorAll("base").forEach((el) => el.remove());
|
|
1443
|
+
const baseEl = document.createElement("base");
|
|
1444
|
+
baseEl.setAttribute("href", "/shadow-claw/");
|
|
1445
|
+
document.head.appendChild(baseEl);
|
|
1446
|
+
(globalThis as any).__applyBasePathCacheReset?.();
|
|
1447
|
+
|
|
1448
|
+
try {
|
|
1449
|
+
const component = new ShadowClawFileViewer();
|
|
1450
|
+
const inputHtml =
|
|
1451
|
+
'<p><a href="01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg">image</a></p>';
|
|
1452
|
+
const rewritten = component.rewriteWorkspacePreviewHtml(
|
|
1453
|
+
inputHtml,
|
|
1454
|
+
"index.md",
|
|
1455
|
+
);
|
|
1456
|
+
expect(rewritten).toContain(
|
|
1457
|
+
'href="/shadow-claw/files/test-group/01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg"',
|
|
1458
|
+
);
|
|
1459
|
+
} finally {
|
|
1460
|
+
baseEl.remove();
|
|
1461
|
+
(globalThis as any).__applyBasePathCacheReset?.();
|
|
1462
|
+
}
|
|
1463
|
+
});
|
|
1464
|
+
|
|
1465
|
+
it("bindEventListeners delegates clicks within .file-content to handlePreviewLinkClick", () => {
|
|
1466
|
+
const component = new ShadowClawFileViewer();
|
|
1467
|
+
const root = document.createElement("div");
|
|
1468
|
+
const content = document.createElement("div");
|
|
1469
|
+
content.className = "file-content";
|
|
1470
|
+
const link = document.createElement("a");
|
|
1471
|
+
link.setAttribute("href", "01M1A1R4Y708YJNPGV2NHQKYF7-IMG_0233.jpeg");
|
|
1472
|
+
content.appendChild(link);
|
|
1473
|
+
root.appendChild(content);
|
|
1474
|
+
Object.defineProperty(component, "shadowRoot", {
|
|
1475
|
+
configurable: true,
|
|
1476
|
+
value: root,
|
|
1477
|
+
});
|
|
1478
|
+
|
|
1479
|
+
const spy = jest
|
|
1480
|
+
.spyOn(component, "handlePreviewLinkClick")
|
|
1481
|
+
.mockImplementation(async () => {});
|
|
1482
|
+
|
|
1483
|
+
component.bindEventListeners();
|
|
1484
|
+
|
|
1485
|
+
link.dispatchEvent(
|
|
1486
|
+
new MouseEvent("click", { bubbles: true, cancelable: true }),
|
|
1487
|
+
);
|
|
1488
|
+
|
|
1489
|
+
expect(spy).toHaveBeenCalledTimes(1);
|
|
1490
|
+
spy.mockRestore();
|
|
1491
|
+
});
|
|
1492
|
+
|
|
1388
1493
|
it("resolves relative links against the opened file directory", () => {
|
|
1389
1494
|
const component = new ShadowClawFileViewer();
|
|
1390
1495
|
|
|
@@ -1757,4 +1862,277 @@ describe("shadow-claw-file-viewer", () => {
|
|
|
1757
1862
|
expect(resolved).toContain("data:image/png;base64,iVBORw==");
|
|
1758
1863
|
});
|
|
1759
1864
|
});
|
|
1865
|
+
|
|
1866
|
+
describe("syncIframeTheme & storage proxy bridge", () => {
|
|
1867
|
+
it("syncs theme and custom properties to previewFrameWindow", () => {
|
|
1868
|
+
const component = new ShadowClawFileViewer() as any;
|
|
1869
|
+
const mockPostMessage = jest.fn();
|
|
1870
|
+
component.previewFrameWindow = { postMessage: mockPostMessage };
|
|
1871
|
+
|
|
1872
|
+
component.syncIframeTheme();
|
|
1873
|
+
|
|
1874
|
+
expect(mockPostMessage).toHaveBeenCalledWith(
|
|
1875
|
+
expect.objectContaining({
|
|
1876
|
+
type: "shadow-claw-theme-update",
|
|
1877
|
+
theme: expect.any(String),
|
|
1878
|
+
customProperties: expect.any(Object),
|
|
1879
|
+
}),
|
|
1880
|
+
"*",
|
|
1881
|
+
);
|
|
1882
|
+
});
|
|
1883
|
+
|
|
1884
|
+
it("handles storage proxy requests (setItem, removeItem, getAllItems, idb-put, idb-get)", async () => {
|
|
1885
|
+
const component = new ShadowClawFileViewer() as any;
|
|
1886
|
+
const mockSource = { postMessage: jest.fn() };
|
|
1887
|
+
|
|
1888
|
+
(fileViewerStore as any).file = { path: "demo.html", name: "demo.html" };
|
|
1889
|
+
|
|
1890
|
+
// 1. setItem
|
|
1891
|
+
await component.handleStorageProxyRequest(
|
|
1892
|
+
mockSource,
|
|
1893
|
+
"req-1",
|
|
1894
|
+
"setItem",
|
|
1895
|
+
{
|
|
1896
|
+
key: "pref",
|
|
1897
|
+
value: "dark",
|
|
1898
|
+
},
|
|
1899
|
+
);
|
|
1900
|
+
expect(mockSource.postMessage).toHaveBeenCalledWith(
|
|
1901
|
+
expect.objectContaining({
|
|
1902
|
+
type: "shadow-claw-storage-proxy-result",
|
|
1903
|
+
requestId: "req-1",
|
|
1904
|
+
result: null,
|
|
1905
|
+
}),
|
|
1906
|
+
"*",
|
|
1907
|
+
);
|
|
1908
|
+
|
|
1909
|
+
// 2. idb-put
|
|
1910
|
+
await component.handleStorageProxyRequest(
|
|
1911
|
+
mockSource,
|
|
1912
|
+
"req-2",
|
|
1913
|
+
"idb-put",
|
|
1914
|
+
{
|
|
1915
|
+
dbName: "test-db",
|
|
1916
|
+
storeName: "items",
|
|
1917
|
+
key: "k1",
|
|
1918
|
+
value: { foo: "bar" },
|
|
1919
|
+
},
|
|
1920
|
+
);
|
|
1921
|
+
expect(mockSource.postMessage).toHaveBeenCalledWith(
|
|
1922
|
+
expect.objectContaining({
|
|
1923
|
+
type: "shadow-claw-storage-proxy-result",
|
|
1924
|
+
requestId: "req-2",
|
|
1925
|
+
result: "k1",
|
|
1926
|
+
}),
|
|
1927
|
+
"*",
|
|
1928
|
+
);
|
|
1929
|
+
|
|
1930
|
+
// 3. Unknown method sends error
|
|
1931
|
+
await component.handleStorageProxyRequest(
|
|
1932
|
+
mockSource,
|
|
1933
|
+
"req-3",
|
|
1934
|
+
"unknownMethod",
|
|
1935
|
+
{},
|
|
1936
|
+
);
|
|
1937
|
+
expect(mockSource.postMessage).toHaveBeenCalledWith(
|
|
1938
|
+
expect.objectContaining({
|
|
1939
|
+
type: "shadow-claw-storage-proxy-result",
|
|
1940
|
+
requestId: "req-3",
|
|
1941
|
+
error: expect.stringContaining("Unknown storage proxy method"),
|
|
1942
|
+
}),
|
|
1943
|
+
"*",
|
|
1944
|
+
);
|
|
1945
|
+
});
|
|
1946
|
+
|
|
1947
|
+
it("builds web share File instances from native files and binary buffers", () => {
|
|
1948
|
+
const component = new ShadowClawFileViewer() as any;
|
|
1949
|
+
|
|
1950
|
+
const native = new File(["content"], "test.txt", {
|
|
1951
|
+
type: "text/plain",
|
|
1952
|
+
});
|
|
1953
|
+
expect(component.buildWebShareFile({ nativeFile: native })).toBe(native);
|
|
1954
|
+
|
|
1955
|
+
const binaryFile = component.buildWebShareFile({
|
|
1956
|
+
name: "data.bin",
|
|
1957
|
+
binaryContent: new Uint8Array([1, 2, 3]),
|
|
1958
|
+
mimeType: "application/octet-stream",
|
|
1959
|
+
});
|
|
1960
|
+
expect(binaryFile).toBeInstanceOf(File);
|
|
1961
|
+
expect(binaryFile.name).toBe("data.bin");
|
|
1962
|
+
|
|
1963
|
+
expect(component.buildWebShareFile(null)).toBeNull();
|
|
1964
|
+
expect(component.buildWebShareFile({ binaryContent: null })).toBeNull();
|
|
1965
|
+
expect(
|
|
1966
|
+
component.buildWebShareFile({ binaryContent: new Uint8Array([]) }),
|
|
1967
|
+
).toBeNull();
|
|
1968
|
+
});
|
|
1969
|
+
|
|
1970
|
+
it("cleans up resources and disconnects observers on disconnectedCallback", () => {
|
|
1971
|
+
const component = new ShadowClawFileViewer() as any;
|
|
1972
|
+
const disposeMock = jest.fn();
|
|
1973
|
+
component.broadcastProxy = { dispose: disposeMock };
|
|
1974
|
+
const disconnectMock = jest.fn();
|
|
1975
|
+
component.themeObserver = { disconnect: disconnectMock };
|
|
1976
|
+
const revokeMock = jest.spyOn(component, "revokeObjectUrl");
|
|
1977
|
+
|
|
1978
|
+
component.disconnectedCallback();
|
|
1979
|
+
|
|
1980
|
+
expect(disposeMock).toHaveBeenCalledTimes(1);
|
|
1981
|
+
expect(component.broadcastProxy).toBeNull();
|
|
1982
|
+
expect(disconnectMock).toHaveBeenCalledTimes(1);
|
|
1983
|
+
expect(component.themeObserver).toBeNull();
|
|
1984
|
+
expect(revokeMock).toHaveBeenCalledTimes(1);
|
|
1985
|
+
});
|
|
1986
|
+
|
|
1987
|
+
it("synchronizes iframe theme with document dark/light mode class", () => {
|
|
1988
|
+
const component = new ShadowClawFileViewer() as any;
|
|
1989
|
+
const mockPostMessage = jest.fn();
|
|
1990
|
+
component.previewFrameWindow = { postMessage: mockPostMessage };
|
|
1991
|
+
|
|
1992
|
+
document.documentElement.className = "dark-theme";
|
|
1993
|
+
component.syncIframeTheme();
|
|
1994
|
+
|
|
1995
|
+
expect(mockPostMessage).toHaveBeenCalledWith(
|
|
1996
|
+
expect.objectContaining({
|
|
1997
|
+
type: "shadow-claw-theme-update",
|
|
1998
|
+
}),
|
|
1999
|
+
"*",
|
|
2000
|
+
);
|
|
2001
|
+
});
|
|
2002
|
+
|
|
2003
|
+
it("handles openFolderInFilesView, openWorkspaceLink, and viewer dismissal", async () => {
|
|
2004
|
+
const component = new ShadowClawFileViewer() as any;
|
|
2005
|
+
component.db = {} as any;
|
|
2006
|
+
|
|
2007
|
+
// 1. openFolderInFilesView
|
|
2008
|
+
await component.openFolderInFilesView("docs/subfolder/");
|
|
2009
|
+
expect(orchestratorStore.setCurrentPath).toHaveBeenCalledWith(
|
|
2010
|
+
component.db,
|
|
2011
|
+
"docs/subfolder",
|
|
2012
|
+
);
|
|
2013
|
+
expect(fileViewerStore.closeFile).toHaveBeenCalled();
|
|
2014
|
+
|
|
2015
|
+
// 2. openWorkspaceLink with extension
|
|
2016
|
+
await component.openWorkspaceLink("notes.txt", "docs");
|
|
2017
|
+
expect(fileViewerStore.openFile).toHaveBeenCalledWith(
|
|
2018
|
+
component.db,
|
|
2019
|
+
expect.any(String),
|
|
2020
|
+
expect.any(String),
|
|
2021
|
+
);
|
|
2022
|
+
|
|
2023
|
+
// 3. canDismissViewer when not dirty
|
|
2024
|
+
component.isDirty = false;
|
|
2025
|
+
const canDismiss = await component.canDismissViewer();
|
|
2026
|
+
expect(canDismiss).toBe(true);
|
|
2027
|
+
|
|
2028
|
+
// 4. canDismissViewer when dirty
|
|
2029
|
+
component.isDirty = true;
|
|
2030
|
+
component.requestConfirmation = jest.fn<any>().mockResolvedValue(true);
|
|
2031
|
+
const canDismissDirty = await component.canDismissViewer();
|
|
2032
|
+
expect(canDismissDirty).toBe(true);
|
|
2033
|
+
|
|
2034
|
+
// 5. requestCloseViewer
|
|
2035
|
+
component.isDirty = false;
|
|
2036
|
+
const closed = await component.requestCloseViewer();
|
|
2037
|
+
expect(closed).toBe(true);
|
|
2038
|
+
expect(fileViewerStore.closeFile).toHaveBeenCalled();
|
|
2039
|
+
});
|
|
2040
|
+
|
|
2041
|
+
it("handles handleSave, handleShareFile, and toggleFullscreenMode", async () => {
|
|
2042
|
+
const { ShadowClawFileViewer } =
|
|
2043
|
+
await import("./shadow-claw-file-viewer.js");
|
|
2044
|
+
const { writeGroupFile } =
|
|
2045
|
+
await import("../../storage/writeGroupFile.js");
|
|
2046
|
+
|
|
2047
|
+
const component = new ShadowClawFileViewer() as any;
|
|
2048
|
+
document.body.appendChild(component);
|
|
2049
|
+
component.db = {} as any;
|
|
2050
|
+
|
|
2051
|
+
// 1. handleSave
|
|
2052
|
+
(fileViewerStore as any).file = {
|
|
2053
|
+
name: "test.txt",
|
|
2054
|
+
path: "docs/test.txt",
|
|
2055
|
+
kind: "text",
|
|
2056
|
+
content: "old content",
|
|
2057
|
+
groupId: "g1",
|
|
2058
|
+
};
|
|
2059
|
+
let editor = component.shadowRoot?.querySelector(".file-editor");
|
|
2060
|
+
if (!editor) {
|
|
2061
|
+
editor = document.createElement("textarea");
|
|
2062
|
+
editor.className = "file-editor";
|
|
2063
|
+
component.shadowRoot?.appendChild(editor);
|
|
2064
|
+
}
|
|
2065
|
+
editor.value = "new content";
|
|
2066
|
+
|
|
2067
|
+
await component.handleSave();
|
|
2068
|
+
expect(writeGroupFile).toHaveBeenCalledWith(
|
|
2069
|
+
component.db,
|
|
2070
|
+
"g1",
|
|
2071
|
+
"docs/test.txt",
|
|
2072
|
+
"new content",
|
|
2073
|
+
);
|
|
2074
|
+
|
|
2075
|
+
// 2. handleShareFile
|
|
2076
|
+
const mockShare = jest.fn<any>().mockResolvedValue(undefined);
|
|
2077
|
+
Object.defineProperty(navigator, "share", {
|
|
2078
|
+
value: mockShare,
|
|
2079
|
+
configurable: true,
|
|
2080
|
+
writable: true,
|
|
2081
|
+
});
|
|
2082
|
+
await component.handleShareFile();
|
|
2083
|
+
expect(mockShare).toHaveBeenCalledWith(
|
|
2084
|
+
expect.objectContaining({ title: "test.txt" }),
|
|
2085
|
+
);
|
|
2086
|
+
|
|
2087
|
+
// 3. renderBinaryPreview - empty
|
|
2088
|
+
const contentEl = document.createElement("div");
|
|
2089
|
+
component.renderBinaryPreview(contentEl, {
|
|
2090
|
+
name: "empty.bin",
|
|
2091
|
+
mimeType: "application/octet-stream",
|
|
2092
|
+
binaryContent: new Uint8Array(0),
|
|
2093
|
+
});
|
|
2094
|
+
expect(contentEl.textContent).toBe("Binary content unavailable.");
|
|
2095
|
+
|
|
2096
|
+
// 4. renderBinaryPreview - image
|
|
2097
|
+
(global.URL.createObjectURL as any) = jest
|
|
2098
|
+
.fn<any>()
|
|
2099
|
+
.mockReturnValue("blob:image-url");
|
|
2100
|
+
component.renderBinaryPreview(contentEl, {
|
|
2101
|
+
name: "img.jpg",
|
|
2102
|
+
mimeType: "image/jpeg",
|
|
2103
|
+
binaryContent: new Uint8Array([1, 2, 3]),
|
|
2104
|
+
});
|
|
2105
|
+
expect(contentEl.querySelector("img")).not.toBeNull();
|
|
2106
|
+
|
|
2107
|
+
// 5. renderBinaryPreview - video
|
|
2108
|
+
component.renderBinaryPreview(contentEl, {
|
|
2109
|
+
name: "clip.mp4",
|
|
2110
|
+
mimeType: "video/mp4",
|
|
2111
|
+
binaryContent: new Uint8Array([1, 2, 3]),
|
|
2112
|
+
});
|
|
2113
|
+
expect(contentEl.querySelector("video")).not.toBeNull();
|
|
2114
|
+
|
|
2115
|
+
// 6. renderBinaryPreview - audio
|
|
2116
|
+
component.renderBinaryPreview(contentEl, {
|
|
2117
|
+
name: "song.mp3",
|
|
2118
|
+
mimeType: "audio/mp3",
|
|
2119
|
+
binaryContent: new Uint8Array([1, 2, 3]),
|
|
2120
|
+
});
|
|
2121
|
+
expect(contentEl.querySelector("audio")).not.toBeNull();
|
|
2122
|
+
|
|
2123
|
+
// 7. renderBinaryPreview - iframe/pdf
|
|
2124
|
+
component.renderBinaryPreview(contentEl, {
|
|
2125
|
+
name: "doc.pdf",
|
|
2126
|
+
mimeType: "application/pdf",
|
|
2127
|
+
binaryContent: new Uint8Array([1, 2, 3]),
|
|
2128
|
+
});
|
|
2129
|
+
expect(contentEl.querySelector("iframe")).not.toBeNull();
|
|
2130
|
+
|
|
2131
|
+
// 8. resetContent
|
|
2132
|
+
component.resetContent();
|
|
2133
|
+
expect(component.isFullscreenMode).toBe(false);
|
|
2134
|
+
|
|
2135
|
+
document.body.removeChild(component);
|
|
2136
|
+
});
|
|
2137
|
+
});
|
|
1760
2138
|
});
|
|
@@ -251,6 +251,11 @@ export class ShadowClawFileViewer extends ShadowClawElement {
|
|
|
251
251
|
return;
|
|
252
252
|
}
|
|
253
253
|
|
|
254
|
+
const content = root.querySelector(".file-content");
|
|
255
|
+
content?.addEventListener("click", (event: Event) => {
|
|
256
|
+
void this.handlePreviewLinkClick(event as MouseEvent);
|
|
257
|
+
});
|
|
258
|
+
|
|
254
259
|
const modal = root.querySelector(".file-modal");
|
|
255
260
|
modal?.addEventListener("cancel", (event: Event) => {
|
|
256
261
|
event.preventDefault();
|
|
@@ -706,9 +711,8 @@ export class ShadowClawFileViewer extends ShadowClawElement {
|
|
|
706
711
|
|
|
707
712
|
const current = fileViewerStore.file;
|
|
708
713
|
const filePath = current?.path || current?.name || "";
|
|
709
|
-
const routeDir =
|
|
710
|
-
orchestratorStore.activeGroupId,
|
|
711
|
-
filePath,
|
|
714
|
+
const routeDir = applyBasePath(
|
|
715
|
+
getFileRouteDirPath(orchestratorStore.activeGroupId, filePath),
|
|
712
716
|
);
|
|
713
717
|
const resolved = resolveHrefAgainstRoute(
|
|
714
718
|
payload.href,
|
|
@@ -1110,9 +1114,8 @@ export class ShadowClawFileViewer extends ShadowClawElement {
|
|
|
1110
1114
|
return html;
|
|
1111
1115
|
}
|
|
1112
1116
|
|
|
1113
|
-
const routeDir =
|
|
1114
|
-
orchestratorStore.activeGroupId,
|
|
1115
|
-
filePath,
|
|
1117
|
+
const routeDir = applyBasePath(
|
|
1118
|
+
getFileRouteDirPath(orchestratorStore.activeGroupId, filePath),
|
|
1116
1119
|
);
|
|
1117
1120
|
const parsed = new DOMParser().parseFromString(html, "text/html");
|
|
1118
1121
|
|
|
@@ -1509,9 +1512,8 @@ export class ShadowClawFileViewer extends ShadowClawElement {
|
|
|
1509
1512
|
const current = fileViewerStore.file;
|
|
1510
1513
|
const basePath = current?.path || current?.name || "";
|
|
1511
1514
|
const href = link.getAttribute("href") || "";
|
|
1512
|
-
const routeDir =
|
|
1513
|
-
orchestratorStore.activeGroupId,
|
|
1514
|
-
basePath,
|
|
1515
|
+
const routeDir = applyBasePath(
|
|
1516
|
+
getFileRouteDirPath(orchestratorStore.activeGroupId, basePath),
|
|
1515
1517
|
);
|
|
1516
1518
|
const resolved = resolveHrefAgainstRoute(
|
|
1517
1519
|
href,
|
|
@@ -361,7 +361,8 @@
|
|
|
361
361
|
|
|
362
362
|
.files__new-label,
|
|
363
363
|
.files__rename-label,
|
|
364
|
-
.files__paste-rename-label
|
|
364
|
+
.files__paste-rename-label,
|
|
365
|
+
.files__upload-conflict-rename-label {
|
|
365
366
|
color: var(--shadow-claw-text-secondary);
|
|
366
367
|
font-size: var(--shadow-claw-font-size-sm);
|
|
367
368
|
font-weight: 600;
|
|
@@ -369,7 +370,8 @@
|
|
|
369
370
|
|
|
370
371
|
.files__new-input,
|
|
371
372
|
.files__rename-input,
|
|
372
|
-
.files__paste-rename-input
|
|
373
|
+
.files__paste-rename-input,
|
|
374
|
+
.files__upload-conflict-rename-input {
|
|
373
375
|
background-color: var(--shadow-claw-bg-primary);
|
|
374
376
|
border: 0.0625rem solid var(--shadow-claw-border-color);
|
|
375
377
|
border-radius: var(--shadow-claw-radius-s);
|
|
@@ -379,7 +381,8 @@
|
|
|
379
381
|
padding: 0.375rem 0.5rem;
|
|
380
382
|
}
|
|
381
383
|
|
|
382
|
-
.files__paste-rename-container
|
|
384
|
+
.files__paste-rename-container,
|
|
385
|
+
.files__upload-conflict-rename-container {
|
|
383
386
|
display: flex;
|
|
384
387
|
flex-direction: column;
|
|
385
388
|
gap: 0.5rem;
|
|
@@ -401,7 +404,8 @@
|
|
|
401
404
|
|
|
402
405
|
.files__new-input:focus,
|
|
403
406
|
.files__rename-input:focus,
|
|
404
|
-
.files__paste-rename-input:focus
|
|
407
|
+
.files__paste-rename-input:focus,
|
|
408
|
+
.files__upload-conflict-rename-input:focus {
|
|
405
409
|
border-color: var(--shadow-claw-accent-primary);
|
|
406
410
|
box-shadow: 0 0 0 0.125rem var(--shadow-claw-bg-tertiary);
|
|
407
411
|
outline: none;
|
|
@@ -469,7 +473,8 @@
|
|
|
469
473
|
opacity: 0.5;
|
|
470
474
|
}
|
|
471
475
|
|
|
472
|
-
.files__paste-dialog
|
|
476
|
+
.files__paste-dialog,
|
|
477
|
+
.files__upload-conflict-dialog {
|
|
473
478
|
background-color: var(--shadow-claw-bg-primary);
|
|
474
479
|
border: 0.0625rem solid var(--shadow-claw-border-color);
|
|
475
480
|
border-radius: var(--shadow-claw-radius-l, 1.5rem);
|
|
@@ -479,31 +484,36 @@
|
|
|
479
484
|
width: calc(100vw - 2rem);
|
|
480
485
|
}
|
|
481
486
|
|
|
482
|
-
.files__paste-dialog::backdrop
|
|
487
|
+
.files__paste-dialog::backdrop,
|
|
488
|
+
.files__upload-conflict-dialog::backdrop {
|
|
483
489
|
background-color: rgba(0, 0, 0, 0.35);
|
|
484
490
|
}
|
|
485
491
|
|
|
486
|
-
.files__paste-form
|
|
492
|
+
.files__paste-form,
|
|
493
|
+
.files__upload-conflict-form {
|
|
487
494
|
display: flex;
|
|
488
495
|
flex-direction: column;
|
|
489
496
|
gap: 1rem;
|
|
490
497
|
padding: 1.5rem;
|
|
491
498
|
}
|
|
492
499
|
|
|
493
|
-
.files__paste-title
|
|
500
|
+
.files__paste-title,
|
|
501
|
+
.files__upload-conflict-title {
|
|
494
502
|
font-size: 1.1rem;
|
|
495
503
|
font-weight: 700;
|
|
496
504
|
margin: 0;
|
|
497
505
|
}
|
|
498
506
|
|
|
499
|
-
.files__paste-message
|
|
507
|
+
.files__paste-message,
|
|
508
|
+
.files__upload-conflict-message {
|
|
500
509
|
color: var(--shadow-claw-text-secondary);
|
|
501
510
|
font-size: var(--shadow-claw-font-size-sm);
|
|
502
511
|
line-height: 1.4;
|
|
503
512
|
margin: 0;
|
|
504
513
|
}
|
|
505
514
|
|
|
506
|
-
.files__paste-actions
|
|
515
|
+
.files__paste-actions,
|
|
516
|
+
.files__upload-conflict-actions {
|
|
507
517
|
display: flex;
|
|
508
518
|
gap: 0.5rem;
|
|
509
519
|
justify-content: flex-end;
|
|
@@ -514,7 +524,10 @@
|
|
|
514
524
|
.files__paste-continue,
|
|
515
525
|
.files__paste-apply,
|
|
516
526
|
.files__paste-ok,
|
|
517
|
-
.files__paste-overwrite
|
|
527
|
+
.files__paste-overwrite,
|
|
528
|
+
.files__upload-conflict-cancel,
|
|
529
|
+
.files__upload-conflict-ok,
|
|
530
|
+
.files__upload-conflict-overwrite {
|
|
518
531
|
background-color: transparent;
|
|
519
532
|
border: 0.0625rem solid var(--shadow-claw-border-color);
|
|
520
533
|
border-radius: var(--shadow-claw-radius-pill);
|
|
@@ -528,13 +541,16 @@
|
|
|
528
541
|
transition: all 0.15s ease;
|
|
529
542
|
}
|
|
530
543
|
|
|
531
|
-
.files__paste-overwrite
|
|
544
|
+
.files__paste-overwrite,
|
|
545
|
+
.files__upload-conflict-overwrite {
|
|
532
546
|
border-color: var(--shadow-claw-error-color);
|
|
533
547
|
color: var(--shadow-claw-error-color);
|
|
534
548
|
}
|
|
535
549
|
|
|
536
550
|
.files__paste-overwrite:hover,
|
|
537
|
-
.files__paste-overwrite:focus-visible
|
|
551
|
+
.files__paste-overwrite:focus-visible,
|
|
552
|
+
.files__upload-conflict-overwrite:hover,
|
|
553
|
+
.files__upload-conflict-overwrite:focus-visible {
|
|
538
554
|
background-color: var(--shadow-claw-error-color);
|
|
539
555
|
color: var(--shadow-claw-on-error, white);
|
|
540
556
|
outline: none;
|
|
@@ -542,7 +558,8 @@
|
|
|
542
558
|
|
|
543
559
|
.files__paste-continue,
|
|
544
560
|
.files__paste-apply,
|
|
545
|
-
.files__paste-ok
|
|
561
|
+
.files__paste-ok,
|
|
562
|
+
.files__upload-conflict-ok {
|
|
546
563
|
background-color: var(--shadow-claw-text-primary);
|
|
547
564
|
border-color: var(--shadow-claw-text-primary);
|
|
548
565
|
color: var(--shadow-claw-bg-primary);
|
|
@@ -553,7 +570,9 @@
|
|
|
553
570
|
.files__paste-apply:hover,
|
|
554
571
|
.files__paste-apply:focus-visible,
|
|
555
572
|
.files__paste-ok:hover,
|
|
556
|
-
.files__paste-ok:focus-visible
|
|
573
|
+
.files__paste-ok:focus-visible,
|
|
574
|
+
.files__upload-conflict-ok:hover,
|
|
575
|
+
.files__upload-conflict-ok:focus-visible {
|
|
557
576
|
background-color: var(--shadow-claw-accent-primary);
|
|
558
577
|
border-color: var(--shadow-claw-accent-primary);
|
|
559
578
|
color: var(--shadow-claw-on-primary);
|
|
@@ -561,7 +580,9 @@
|
|
|
561
580
|
}
|
|
562
581
|
|
|
563
582
|
.files__paste-cancel:hover,
|
|
564
|
-
.files__paste-cancel:focus-visible
|
|
583
|
+
.files__paste-cancel:focus-visible,
|
|
584
|
+
.files__upload-conflict-cancel:hover,
|
|
585
|
+
.files__upload-conflict-cancel:focus-visible {
|
|
565
586
|
border-color: var(--shadow-claw-text-primary);
|
|
566
587
|
box-shadow: var(--shadow-claw-shadow-md);
|
|
567
588
|
outline: none;
|
|
@@ -570,7 +591,10 @@
|
|
|
570
591
|
.files__paste-cancel:disabled,
|
|
571
592
|
.files__paste-continue:disabled,
|
|
572
593
|
.files__paste-apply:disabled,
|
|
573
|
-
.files__paste-ok:disabled
|
|
594
|
+
.files__paste-ok:disabled,
|
|
595
|
+
.files__upload-conflict-cancel:disabled,
|
|
596
|
+
.files__upload-conflict-ok:disabled,
|
|
597
|
+
.files__upload-conflict-overwrite:disabled {
|
|
574
598
|
cursor: not-allowed;
|
|
575
599
|
opacity: 0.6;
|
|
576
600
|
}
|
|
@@ -172,6 +172,41 @@
|
|
|
172
172
|
</div>
|
|
173
173
|
</form>
|
|
174
174
|
</shadow-claw-dialog>
|
|
175
|
+
<shadow-claw-dialog
|
|
176
|
+
dialog-class="files__upload-conflict-dialog"
|
|
177
|
+
dialog-aria-label="File upload conflict"
|
|
178
|
+
>
|
|
179
|
+
<form class="files__upload-conflict-form" method="dialog">
|
|
180
|
+
<h3 class="files__upload-conflict-title">File Already Exists</h3>
|
|
181
|
+
<p class="files__upload-conflict-message"></p>
|
|
182
|
+
<div class="files__upload-conflict-rename-container">
|
|
183
|
+
<label
|
|
184
|
+
class="files__upload-conflict-rename-label"
|
|
185
|
+
for="files-upload-conflict-name"
|
|
186
|
+
>New Name</label
|
|
187
|
+
>
|
|
188
|
+
<input
|
|
189
|
+
class="files__upload-conflict-rename-input"
|
|
190
|
+
id="files-upload-conflict-name"
|
|
191
|
+
name="files-upload-conflict-name"
|
|
192
|
+
type="text"
|
|
193
|
+
autocomplete="off"
|
|
194
|
+
required
|
|
195
|
+
/>
|
|
196
|
+
</div>
|
|
197
|
+
<div class="files__upload-conflict-actions">
|
|
198
|
+
<button class="files__upload-conflict-cancel" type="button">
|
|
199
|
+
Cancel
|
|
200
|
+
</button>
|
|
201
|
+
<button class="files__upload-conflict-overwrite" type="button">
|
|
202
|
+
Overwrite
|
|
203
|
+
</button>
|
|
204
|
+
<button class="files__upload-conflict-ok" type="submit">
|
|
205
|
+
Rename
|
|
206
|
+
</button>
|
|
207
|
+
</div>
|
|
208
|
+
</form>
|
|
209
|
+
</shadow-claw-dialog>
|
|
175
210
|
<div class="files__content">
|
|
176
211
|
<div class="files__terminal-slot" data-terminal-slot hidden></div>
|
|
177
212
|
<p class="files__drop-hint">Drop files here to upload</p>
|