synabun 2.0.5 → 2.0.9
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/CHANGELOG.md +78 -0
- package/README.md +2 -1
- package/THIRD-PARTY-LICENSES.md +13 -0
- package/docs/whatsapp.md +151 -16
- package/hooks/claude-code/post-plan.mjs +6 -30
- package/hooks/claude-code/session-start.mjs +1 -4
- package/hooks/claude-code/shared.mjs +112 -75
- package/lib/child-path.js +303 -0
- package/lib/claude-hooks.js +2 -1
- package/lib/data-home-migration.js +2 -0
- package/lib/desktop-app.js +265 -0
- package/lib/external-tools.js +7 -3
- package/lib/log-redact.js +102 -0
- package/lib/package-processes.js +670 -0
- package/lib/packaged-runtime.js +138 -11
- package/lib/runtime-dependencies.js +225 -0
- package/mcp-server/dist/http.d.ts +3 -1
- package/mcp-server/dist/http.d.ts.map +1 -1
- package/mcp-server/dist/http.js +15 -4
- package/mcp-server/dist/http.js.map +1 -1
- package/mcp-server/dist/index.d.ts.map +1 -1
- package/mcp-server/dist/index.js +9 -6
- package/mcp-server/dist/index.js.map +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts +1 -1
- package/mcp-server/dist/services/browser-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/browser-bench.js +1 -1
- package/mcp-server/dist/services/browser-bench.js.map +1 -1
- package/mcp-server/dist/services/budget-judgments.d.ts +108 -0
- package/mcp-server/dist/services/budget-judgments.d.ts.map +1 -0
- package/mcp-server/dist/services/budget-judgments.js +153 -0
- package/mcp-server/dist/services/budget-judgments.js.map +1 -0
- package/mcp-server/dist/services/categories.d.ts +35 -2
- package/mcp-server/dist/services/categories.d.ts.map +1 -1
- package/mcp-server/dist/services/categories.js +77 -33
- package/mcp-server/dist/services/categories.js.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts +5 -1
- package/mcp-server/dist/services/codex-config-heal.d.ts.map +1 -1
- package/mcp-server/dist/services/codex-config-heal.js +41 -4
- package/mcp-server/dist/services/codex-config-heal.js.map +1 -1
- package/mcp-server/dist/services/desktop-bench.d.ts +7 -7
- package/mcp-server/dist/services/desktop-bench.d.ts.map +1 -1
- package/mcp-server/dist/services/desktop-bench.js +1 -1
- package/mcp-server/dist/services/desktop-bench.js.map +1 -1
- package/mcp-server/dist/services/identity.d.ts +11 -0
- package/mcp-server/dist/services/identity.d.ts.map +1 -1
- package/mcp-server/dist/services/identity.js +24 -0
- package/mcp-server/dist/services/identity.js.map +1 -1
- package/mcp-server/dist/services/neural-interface.d.ts +13 -0
- package/mcp-server/dist/services/neural-interface.d.ts.map +1 -1
- package/mcp-server/dist/services/neural-interface.js +34 -2
- package/mcp-server/dist/services/neural-interface.js.map +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts +1 -1
- package/mcp-server/dist/services/typesafe-config.d.ts.map +1 -1
- package/mcp-server/dist/services/typesafe-config.js +4 -1
- package/mcp-server/dist/services/typesafe-config.js.map +1 -1
- package/mcp-server/dist/tools/agents.d.ts +11 -8
- package/mcp-server/dist/tools/agents.d.ts.map +1 -1
- package/mcp-server/dist/tools/agents.js +35 -14
- package/mcp-server/dist/tools/agents.js.map +1 -1
- package/mcp-server/dist/tools/bluesky.js +1 -1
- package/mcp-server/dist/tools/bluesky.js.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts +1 -1
- package/mcp-server/dist/tools/browser-advanced.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-advanced.js +1 -1
- package/mcp-server/dist/tools/browser-advanced.js.map +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts +1 -1
- package/mcp-server/dist/tools/browser-batch.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-batch.js +1 -1
- package/mcp-server/dist/tools/browser-batch.js.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js +1 -1
- package/mcp-server/dist/tools/browser-cheatsheet.js.map +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts +1 -1
- package/mcp-server/dist/tools/browser-interact.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-interact.js +1 -1
- package/mcp-server/dist/tools/browser-interact.js.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts +1 -1
- package/mcp-server/dist/tools/browser-navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-navigate.js +1 -1
- package/mcp-server/dist/tools/browser-navigate.js.map +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts +1 -1
- package/mcp-server/dist/tools/browser-observe.d.ts.map +1 -1
- package/mcp-server/dist/tools/browser-observe.js +1 -1
- package/mcp-server/dist/tools/browser-observe.js.map +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts +1 -1
- package/mcp-server/dist/tools/card-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/card-tools.js +1 -1
- package/mcp-server/dist/tools/card-tools.js.map +1 -1
- package/mcp-server/dist/tools/category.d.ts +1 -1
- package/mcp-server/dist/tools/category.d.ts.map +1 -1
- package/mcp-server/dist/tools/category.js +5 -1
- package/mcp-server/dist/tools/category.js.map +1 -1
- package/mcp-server/dist/tools/choice.d.ts +1 -1
- package/mcp-server/dist/tools/choice.d.ts.map +1 -1
- package/mcp-server/dist/tools/choice.js +1 -1
- package/mcp-server/dist/tools/choice.js.map +1 -1
- package/mcp-server/dist/tools/computer.d.ts +1 -1
- package/mcp-server/dist/tools/computer.d.ts.map +1 -1
- package/mcp-server/dist/tools/computer.js +1 -1
- package/mcp-server/dist/tools/computer.js.map +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts +1 -1
- package/mcp-server/dist/tools/discord-channel.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-channel.js +1 -1
- package/mcp-server/dist/tools/discord-channel.js.map +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts +1 -1
- package/mcp-server/dist/tools/discord-guild.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-guild.js +1 -1
- package/mcp-server/dist/tools/discord-guild.js.map +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts +1 -1
- package/mcp-server/dist/tools/discord-member.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-member.js +1 -1
- package/mcp-server/dist/tools/discord-member.js.map +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts +1 -1
- package/mcp-server/dist/tools/discord-message.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-message.js +1 -1
- package/mcp-server/dist/tools/discord-message.js.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts +1 -1
- package/mcp-server/dist/tools/discord-onboarding.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js +1 -1
- package/mcp-server/dist/tools/discord-onboarding.js.map +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts +1 -1
- package/mcp-server/dist/tools/discord-role.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-role.js +1 -1
- package/mcp-server/dist/tools/discord-role.js.map +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts +1 -1
- package/mcp-server/dist/tools/discord-thread.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-thread.js +1 -1
- package/mcp-server/dist/tools/discord-thread.js.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts +1 -1
- package/mcp-server/dist/tools/discord-webhook.d.ts.map +1 -1
- package/mcp-server/dist/tools/discord-webhook.js +1 -1
- package/mcp-server/dist/tools/discord-webhook.js.map +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts +1 -1
- package/mcp-server/dist/tools/fb-groups.d.ts.map +1 -1
- package/mcp-server/dist/tools/fb-groups.js +1 -1
- package/mcp-server/dist/tools/fb-groups.js.map +1 -1
- package/mcp-server/dist/tools/forget.d.ts +1 -1
- package/mcp-server/dist/tools/forget.d.ts.map +1 -1
- package/mcp-server/dist/tools/forget.js +1 -1
- package/mcp-server/dist/tools/forget.js.map +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts +1 -1
- package/mcp-server/dist/tools/git-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/git-tools.js +1 -1
- package/mcp-server/dist/tools/git-tools.js.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/experience.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/experience.js +1 -1
- package/mcp-server/dist/tools/gsc/experience.js.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/indexing.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js +1 -1
- package/mcp-server/dist/tools/gsc/indexing.js.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/inspect.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js +1 -1
- package/mcp-server/dist/tools/gsc/inspect.js.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js +1 -1
- package/mcp-server/dist/tools/gsc/navigate.js.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/performance.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/performance.js +1 -1
- package/mcp-server/dist/tools/gsc/performance.js.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts +1 -1
- package/mcp-server/dist/tools/gsc/settings.d.ts.map +1 -1
- package/mcp-server/dist/tools/gsc/settings.js +1 -1
- package/mcp-server/dist/tools/gsc/settings.js.map +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts +1 -1
- package/mcp-server/dist/tools/image-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/image-tools.js +1 -1
- package/mcp-server/dist/tools/image-tools.js.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js +1 -1
- package/mcp-server/dist/tools/leonardo-browser-tools.js.map +1 -1
- package/mcp-server/dist/tools/loop.d.ts +1 -1
- package/mcp-server/dist/tools/loop.d.ts.map +1 -1
- package/mcp-server/dist/tools/loop.js +1 -1
- package/mcp-server/dist/tools/loop.js.map +1 -1
- package/mcp-server/dist/tools/memories.d.ts +1 -1
- package/mcp-server/dist/tools/memories.d.ts.map +1 -1
- package/mcp-server/dist/tools/memories.js +1 -1
- package/mcp-server/dist/tools/memories.js.map +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts +1 -1
- package/mcp-server/dist/tools/morelogin.d.ts.map +1 -1
- package/mcp-server/dist/tools/morelogin.js +1 -1
- package/mcp-server/dist/tools/morelogin.js.map +1 -1
- package/mcp-server/dist/tools/profile.d.ts +1 -1
- package/mcp-server/dist/tools/profile.d.ts.map +1 -1
- package/mcp-server/dist/tools/profile.js +1 -1
- package/mcp-server/dist/tools/profile.js.map +1 -1
- package/mcp-server/dist/tools/recall.d.ts +1 -1
- package/mcp-server/dist/tools/recall.d.ts.map +1 -1
- package/mcp-server/dist/tools/recall.js +1 -1
- package/mcp-server/dist/tools/recall.js.map +1 -1
- package/mcp-server/dist/tools/reflect.d.ts +1 -1
- package/mcp-server/dist/tools/reflect.d.ts.map +1 -1
- package/mcp-server/dist/tools/reflect.js +1 -1
- package/mcp-server/dist/tools/reflect.js.map +1 -1
- package/mcp-server/dist/tools/remember.d.ts +1 -1
- package/mcp-server/dist/tools/remember.d.ts.map +1 -1
- package/mcp-server/dist/tools/remember.js +13 -11
- package/mcp-server/dist/tools/remember.js.map +1 -1
- package/mcp-server/dist/tools/restore.d.ts +1 -1
- package/mcp-server/dist/tools/restore.d.ts.map +1 -1
- package/mcp-server/dist/tools/restore.js +1 -1
- package/mcp-server/dist/tools/restore.js.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts +1 -1
- package/mcp-server/dist/tools/style-guide-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js +1 -1
- package/mcp-server/dist/tools/style-guide-tools.js.map +1 -1
- package/mcp-server/dist/tools/sync.d.ts +1 -1
- package/mcp-server/dist/tools/sync.d.ts.map +1 -1
- package/mcp-server/dist/tools/sync.js +1 -1
- package/mcp-server/dist/tools/sync.js.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js +1 -1
- package/mcp-server/dist/tools/tictactoe-tools.js.map +1 -1
- package/mcp-server/dist/tools/utils.d.ts +1 -1
- package/mcp-server/dist/tools/utils.d.ts.map +1 -1
- package/mcp-server/dist/tools/utils.js +1 -1
- package/mcp-server/dist/tools/utils.js.map +1 -1
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts +17 -0
- package/mcp-server/dist/tools/whatsapp-send-file.d.ts.map +1 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js +44 -0
- package/mcp-server/dist/tools/whatsapp-send-file.js.map +1 -0
- package/mcp-server/dist/tools/whiteboard-tools.d.ts +3 -3
- package/mcp-server/dist/tools/whiteboard-tools.d.ts.map +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js +1 -1
- package/mcp-server/dist/tools/whiteboard-tools.js.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/analytics.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js +1 -1
- package/mcp-server/dist/tools/youtube/analytics.js.map +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/download.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/download.js +1 -1
- package/mcp-server/dist/tools/youtube/download.js.map +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/media.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/media.js +1 -1
- package/mcp-server/dist/tools/youtube/media.js.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/metadata.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js +1 -1
- package/mcp-server/dist/tools/youtube/metadata.js.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/navigate.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js +1 -1
- package/mcp-server/dist/tools/youtube/navigate.js.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/playlist.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js +1 -1
- package/mcp-server/dist/tools/youtube/playlist.js.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/schedule.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js +1 -1
- package/mcp-server/dist/tools/youtube/schedule.js.map +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/source.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/source.js +1 -1
- package/mcp-server/dist/tools/youtube/source.js.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts +1 -1
- package/mcp-server/dist/tools/youtube/upload.d.ts.map +1 -1
- package/mcp-server/dist/tools/youtube/upload.js +4 -9
- package/mcp-server/dist/tools/youtube/upload.js.map +1 -1
- package/mcp-server/dist/tui.js +18 -5
- package/mcp-server/dist/tui.js.map +1 -1
- package/mcp-server/dist/version.d.ts +5 -0
- package/mcp-server/dist/version.d.ts.map +1 -0
- package/mcp-server/dist/version.js +21 -0
- package/mcp-server/dist/version.js.map +1 -0
- package/mcp-server/package-lock.json +3 -3
- package/mcp-server/package.json +3 -3
- package/mcp-server/run.mjs +15 -9
- package/mcp-server/src/http.ts +18 -3
- package/mcp-server/src/index.ts +9 -6
- package/mcp-server/src/services/browser-bench.ts +1 -1
- package/mcp-server/src/services/budget-judgments.ts +183 -0
- package/mcp-server/src/services/categories.ts +77 -32
- package/mcp-server/src/services/codex-config-heal.ts +35 -4
- package/mcp-server/src/services/desktop-bench.ts +1 -1
- package/mcp-server/src/services/identity.ts +27 -0
- package/mcp-server/src/services/neural-interface.ts +34 -2
- package/mcp-server/src/services/typesafe-config.ts +4 -1
- package/mcp-server/src/tools/agents.ts +34 -15
- package/mcp-server/src/tools/bluesky.ts +1 -1
- package/mcp-server/src/tools/browser-advanced.ts +1 -1
- package/mcp-server/src/tools/browser-batch.ts +1 -1
- package/mcp-server/src/tools/browser-cheatsheet.ts +1 -1
- package/mcp-server/src/tools/browser-interact.ts +1 -1
- package/mcp-server/src/tools/browser-navigate.ts +1 -1
- package/mcp-server/src/tools/browser-observe.ts +1 -1
- package/mcp-server/src/tools/card-tools.ts +1 -1
- package/mcp-server/src/tools/category.ts +6 -1
- package/mcp-server/src/tools/choice.ts +1 -1
- package/mcp-server/src/tools/computer.ts +1 -1
- package/mcp-server/src/tools/discord-channel.ts +1 -1
- package/mcp-server/src/tools/discord-guild.ts +1 -1
- package/mcp-server/src/tools/discord-member.ts +1 -1
- package/mcp-server/src/tools/discord-message.ts +1 -1
- package/mcp-server/src/tools/discord-onboarding.ts +1 -1
- package/mcp-server/src/tools/discord-role.ts +1 -1
- package/mcp-server/src/tools/discord-thread.ts +1 -1
- package/mcp-server/src/tools/discord-webhook.ts +1 -1
- package/mcp-server/src/tools/fb-groups.ts +1 -1
- package/mcp-server/src/tools/forget.ts +1 -1
- package/mcp-server/src/tools/git-tools.ts +1 -1
- package/mcp-server/src/tools/gsc/experience.ts +1 -1
- package/mcp-server/src/tools/gsc/indexing.ts +1 -1
- package/mcp-server/src/tools/gsc/inspect.ts +1 -1
- package/mcp-server/src/tools/gsc/navigate.ts +1 -1
- package/mcp-server/src/tools/gsc/performance.ts +1 -1
- package/mcp-server/src/tools/gsc/settings.ts +1 -1
- package/mcp-server/src/tools/image-tools.ts +1 -1
- package/mcp-server/src/tools/leonardo-browser-tools.ts +1 -1
- package/mcp-server/src/tools/loop.ts +1 -1
- package/mcp-server/src/tools/memories.ts +1 -1
- package/mcp-server/src/tools/morelogin.ts +1 -1
- package/mcp-server/src/tools/profile.ts +1 -1
- package/mcp-server/src/tools/recall.ts +1 -1
- package/mcp-server/src/tools/reflect.ts +1 -1
- package/mcp-server/src/tools/remember.ts +13 -12
- package/mcp-server/src/tools/restore.ts +1 -1
- package/mcp-server/src/tools/style-guide-tools.ts +1 -1
- package/mcp-server/src/tools/sync.ts +1 -1
- package/mcp-server/src/tools/tictactoe-tools.ts +1 -1
- package/mcp-server/src/tools/utils.ts +1 -1
- package/mcp-server/src/tools/whatsapp-send-file.ts +51 -0
- package/mcp-server/src/tools/whiteboard-tools.ts +1 -1
- package/mcp-server/src/tools/youtube/analytics.ts +1 -1
- package/mcp-server/src/tools/youtube/download.ts +1 -1
- package/mcp-server/src/tools/youtube/media.ts +1 -1
- package/mcp-server/src/tools/youtube/metadata.ts +1 -1
- package/mcp-server/src/tools/youtube/navigate.ts +1 -1
- package/mcp-server/src/tools/youtube/playlist.ts +1 -1
- package/mcp-server/src/tools/youtube/schedule.ts +1 -1
- package/mcp-server/src/tools/youtube/source.ts +1 -1
- package/mcp-server/src/tools/youtube/upload.ts +4 -12
- package/mcp-server/src/tui.ts +24 -10
- package/mcp-server/src/version.ts +22 -0
- package/neural-interface/i18n/en.json +645 -57
- package/neural-interface/i18n/pt-BR.json +644 -56
- package/neural-interface/lib/account-usage.js +163 -0
- package/neural-interface/lib/agent-sandbox-elevation.js +102 -0
- package/neural-interface/lib/agent-sandbox-setting.js +57 -0
- package/neural-interface/lib/assistant-api.js +274 -52
- package/neural-interface/lib/assistant-brains/claude.js +15 -2
- package/neural-interface/lib/assistant-brains/codex.js +3 -0
- package/neural-interface/lib/assistant-brains/opencode.js +17 -4
- package/neural-interface/lib/assistant-budget-planner.js +390 -0
- package/neural-interface/lib/assistant-budget-split.js +408 -0
- package/neural-interface/lib/assistant-budget.js +94 -95
- package/neural-interface/lib/assistant-config.js +62 -14
- package/neural-interface/lib/assistant-dispatch.js +432 -125
- package/neural-interface/lib/assistant-jev.js +26 -3
- package/neural-interface/lib/assistant-persona.js +68 -11
- package/neural-interface/lib/assistant-plan-permissions.js +8 -1
- package/neural-interface/lib/assistant-route-accounts.js +14 -0
- package/neural-interface/lib/assistant-route-gate-codex.js +3 -1
- package/neural-interface/lib/assistant-route-gate.js +19 -3
- package/neural-interface/lib/assistant-router.js +373 -42
- package/neural-interface/lib/assistant-runtime.js +312 -48
- package/neural-interface/lib/assistant-user-writer.js +4 -2
- package/neural-interface/lib/augmented-path.js +317 -53
- package/neural-interface/lib/claude-accounts.js +342 -31
- package/neural-interface/lib/claude-agent-bridge.js +90 -3
- package/neural-interface/lib/claude-executable.js +6 -1
- package/neural-interface/lib/claude-model-catalog.js +3 -3
- package/neural-interface/lib/claude-panel-auth.js +221 -0
- package/neural-interface/lib/claude-profile-api-key.js +62 -0
- package/neural-interface/lib/cli-install-api.js +137 -0
- package/neural-interface/lib/cli-install.js +797 -0
- package/neural-interface/lib/cli-path.js +130 -0
- package/neural-interface/lib/cli-resolver.js +744 -0
- package/neural-interface/lib/cli-update-planner.js +143 -7
- package/neural-interface/lib/codex-account-auth.js +180 -0
- package/neural-interface/lib/codex-accounts.js +502 -0
- package/neural-interface/lib/codex-capabilities.js +2 -1
- package/neural-interface/lib/codex-compat-rpc.js +8 -2
- package/neural-interface/lib/codex-mcp-approvals.js +5 -1
- package/neural-interface/lib/codex-panel-auth.js +62 -0
- package/neural-interface/lib/codex-session-settings.js +1 -1
- package/neural-interface/lib/codex-settings-schema.js +122 -0
- package/neural-interface/lib/desktop/config.js +3 -0
- package/neural-interface/lib/exec-loop-command.js +3 -1
- package/neural-interface/lib/git-bash/bzip2.js +339 -0
- package/neural-interface/lib/git-bash/claude-settings.js +73 -0
- package/neural-interface/lib/git-bash/extract-worker.js +34 -0
- package/neural-interface/lib/git-bash/install.js +529 -0
- package/neural-interface/lib/git-bash/licenses/COPYING +360 -0
- package/neural-interface/lib/git-bash/licenses/LGPL-2.1 +511 -0
- package/neural-interface/lib/git-bash/manager.js +268 -0
- package/neural-interface/lib/git-bash/pins.js +206 -0
- package/neural-interface/lib/git-bash/pins.json +34 -0
- package/neural-interface/lib/git-bash/resolver.js +194 -0
- package/neural-interface/lib/git-read.js +34 -2
- package/neural-interface/lib/install-details.js +163 -0
- package/neural-interface/lib/mcp-installer.js +14 -2
- package/neural-interface/lib/native-loop-providers.js +22 -4
- package/neural-interface/lib/native-loop-runtime.js +9 -4
- package/neural-interface/lib/os-path.js +319 -0
- package/neural-interface/lib/provider-panel-api-key-auth.js +92 -0
- package/neural-interface/lib/provider-panel-terminal-auth.js +96 -0
- package/neural-interface/lib/pty-host/core.js +2 -1
- package/neural-interface/lib/pty-host/manager.js +27 -2
- package/neural-interface/lib/remote-policy.js +20 -11
- package/neural-interface/lib/session-indexer.js +3 -1
- package/neural-interface/lib/session-title-generator.js +6 -3
- package/neural-interface/lib/system-about.js +340 -0
- package/neural-interface/lib/system-activity.js +66 -0
- package/neural-interface/lib/terminal-env.js +11 -3
- package/neural-interface/lib/terminal-path.js +55 -0
- package/neural-interface/lib/virtual-socket.js +12 -1
- package/neural-interface/lib/whatsapp/baileys-adapter.js +165 -45
- package/neural-interface/lib/whatsapp/bridge.js +78 -34
- package/neural-interface/lib/whatsapp/commands.js +13 -14
- package/neural-interface/lib/whatsapp/config.js +134 -20
- package/neural-interface/lib/whatsapp/fake-baileys.js +99 -14
- package/neural-interface/lib/whatsapp/host-core.js +168 -35
- package/neural-interface/lib/whatsapp/inbound.js +5 -3
- package/neural-interface/lib/whatsapp/local-file.js +524 -0
- package/neural-interface/lib/whatsapp/manager.js +92 -32
- package/neural-interface/lib/whatsapp/paths.js +11 -1
- package/neural-interface/lib/whatsapp/protocol.js +65 -5
- package/neural-interface/lib/whatsapp/redact.js +63 -0
- package/neural-interface/lib/whatsapp/service.js +343 -91
- package/neural-interface/lib/whatsapp/transcription.js +463 -0
- package/neural-interface/package-lock.json +2 -2
- package/neural-interface/package.json +1 -1
- package/neural-interface/public/index.html +2 -0
- package/neural-interface/public/index2d.html +2 -0
- package/neural-interface/public/offline.html +16 -1
- package/neural-interface/public/onboarding.html +96 -2
- package/neural-interface/public/shared/account-i18n.js +7 -0
- package/neural-interface/public/shared/account-menu.js +542 -0
- package/neural-interface/public/shared/account-usage.js +79 -0
- package/neural-interface/public/shared/account-view.js +336 -0
- package/neural-interface/public/shared/agent-runtime-options.js +28 -8
- package/neural-interface/public/shared/assistant/asst-account-menu.js +298 -0
- package/neural-interface/public/shared/assistant/asst-accounts.js +182 -184
- package/neural-interface/public/shared/assistant/asst-api.js +39 -2
- package/neural-interface/public/shared/assistant/asst-brain-picker.js +288 -32
- package/neural-interface/public/shared/assistant/asst-budget.js +259 -303
- package/neural-interface/public/shared/assistant/asst-mascot.js +126 -7
- package/neural-interface/public/shared/assistant/asst-menu.js +219 -80
- package/neural-interface/public/shared/assistant/asst-model-chip.js +135 -0
- package/neural-interface/public/shared/assistant/asst-panel.js +87 -16
- package/neural-interface/public/shared/assistant/asst-render.js +38 -7
- package/neural-interface/public/shared/assistant/asst-route.js +72 -14
- package/neural-interface/public/shared/assistant/asst-routes-editor.js +45 -6
- package/neural-interface/public/shared/assistant/asst-slash.js +1 -1
- package/neural-interface/public/shared/assistant/asst-state.js +1 -1
- package/neural-interface/public/shared/assistant/asst-styles.js +160 -29
- package/neural-interface/public/shared/cdx/cdx-commands.js +4 -1
- package/neural-interface/public/shared/cdx/cdx-features.js +22 -14
- package/neural-interface/public/shared/cdx/cdx-icons.js +2 -0
- package/neural-interface/public/shared/cdx/cdx-panel.js +49 -108
- package/neural-interface/public/shared/cdx/cdx-settings.js +231 -0
- package/neural-interface/public/shared/cdx/cdx-styles.js +172 -111
- package/neural-interface/public/shared/cdx/cdx-tabs.js +264 -664
- package/neural-interface/public/shared/cdx/cdx-ws.js +2 -0
- package/neural-interface/public/shared/claude-account-view.js +3 -0
- package/neural-interface/public/shared/cli-install-view.js +392 -0
- package/neural-interface/public/shared/cli-status.js +282 -22
- package/neural-interface/public/shared/cp/cp-events.js +2 -1
- package/neural-interface/public/shared/cp/cp-styles.js +46 -0
- package/neural-interface/public/shared/desktop-bridge.js +39 -0
- package/neural-interface/public/shared/desktop-titlebar-page.js +59 -0
- package/neural-interface/public/shared/desktop-titlebar-view.js +122 -0
- package/neural-interface/public/shared/desktop-titlebar.js +153 -0
- package/neural-interface/public/shared/desktop-update-view.js +462 -0
- package/neural-interface/public/shared/editor/editor-panel.js +1 -1
- package/neural-interface/public/shared/explorer/fx-main.js +1 -1
- package/neural-interface/public/shared/git-bash-view.js +145 -0
- package/neural-interface/public/shared/html-shell.js +1 -0
- package/neural-interface/public/shared/i18n.js +19 -1
- package/neural-interface/public/shared/install-details-view.js +86 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-childpanel.js +4 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-panel.js +36 -1
- package/neural-interface/public/shared/ocp-v2/ocp-v2-send.js +17 -0
- package/neural-interface/public/shared/ocp-v2/ocp-v2-styles.js +169 -2
- package/neural-interface/public/shared/pwa-install-page.js +3 -1
- package/neural-interface/public/shared/settings/settings-about-pane.js +271 -0
- package/neural-interface/public/shared/settings/settings-about.js +254 -0
- package/neural-interface/public/shared/settings/settings-ia.js +15 -0
- package/neural-interface/public/shared/showcase-privacy.js +490 -0
- package/neural-interface/public/shared/storage.js +30 -3
- package/neural-interface/public/shared/styles.css +281 -32
- package/neural-interface/public/shared/synabun-mascot.js +48 -2
- package/neural-interface/public/shared/ui-claude-panel.js +262 -217
- package/neural-interface/public/shared/ui-cli-banner.js +355 -0
- package/neural-interface/public/shared/ui-explorer.js +2 -0
- package/neural-interface/public/shared/ui-git-bash.js +124 -0
- package/neural-interface/public/shared/ui-judgments.js +10 -1
- package/neural-interface/public/shared/ui-loading.js +30 -6
- package/neural-interface/public/shared/ui-menubar.js +13 -3
- package/neural-interface/public/shared/ui-navbar.js +3 -1
- package/neural-interface/public/shared/ui-provider-auth.js +104 -0
- package/neural-interface/public/shared/ui-provider-terminal-auth.js +223 -0
- package/neural-interface/public/shared/ui-sessions.js +9 -3
- package/neural-interface/public/shared/ui-settings.js +55 -3
- package/neural-interface/public/shared/ui-sidepanel-sessions.js +3 -1
- package/neural-interface/public/shared/ui-sync.js +8 -0
- package/neural-interface/public/shared/ui-terminal.js +17 -0
- package/neural-interface/public/shared/ui-tool-updates.js +5 -0
- package/neural-interface/public/shared/ui-update.js +598 -7
- package/neural-interface/public/shared/ui-whatsapp.js +21 -6
- package/neural-interface/public/shared/whatsapp/wa-view.js +51 -8
- package/neural-interface/public/shared/worker-end.js +2 -0
- package/neural-interface/public/sw.js +1 -1
- package/neural-interface/public/variant/2d/main.js +10 -1
- package/neural-interface/public/variant/3d/main.js +10 -1
- package/neural-interface/scripts/rebuild-pty.js +14 -2
- package/neural-interface/server.js +1384 -679
- package/npm-shrinkwrap.json +4424 -0
- package/package.json +57 -2
- package/postinstall.js +61 -47
- package/preuninstall.js +8 -35
- package/setup.js +80 -47
- package/skills/synabun/modules/idea.md +1 -1
- package/skills/synabun/modules/schedule.md +1 -1
- package/updater.cmd +2 -2
- package/updater.mjs +97 -3
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
// ═══════════════════════════════════════════
|
|
2
|
+
// SynaBun Neural Interface — Showcase privacy
|
|
3
|
+
// ═══════════════════════════════════════════
|
|
4
|
+
//
|
|
5
|
+
// Loaded only in showcase mode (`?showcase`, storage.js isShowcaseMode) by the
|
|
6
|
+
// main entry points, for product screenshots: the feature stays sharp, the
|
|
7
|
+
// user's own content is blurred (a CSS filter; no text is removed or rewritten).
|
|
8
|
+
//
|
|
9
|
+
// - Wholesale, by selector: the surfaces that always hold the user's content
|
|
10
|
+
// (memory text, session titles, transcripts, terminal output, Resume results,
|
|
11
|
+
// account names, toast text). Pure CSS, so a node is blurred the moment it
|
|
12
|
+
// is drawn.
|
|
13
|
+
// - By what it says: any element whose own visible text, value or placeholder
|
|
14
|
+
// matches a private pattern (emails, home paths, ids, keys, phone numbers,
|
|
15
|
+
// the names of the user's other projects, their OS user name...). A
|
|
16
|
+
// debounced MutationObserver looks only at added or changed nodes.
|
|
17
|
+
// - Same-origin frames (detached side-panel sessions) get the same treatment.
|
|
18
|
+
//
|
|
19
|
+
// Canvas text cannot be blurred: the 3D map's labels (#map-labels) and the 2D
|
|
20
|
+
// map (#canvas-main) are drawn on canvases; blur("#map-labels") blurs all of
|
|
21
|
+
// them at once.
|
|
22
|
+
//
|
|
23
|
+
// window.__synabunShowcase = { scan, blur, unblur, stats }.
|
|
24
|
+
|
|
25
|
+
const BLUR_CLASS = 'sb-showcase-blur';
|
|
26
|
+
const MANUAL_CLASS = 'sb-showcase-manual';
|
|
27
|
+
const CLEAR_ATTR = 'data-sb-showcase-clear';
|
|
28
|
+
const STYLE_ID = 'sb-showcase-style';
|
|
29
|
+
const BLUR = 'blur(7px)';
|
|
30
|
+
const DEBOUNCE_MS = 120;
|
|
31
|
+
// Private text split over a few spans (a path in pieces): a parent holding at
|
|
32
|
+
// most this much text is read as one, up to this many levels up.
|
|
33
|
+
const SPAN_MAX = 160;
|
|
34
|
+
const SPAN_LEVELS = 3;
|
|
35
|
+
|
|
36
|
+
/** Surfaces that always hold the user's own content (class names from the shared UI modules). */
|
|
37
|
+
export const WHOLESALE_SELECTORS = [
|
|
38
|
+
// Memories: detail card, map hover title, Memory Explorer rows, bookmarks, trash
|
|
39
|
+
'.detail-card [data-role="content"]', '.detail-card .md-textarea', '.detail-card .tag-text',
|
|
40
|
+
'.detail-card [data-role="files-content"]', '.detail-card [data-role="meta-content"] .detail-meta-row > span:last-child',
|
|
41
|
+
'#tooltip-preview', '.explorer-memory .explorer-label', '.dropdown-item-title',
|
|
42
|
+
'.tw-item-preview', '.tw-detail-body', '.tw-detail-tags', '.tw-detail-file',
|
|
43
|
+
// Session pills, windows and tabs, with their titles
|
|
44
|
+
'.term-minimized-pill-label', '.sp-session-title', '.asp-title',
|
|
45
|
+
'.cp-session-label', '.cp-sess-prompt', '.cp-sess-meta',
|
|
46
|
+
'#cxp-session-label', '.cxp-sess-prompt', '.cxp-sess-meta',
|
|
47
|
+
'.ocpv2-session-label', '.ocpv2-session-item[data-tooltip] .ocpv2-session-item-label', '.ocpv2-session-item-meta', '.ocpv2-session-share-url',
|
|
48
|
+
'.term-tab-label', '.term-float-tab-title', '.peek-tab-label', '.sm-card-project', '.sm-card-val',
|
|
49
|
+
// Claude Code side panel transcript
|
|
50
|
+
'.msg-user .msg-bubble', '.msg-assistant .msg-body', '.msg-thinking-content', '.tool-detail', '.tool-section', '.tool-result-content',
|
|
51
|
+
'.cp-bash-cmd', '.cp-bash-desc', '.cp-bash-out', '.cp-queue-text', '.cp-todo-txt', '.cp-task-title',
|
|
52
|
+
// Codex side panel transcript
|
|
53
|
+
'.cxp-msg-user-text', '.cxp-msg-body', '.cxp-reasoning-body', '.cxp-think-detail',
|
|
54
|
+
'.cxp-card-subtitle', '.cxp-card-body', '.cxp-fold-body', '.cxp-queue-item-text',
|
|
55
|
+
// OpenCode side panel transcript
|
|
56
|
+
'.ocpv2-part-text', '.ocpv2-reasoning-body', '.ocpv2-tc-summary', '.ocpv2-tc-meta', '.ocpv2-tc-body',
|
|
57
|
+
'.ocpv2-tool-input', '.ocpv2-tool-output', '.ocpv2-sb-summary',
|
|
58
|
+
// Assistant transcript, run and worker cards
|
|
59
|
+
'.asst-msg-text', '.asst-md', '.asst-thinking-content', '.asst-run-title', '.asst-run-summary', '.asst-run-escalate-reason',
|
|
60
|
+
'.asst-tool-detail', '.asst-tool-body', '.asst-tool-section', '.asst-tl-said', '.asst-tl-subject', '.asst-bubble-text',
|
|
61
|
+
'.asst-station-sub', '.asst-station-log', '.asst-auto-title', '.asst-auto-summary', '.asst-auto-detail',
|
|
62
|
+
// Composers, once they hold text (an empty one keeps its placeholder sharp)
|
|
63
|
+
'#cp-input:not(:placeholder-shown)', '#cxp-input:not(:placeholder-shown)', '.ocpv2-compose-input:not(:placeholder-shown)',
|
|
64
|
+
'.asst-input:not(:placeholder-shown)',
|
|
65
|
+
// Terminal output (the WebGL canvas and the DOM renderer alike) and the terminal's browser tab
|
|
66
|
+
'.term-viewport .xterm-screen', '.term-float-tab-body .xterm', '.browser-canvas', '.browser-url-input', '.browser-title-label',
|
|
67
|
+
// Resume results
|
|
68
|
+
'.resume-session-prompt', '.resume-session-snippet', '.resume-continue-prompt', '.resume-project-name',
|
|
69
|
+
'.resume-project-badge', '.resume-branch',
|
|
70
|
+
// Account emails and names
|
|
71
|
+
'.acct-menu-trigger-name', '.acct-menu-item-name', '.acct-menu-item-id', '.acct-menu-avatar', '.acct-menu-note', '.acct-menu-v',
|
|
72
|
+
'.asst-account-label', '.asst-account-sub', '.asst-account-cred', '.as-launch-account-name',
|
|
73
|
+
'.asst-acct-name', '.asst-acct-line', '.asst-acct-avatar', '.asst-acct-value-text',
|
|
74
|
+
'.invite-key-box', '.invite-url-text', '.invite-key-status',
|
|
75
|
+
// Toast text
|
|
76
|
+
'.notif-toast-msg', '.term-toast', '#fe-toast', '.as-toast', '.ss-toast', '.cc-toast', '.asp-toast',
|
|
77
|
+
// Workspace names, generated image names
|
|
78
|
+
'.ws-row-name', '.ws-row-meta', '#ws-active-name', '.ig-card-name', '.ig-lb-name',
|
|
79
|
+
];
|
|
80
|
+
const WHOLESALE = WHOLESALE_SELECTORS.join(',');
|
|
81
|
+
|
|
82
|
+
// The shared tooltip element shows a memory or session title for some rows and
|
|
83
|
+
// a control's name for the rest: it is blurred when what it describes is private.
|
|
84
|
+
const TOOLTIP_TEXT = '.ui-tooltip-text';
|
|
85
|
+
|
|
86
|
+
// ── Patterns ──────────────────────────────────────────────────────
|
|
87
|
+
|
|
88
|
+
const PHONE_PATTERNS = [
|
|
89
|
+
/(?:^|[^\w+])\+\d{1,3}[\s.-]?\(?\d{1,4}\)?(?:[\s.-]?\d{2,5}){2,4}\b/, // +55 11 9####-####, +1 415 555 0100
|
|
90
|
+
/\(\d{2,3}\)\s?\d{4,5}[\s.-]\d{4}\b/, // (11) 91234-5678
|
|
91
|
+
/\b\d{2}\s9\d{4}[\s-]\d{4}\b/, // 11 91234-5678
|
|
92
|
+
/\b\d{3}[\s.-]\d{3}[\s.-]\d{4}\b/, // 415-555-0100
|
|
93
|
+
];
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Patterns that name a checkout's own private work stay out of the published code: a checkout
|
|
97
|
+
* may list them in showcase-private-patterns.json beside this file, as [reason, source, flags,
|
|
98
|
+
* ownOnly] rows. A release has none, and the page's one request for it answers 404.
|
|
99
|
+
*/
|
|
100
|
+
export function patternsFromRows(rows) {
|
|
101
|
+
return (Array.isArray(rows) ? rows : []).flatMap((row) => {
|
|
102
|
+
if (!Array.isArray(row) || typeof row[0] !== 'string' || typeof row[1] !== 'string') return [];
|
|
103
|
+
try { return [[row[0], new RegExp(row[1], typeof row[2] === 'string' ? row[2] : ''), row[3] === true]]; } catch { return []; }
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
const LOCAL_PATTERNS = typeof window === 'undefined' ? [] : await fetch(new URL('./showcase-private-patterns.json', import.meta.url))
|
|
107
|
+
.then((res) => (res.ok ? res.json() : []))
|
|
108
|
+
.then(patternsFromRows, () => []);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* What the matcher looks for, besides the names it is given: [reason, test, ownOnly].
|
|
112
|
+
* ownOnly tests are skipped on the joined text of a few spans, where labels and numbers
|
|
113
|
+
* that sit side by side would read as one long key or a phone number.
|
|
114
|
+
*/
|
|
115
|
+
export const PRIVATE_PATTERNS = [
|
|
116
|
+
['email', /[A-Za-z0-9._%+-]+@[A-Za-z0-9-]+(?:\.[A-Za-z0-9-]+)*\.[A-Za-z]{2,}/],
|
|
117
|
+
['home-path', /(?:^|[^\w.-])\/(?:Users|home)\/[^/\s]+|\b[A-Za-z]:\\Users\\[^\\\s]+/],
|
|
118
|
+
['uuid', /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/i],
|
|
119
|
+
['secret', /\b(?:sk|pk|rk)[-_][A-Za-z0-9_-]{12,}|\bgh[pousr]_[A-Za-z0-9]{16,}|\bgithub_pat_[A-Za-z0-9_]{20,}|\bxox[a-z]-[A-Za-z0-9-]{8,}|\bAIza[0-9A-Za-z_-]{20,}/],
|
|
120
|
+
['secret', (text) => (text.match(/[A-Za-z0-9+=]{24,}/g) || []).some((run) => (run.match(/\d/g) || []).length >= 3 && (run.match(/[A-Za-z]/g) || []).length >= 3), true],
|
|
121
|
+
['phone', (text) => PHONE_PATTERNS.some((re) => re.test(text)), true],
|
|
122
|
+
...LOCAL_PATTERNS,
|
|
123
|
+
];
|
|
124
|
+
|
|
125
|
+
const SELF_PROJECT = 'synabun';
|
|
126
|
+
const GENERIC_USER_NAMES = new Set(['user', 'users', 'admin', 'root', 'shared', 'home', 'guest', 'test', 'public']);
|
|
127
|
+
const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
128
|
+
const squash = (s) => String(s || '').toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
129
|
+
|
|
130
|
+
/** A path's last folder name ('/a/b/' → 'b', 'C:\\x\\y' → 'y'). */
|
|
131
|
+
function baseName(path) {
|
|
132
|
+
const parts = String(path || '').split(/[\\/]+/).filter(Boolean);
|
|
133
|
+
return parts.length ? parts[parts.length - 1] : '';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* The names a registered-project list gives away: every project's label and folder
|
|
138
|
+
* name except SynaBun's own, and the OS user name read from the home folder in the paths.
|
|
139
|
+
* @param {Array<{path?: string, label?: string}>} projects GET /api/projects → projects
|
|
140
|
+
*/
|
|
141
|
+
export function namesFromProjects(projects = []) {
|
|
142
|
+
const projectNames = new Set();
|
|
143
|
+
const userNames = new Set();
|
|
144
|
+
for (const p of Array.isArray(projects) ? projects : []) {
|
|
145
|
+
const path = String(p?.path || '');
|
|
146
|
+
const label = String(p?.label || '');
|
|
147
|
+
for (const name of [/[\\/]/.test(label) ? baseName(label) : label, baseName(path)]) {
|
|
148
|
+
const key = squash(name);
|
|
149
|
+
if (key.length >= 4 && key !== SELF_PROJECT) projectNames.add(name.trim());
|
|
150
|
+
}
|
|
151
|
+
const home = path.match(/^\/(?:Users|home)\/([^/]+)/) || path.match(/^[A-Za-z]:\\Users\\([^\\]+)/i);
|
|
152
|
+
if (home && home[1].length >= 3 && !GENERIC_USER_NAMES.has(home[1].toLowerCase())) userNames.add(home[1]);
|
|
153
|
+
}
|
|
154
|
+
return { projectNames: [...projectNames], userNames: [...userNames] };
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* A name as a pattern: its words (camelCase humps, '-', '_', '.', spaces) may be joined by
|
|
159
|
+
* '-', '_' or '.' or nothing ('SynabunWeb' finds "synabun-web"), never inside a longer word.
|
|
160
|
+
* A plain one-word name ('Downloads') must match its case, so ordinary prose stays clear.
|
|
161
|
+
*/
|
|
162
|
+
function namePattern(name) {
|
|
163
|
+
const words = String(name).trim().split(/[\s_.-]+|(?<=[a-z0-9])(?=[A-Z])/).filter(Boolean);
|
|
164
|
+
if (!words.length) return null;
|
|
165
|
+
const plainWord = words.length === 1 && !/[A-Z].*[A-Z]/.test(name.slice(1));
|
|
166
|
+
const body = words.map(escapeRe).join('[_.-]?');
|
|
167
|
+
return new RegExp(`(?<![A-Za-z0-9])${body}(?![A-Za-z0-9])`, plainWord ? '' : 'i');
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* The private-text test. Returns the reason ('email', 'project-name'...) or null.
|
|
172
|
+
* @param {{projectNames?: string[], userNames?: string[], extraPatterns?: Array}} names extraPatterns: rows of patternsFromRows (the page's own are already in PRIVATE_PATTERNS)
|
|
173
|
+
*/
|
|
174
|
+
export function createPrivacyMatcher({ projectNames = [], userNames = [], extraPatterns = [] } = {}) {
|
|
175
|
+
const named = [
|
|
176
|
+
...projectNames.filter((n) => squash(n) !== SELF_PROJECT).map((n) => ['project-name', namePattern(n)]),
|
|
177
|
+
...userNames.map((n) => ['user-name', new RegExp(`(?<![A-Za-z0-9])${escapeRe(n)}(?![A-Za-z0-9])`, 'i')]),
|
|
178
|
+
].filter(([, re]) => re);
|
|
179
|
+
const checks = [...PRIVATE_PATTERNS, ...extraPatterns, ...named];
|
|
180
|
+
/** @param {{ joined?: boolean }} [options] joined: the text of several spans run together */
|
|
181
|
+
return function privateReason(text, { joined = false } = {}) {
|
|
182
|
+
const value = String(text ?? '');
|
|
183
|
+
if (value.trim().length < 3) return null;
|
|
184
|
+
for (const [reason, test, ownOnly] of checks) {
|
|
185
|
+
if (joined && ownOnly) continue;
|
|
186
|
+
if (typeof test === 'function' ? test(value) : test.test(value)) return reason;
|
|
187
|
+
}
|
|
188
|
+
return null;
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ── What an element shows ─────────────────────────────────────────
|
|
193
|
+
|
|
194
|
+
const SKIP_TAGS = new Set(['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEMPLATE', 'HTML', 'HEAD', 'BODY', 'IFRAME', 'CANVAS']);
|
|
195
|
+
const TEXTLESS_INPUTS = new Set(['hidden', 'checkbox', 'radio', 'range', 'color', 'file', 'image', 'submit', 'reset', 'button']);
|
|
196
|
+
|
|
197
|
+
/** The text of an element's own text nodes (not its children's). */
|
|
198
|
+
export function ownText(el) {
|
|
199
|
+
let text = '';
|
|
200
|
+
for (const node of el?.childNodes || []) if (node.nodeType === 3) text += node.data;
|
|
201
|
+
return text;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
/** What a form field shows: its value (never a password's) and placeholder; a select's chosen option. */
|
|
205
|
+
export function fieldTexts(el) {
|
|
206
|
+
const tag = el?.tagName;
|
|
207
|
+
if (tag === 'SELECT') return [...(el.selectedOptions || [])].map((o) => o.textContent || '');
|
|
208
|
+
if (tag !== 'INPUT' && tag !== 'TEXTAREA') return [];
|
|
209
|
+
const type = String(el.type || 'text').toLowerCase();
|
|
210
|
+
if (tag === 'INPUT' && TEXTLESS_INPUTS.has(type)) return [];
|
|
211
|
+
return [type === 'password' ? '' : String(el.value || ''), String(el.placeholder || '')];
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/** Why an element's own text, value or placeholder is private, or null. */
|
|
215
|
+
export function elementReason(el, matcher) {
|
|
216
|
+
if (!el || el.nodeType !== 1 || SKIP_TAGS.has(el.tagName)) return null;
|
|
217
|
+
return matcher([ownText(el), ...fieldTexts(el)].filter(Boolean).join('\n'));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// ── The page side ─────────────────────────────────────────────────
|
|
221
|
+
|
|
222
|
+
function styleText() {
|
|
223
|
+
// One rule per selector: a selector one engine rejects drops only its own rule.
|
|
224
|
+
return [...WHOLESALE_SELECTORS, `.${BLUR_CLASS}`, `.${MANUAL_CLASS}`]
|
|
225
|
+
.map((sel) => `${sel} { filter: ${BLUR} !important; }`).join('\n');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Turn showcase privacy on for this window (and its same-origin frames). Idempotent.
|
|
230
|
+
* @param {{ win?: Window, fetchProjects?: () => Promise<Array<{path:string,label:string}>> }} [options]
|
|
231
|
+
*/
|
|
232
|
+
export function installShowcasePrivacy({ win = globalThis.window, fetchProjects } = {}) {
|
|
233
|
+
if (win.__synabunShowcase) return win.__synabunShowcase;
|
|
234
|
+
const docs = new Set();
|
|
235
|
+
let matcher = createPrivacyMatcher();
|
|
236
|
+
let namesLoaded = false;
|
|
237
|
+
let nameCount = 0;
|
|
238
|
+
let scans = 0;
|
|
239
|
+
let lastScanMs = 0;
|
|
240
|
+
|
|
241
|
+
const isCleared = (el) => el.hasAttribute(CLEAR_ATTR);
|
|
242
|
+
const setBlur = (el, on) => { if (el.classList.contains(BLUR_CLASS) !== on) el.classList.toggle(BLUR_CLASS, on); };
|
|
243
|
+
|
|
244
|
+
function hoverIsPrivate(doc, state) {
|
|
245
|
+
const hovered = state.lastHover;
|
|
246
|
+
if (!hovered || !hovered.isConnected) return false;
|
|
247
|
+
try {
|
|
248
|
+
if (hovered.closest(WHOLESALE) || hovered.querySelector(WHOLESALE)) return true;
|
|
249
|
+
} catch { /* a selector this engine rejects */ }
|
|
250
|
+
return !!matcher(hovered.getAttribute('data-tooltip') || '');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Is this element private by what it shows (or, for the tooltip, by what it describes)? */
|
|
254
|
+
function isPrivate(el, doc, state) {
|
|
255
|
+
if (isCleared(el)) return false;
|
|
256
|
+
if (elementReason(el, matcher)) return true;
|
|
257
|
+
return el.matches?.(TOOLTIP_TEXT) && hoverIsPrivate(doc, state);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// A small element's whole text (null for a container): a parent with many children is a list, not a split label.
|
|
261
|
+
function smallText(node, cache) {
|
|
262
|
+
if (cache?.has(node)) return cache.get(node);
|
|
263
|
+
let text = null;
|
|
264
|
+
if (node.childElementCount <= 12 && !SKIP_TAGS.has(node.tagName)) {
|
|
265
|
+
text = node.textContent || '';
|
|
266
|
+
if (text.length > SPAN_MAX) text = null;
|
|
267
|
+
}
|
|
268
|
+
cache?.set(node, text);
|
|
269
|
+
return text;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// Private text split over spans: the nearest small ancestor whose whole text is private.
|
|
273
|
+
function spanningAncestor(el, cache) {
|
|
274
|
+
let node = el?.parentElement;
|
|
275
|
+
for (let level = 0; node && level < SPAN_LEVELS; level++, node = node.parentElement) {
|
|
276
|
+
if (isCleared(node)) return null;
|
|
277
|
+
const text = smallText(node, cache);
|
|
278
|
+
if (text === null) return null;
|
|
279
|
+
if (matcher(text, { joined: true })) return node;
|
|
280
|
+
}
|
|
281
|
+
return null;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** Still private, for an element that carries the blur class: by its own text or as a span hit. */
|
|
285
|
+
function stillPrivate(el, doc, state) {
|
|
286
|
+
if (isPrivate(el, doc, state)) return true;
|
|
287
|
+
const text = isCleared(el) ? null : smallText(el);
|
|
288
|
+
return text !== null && !!matcher(text, { joined: true });
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/** The elements that should carry the blur class: root alone, or root and everything under it. */
|
|
292
|
+
function collect(root, doc, state, into, { walk = true } = {}) {
|
|
293
|
+
const cache = new Map();
|
|
294
|
+
const consider = (el) => {
|
|
295
|
+
if (!el || el.nodeType !== 1 || isCleared(el)) return;
|
|
296
|
+
if (isPrivate(el, doc, state)) { into.add(el); return; }
|
|
297
|
+
const up = ownText(el).trim() ? spanningAncestor(el, cache) : null;
|
|
298
|
+
if (up) into.add(up);
|
|
299
|
+
};
|
|
300
|
+
if (root.nodeType === 3) { consider(root.parentElement); return into; }
|
|
301
|
+
if (root.nodeType !== 1 && root.nodeType !== 9) return into;
|
|
302
|
+
const start = root.nodeType === 9 ? root.body : root;
|
|
303
|
+
if (!start) return into;
|
|
304
|
+
if (start.nodeType === 1) consider(start);
|
|
305
|
+
if (!walk) return into;
|
|
306
|
+
const walker = doc.createTreeWalker(start, 1 /* SHOW_ELEMENT */);
|
|
307
|
+
for (let el = walker.nextNode(); el; el = walker.nextNode()) consider(el);
|
|
308
|
+
return into;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function attachFrames(root) {
|
|
312
|
+
const frames = root.nodeType === 1 && root.tagName === 'IFRAME' ? [root] : [...(root.querySelectorAll?.('iframe') || [])];
|
|
313
|
+
for (const frame of frames) {
|
|
314
|
+
if (frame.__sbShowcase) continue;
|
|
315
|
+
frame.__sbShowcase = true;
|
|
316
|
+
const tryAttach = () => {
|
|
317
|
+
try {
|
|
318
|
+
const doc = frame.contentDocument;
|
|
319
|
+
if (doc && doc.readyState !== 'uninitialized' && doc.location?.href !== 'about:blank') attachDocument(doc);
|
|
320
|
+
} catch { /* another origin: nothing to reach */ }
|
|
321
|
+
};
|
|
322
|
+
frame.addEventListener('load', tryAttach);
|
|
323
|
+
tryAttach();
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
function attachDocument(doc) {
|
|
328
|
+
if (!doc || doc.__sbShowcaseState) return;
|
|
329
|
+
// pending: added nodes (walked); touched: elements whose own content changed (checked alone).
|
|
330
|
+
const state = { doc, pending: new Set(), touched: new Set(), timer: null, lastHover: null, observer: null };
|
|
331
|
+
doc.__sbShowcaseState = state;
|
|
332
|
+
docs.add(doc);
|
|
333
|
+
const style = doc.createElement('style');
|
|
334
|
+
style.id = STYLE_ID;
|
|
335
|
+
style.textContent = styleText();
|
|
336
|
+
(doc.head || doc.documentElement).appendChild(style);
|
|
337
|
+
|
|
338
|
+
const flush = () => {
|
|
339
|
+
state.timer = null;
|
|
340
|
+
const added = [...state.pending];
|
|
341
|
+
const touched = [...state.touched];
|
|
342
|
+
state.pending.clear();
|
|
343
|
+
state.touched.clear();
|
|
344
|
+
const hits = new Set();
|
|
345
|
+
const seen = new Set();
|
|
346
|
+
const note = (node) => {
|
|
347
|
+
// What lost its private text loses the blur: the node and the small ancestors a span hit may sit on.
|
|
348
|
+
let el = node.nodeType === 1 ? node : node.parentElement;
|
|
349
|
+
for (let level = 0; el && level <= SPAN_LEVELS; level++, el = el.parentElement) seen.add(el);
|
|
350
|
+
};
|
|
351
|
+
for (const node of added) {
|
|
352
|
+
if (!node.isConnected) continue;
|
|
353
|
+
collect(node, doc, state, hits);
|
|
354
|
+
note(node);
|
|
355
|
+
if (node.nodeType === 1) {
|
|
356
|
+
attachFrames(node);
|
|
357
|
+
for (const marked of node.querySelectorAll(`.${BLUR_CLASS}`)) seen.add(marked);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
for (const node of touched) {
|
|
361
|
+
if (!node.isConnected) continue;
|
|
362
|
+
collect(node, doc, state, hits, { walk: false });
|
|
363
|
+
note(node);
|
|
364
|
+
}
|
|
365
|
+
for (const el of seen) if (el.classList?.contains(BLUR_CLASS) && !hits.has(el) && !stillPrivate(el, doc, state)) setBlur(el, false);
|
|
366
|
+
for (const el of hits) setBlur(el, true);
|
|
367
|
+
};
|
|
368
|
+
const schedule = () => { if (!state.timer) state.timer = doc.defaultView.setTimeout(flush, DEBOUNCE_MS); };
|
|
369
|
+
const queue = (node) => { if (node) { state.pending.add(node); schedule(); } };
|
|
370
|
+
const touch = (node) => { if (node) { state.touched.add(node); schedule(); } };
|
|
371
|
+
|
|
372
|
+
state.observer = new doc.defaultView.MutationObserver((records) => {
|
|
373
|
+
for (const r of records) {
|
|
374
|
+
if (r.type === 'childList') {
|
|
375
|
+
for (const n of r.addedNodes) queue(n);
|
|
376
|
+
if (r.removedNodes.length) touch(r.target);
|
|
377
|
+
} else {
|
|
378
|
+
touch(r.target);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
});
|
|
382
|
+
state.observer.observe(doc.documentElement, {
|
|
383
|
+
childList: true, subtree: true, characterData: true, attributes: true, attributeFilter: ['value', 'placeholder'],
|
|
384
|
+
});
|
|
385
|
+
const onField = (e) => { if (e.target?.nodeType === 1) touch(e.target); };
|
|
386
|
+
doc.addEventListener('input', onField, true);
|
|
387
|
+
doc.addEventListener('change', onField, true);
|
|
388
|
+
doc.addEventListener('mouseover', (e) => {
|
|
389
|
+
state.lastHover = e.target?.closest?.('[data-tooltip]') || null;
|
|
390
|
+
}, true);
|
|
391
|
+
|
|
392
|
+
scanDocument(doc);
|
|
393
|
+
attachFrames(doc);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function liveDocs() {
|
|
397
|
+
for (const doc of docs) if (!doc.defaultView || !doc.documentElement?.isConnected) docs.delete(doc);
|
|
398
|
+
return [...docs];
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function scanDocument(doc) {
|
|
402
|
+
const state = doc.__sbShowcaseState;
|
|
403
|
+
const hits = collect(doc, doc, state, new Set());
|
|
404
|
+
for (const el of doc.querySelectorAll(`.${BLUR_CLASS}`)) if (!hits.has(el)) setBlur(el, false);
|
|
405
|
+
for (const el of hits) setBlur(el, true);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function blurredIn(doc) {
|
|
409
|
+
const out = new Set();
|
|
410
|
+
for (const sel of [...WHOLESALE_SELECTORS, `.${BLUR_CLASS}`, `.${MANUAL_CLASS}`]) {
|
|
411
|
+
try { for (const el of doc.querySelectorAll(sel)) if (!isCleared(el)) out.add(el); } catch { /* rejected selector */ }
|
|
412
|
+
}
|
|
413
|
+
return out;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
function each(selector, fn) {
|
|
417
|
+
if (typeof selector !== 'string' || !selector.trim()) throw new Error('showcase: a CSS selector is needed');
|
|
418
|
+
let count = 0;
|
|
419
|
+
for (const doc of liveDocs()) {
|
|
420
|
+
let found;
|
|
421
|
+
try { found = doc.querySelectorAll(selector); } catch { throw new Error(`showcase: invalid selector ${selector}`); }
|
|
422
|
+
for (const el of found) { fn(el); count++; }
|
|
423
|
+
}
|
|
424
|
+
return count;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
const api = Object.freeze({
|
|
428
|
+
/** Scan every page and frame again from scratch; returns how many elements are blurred. */
|
|
429
|
+
scan() {
|
|
430
|
+
const started = Date.now();
|
|
431
|
+
for (const doc of liveDocs()) attachFrames(doc);
|
|
432
|
+
let total = 0;
|
|
433
|
+
for (const doc of liveDocs()) { scanDocument(doc); total += blurredIn(doc).size; }
|
|
434
|
+
scans++;
|
|
435
|
+
lastScanMs = Date.now() - started;
|
|
436
|
+
return total;
|
|
437
|
+
},
|
|
438
|
+
/** Blur what the selector matches (also undoes an unblur); returns how many. */
|
|
439
|
+
blur(selector) {
|
|
440
|
+
return each(selector, (el) => {
|
|
441
|
+
el.removeAttribute(CLEAR_ATTR);
|
|
442
|
+
el.style.removeProperty('filter');
|
|
443
|
+
el.classList.add(MANUAL_CLASS);
|
|
444
|
+
});
|
|
445
|
+
},
|
|
446
|
+
/** Keep what the selector matches sharp, whatever the rules say; returns how many. */
|
|
447
|
+
unblur(selector) {
|
|
448
|
+
return each(selector, (el) => {
|
|
449
|
+
el.classList.remove(BLUR_CLASS, MANUAL_CLASS);
|
|
450
|
+
el.setAttribute(CLEAR_ATTR, '');
|
|
451
|
+
el.style.setProperty('filter', 'none', 'important');
|
|
452
|
+
});
|
|
453
|
+
},
|
|
454
|
+
/** Counts only: never the private text or names themselves. */
|
|
455
|
+
stats() {
|
|
456
|
+
const all = liveDocs();
|
|
457
|
+
const count = (sel) => all.reduce((n, doc) => n + doc.querySelectorAll(sel).length, 0);
|
|
458
|
+
return {
|
|
459
|
+
documents: all.length,
|
|
460
|
+
blurred: all.reduce((n, doc) => n + blurredIn(doc).size, 0),
|
|
461
|
+
matched: count(`.${BLUR_CLASS}`),
|
|
462
|
+
manual: count(`.${MANUAL_CLASS}`),
|
|
463
|
+
cleared: count(`[${CLEAR_ATTR}]`),
|
|
464
|
+
namesLoaded,
|
|
465
|
+
names: nameCount,
|
|
466
|
+
scans,
|
|
467
|
+
lastScanMs,
|
|
468
|
+
};
|
|
469
|
+
},
|
|
470
|
+
});
|
|
471
|
+
win.__synabunShowcase = api;
|
|
472
|
+
|
|
473
|
+
attachDocument(win.document);
|
|
474
|
+
|
|
475
|
+
// The registered projects name the user's other work and, by their paths, the OS user.
|
|
476
|
+
const load = fetchProjects || (async () => {
|
|
477
|
+
const res = await win.fetch('/api/projects');
|
|
478
|
+
if (!res.ok) throw new Error(`HTTP ${res.status}`);
|
|
479
|
+
return (await res.json())?.projects || [];
|
|
480
|
+
});
|
|
481
|
+
Promise.resolve().then(load).then((projects) => {
|
|
482
|
+
const names = namesFromProjects(projects);
|
|
483
|
+
matcher = createPrivacyMatcher(names);
|
|
484
|
+
nameCount = names.projectNames.length + names.userNames.length;
|
|
485
|
+
namesLoaded = true;
|
|
486
|
+
api.scan();
|
|
487
|
+
}).catch((err) => console.warn('[showcase] project names unavailable, pattern blur only:', err?.message || err));
|
|
488
|
+
|
|
489
|
+
return api;
|
|
490
|
+
}
|
|
@@ -33,6 +33,27 @@ const FAT_BLOB_KEYS = new Set([
|
|
|
33
33
|
// as a string and still needs JSON.parse for the server payload.
|
|
34
34
|
const STR_SENTINEL = Symbol('storage:str');
|
|
35
35
|
|
|
36
|
+
// Showcase mode: a page opened with `?showcase` (any value but "0" or "false")
|
|
37
|
+
// hydrates as usual but never writes back, so what a demo walk-through opens,
|
|
38
|
+
// moves or types leaves the user's saved UI state as it was. Reads work from
|
|
39
|
+
// the in-memory cache. (blob-store.js keeps its own writes.)
|
|
40
|
+
function _readShowcaseMode() {
|
|
41
|
+
try {
|
|
42
|
+
const params = new URLSearchParams(globalThis.window?.location?.search || '');
|
|
43
|
+
if (!params.has('showcase')) return false;
|
|
44
|
+
const value = String(params.get('showcase')).trim().toLowerCase();
|
|
45
|
+
return value !== '0' && value !== 'false';
|
|
46
|
+
} catch {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
const SHOWCASE_MODE = _readShowcaseMode();
|
|
51
|
+
|
|
52
|
+
/** True when this page load is a showcase (`?showcase`): nothing is written back. */
|
|
53
|
+
export function isShowcaseMode() {
|
|
54
|
+
return SHOWCASE_MODE;
|
|
55
|
+
}
|
|
56
|
+
|
|
36
57
|
// ── Hydrate from server ──────────────────────────────
|
|
37
58
|
|
|
38
59
|
async function _hydrate() {
|
|
@@ -55,8 +76,8 @@ async function _hydrate() {
|
|
|
55
76
|
// First run or server down: import from localStorage
|
|
56
77
|
console.info('[storage] Migrating localStorage to server...');
|
|
57
78
|
_migrateFromLocalStorage();
|
|
58
|
-
// Build dirty set from everything we imported
|
|
59
|
-
for (const [key, val] of Object.entries(_cache)) {
|
|
79
|
+
// Build dirty set from everything we imported (a showcase only reads it)
|
|
80
|
+
if (!SHOWCASE_MODE) for (const [key, val] of Object.entries(_cache)) {
|
|
60
81
|
try { _dirty[key] = JSON.parse(val); } catch { _dirty[key] = val; }
|
|
61
82
|
}
|
|
62
83
|
if (Object.keys(_dirty).length > 0) {
|
|
@@ -74,6 +95,10 @@ else await _hydrate();
|
|
|
74
95
|
// ── Drop-in API (mirrors localStorage) ───────────────
|
|
75
96
|
|
|
76
97
|
export const storage = (hostedSidepanelId && sidepanelHost()?.storageFor(hostedSidepanelId)) || {
|
|
98
|
+
// False in showcase mode: values stay in this page. Code that sends a value to the server
|
|
99
|
+
// by itself (an unload beacon) asks this first.
|
|
100
|
+
persists: !SHOWCASE_MODE,
|
|
101
|
+
|
|
77
102
|
getItem(key) {
|
|
78
103
|
return _cache[key] ?? null;
|
|
79
104
|
},
|
|
@@ -105,11 +130,13 @@ export const storage = (hostedSidepanelId && sidepanelHost()?.storageFor(hostedS
|
|
|
105
130
|
// ── Debounced flush to server ────────────────────────
|
|
106
131
|
|
|
107
132
|
function _scheduleDebouncedFlush() {
|
|
133
|
+
if (SHOWCASE_MODE) { _dirty = {}; return; }
|
|
108
134
|
if (_flushTimer) clearTimeout(_flushTimer);
|
|
109
135
|
_flushTimer = setTimeout(_flushToServer, DEBOUNCE_MS);
|
|
110
136
|
}
|
|
111
137
|
|
|
112
138
|
async function _flushToServer() {
|
|
139
|
+
if (SHOWCASE_MODE) { _dirty = {}; _flushTimer = null; return; }
|
|
113
140
|
const payload = {};
|
|
114
141
|
for (const [k, v] of Object.entries(_dirty)) {
|
|
115
142
|
if (v === STR_SENTINEL) {
|
|
@@ -142,7 +169,7 @@ async function _flushToServer() {
|
|
|
142
169
|
|
|
143
170
|
// ── Flush on page unload (sendBeacon for reliability) ──
|
|
144
171
|
|
|
145
|
-
window.addEventListener('beforeunload', () => {
|
|
172
|
+
if (!SHOWCASE_MODE) window.addEventListener('beforeunload', () => {
|
|
146
173
|
if (_flushTimer) {
|
|
147
174
|
clearTimeout(_flushTimer);
|
|
148
175
|
_flushTimer = null;
|