@averyyy/pi-coding-agent 0.80.3-piclient.3
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 +4848 -0
- package/README.md +689 -0
- package/dist/bun/cli.d.ts +3 -0
- package/dist/bun/cli.d.ts.map +1 -0
- package/dist/bun/cli.js +9 -0
- package/dist/bun/cli.js.map +1 -0
- package/dist/bun/register-bedrock.d.ts +2 -0
- package/dist/bun/register-bedrock.d.ts.map +1 -0
- package/dist/bun/register-bedrock.js +4 -0
- package/dist/bun/register-bedrock.js.map +1 -0
- package/dist/bun/restore-sandbox-env.d.ts +17 -0
- package/dist/bun/restore-sandbox-env.d.ts.map +1 -0
- package/dist/bun/restore-sandbox-env.js +36 -0
- package/dist/bun/restore-sandbox-env.js.map +1 -0
- package/dist/cli/args.d.ts +57 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +379 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/config-selector.d.ts +14 -0
- package/dist/cli/config-selector.d.ts.map +1 -0
- package/dist/cli/config-selector.js +31 -0
- package/dist/cli/config-selector.js.map +1 -0
- package/dist/cli/file-processor.d.ts +15 -0
- package/dist/cli/file-processor.d.ts.map +1 -0
- package/dist/cli/file-processor.js +70 -0
- package/dist/cli/file-processor.js.map +1 -0
- package/dist/cli/initial-message.d.ts +18 -0
- package/dist/cli/initial-message.d.ts.map +1 -0
- package/dist/cli/initial-message.js +22 -0
- package/dist/cli/initial-message.js.map +1 -0
- package/dist/cli/list-models.d.ts +9 -0
- package/dist/cli/list-models.d.ts.map +1 -0
- package/dist/cli/list-models.js +98 -0
- package/dist/cli/list-models.js.map +1 -0
- package/dist/cli/project-trust.d.ts +10 -0
- package/dist/cli/project-trust.d.ts.map +1 -0
- package/dist/cli/project-trust.js +48 -0
- package/dist/cli/project-trust.js.map +1 -0
- package/dist/cli/session-picker.d.ts +10 -0
- package/dist/cli/session-picker.d.ts.map +1 -0
- package/dist/cli/session-picker.js +36 -0
- package/dist/cli/session-picker.js.map +1 -0
- package/dist/cli/startup-ui.d.ts +20 -0
- package/dist/cli/startup-ui.d.ts.map +1 -0
- package/dist/cli/startup-ui.js +184 -0
- package/dist/cli/startup-ui.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +18 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +96 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +455 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-session-runtime.d.ts +119 -0
- package/dist/core/agent-session-runtime.d.ts.map +1 -0
- package/dist/core/agent-session-runtime.js +307 -0
- package/dist/core/agent-session-runtime.js.map +1 -0
- package/dist/core/agent-session-services.d.ts +88 -0
- package/dist/core/agent-session-services.d.ts.map +1 -0
- package/dist/core/agent-session-services.js +119 -0
- package/dist/core/agent-session-services.js.map +1 -0
- package/dist/core/agent-session.d.ts +620 -0
- package/dist/core/agent-session.d.ts.map +1 -0
- package/dist/core/agent-session.js +2728 -0
- package/dist/core/agent-session.js.map +1 -0
- package/dist/core/auth-guidance.d.ts +5 -0
- package/dist/core/auth-guidance.d.ts.map +1 -0
- package/dist/core/auth-guidance.js +21 -0
- package/dist/core/auth-guidance.js.map +1 -0
- package/dist/core/auth-storage.d.ts +140 -0
- package/dist/core/auth-storage.d.ts.map +1 -0
- package/dist/core/auth-storage.js +434 -0
- package/dist/core/auth-storage.js.map +1 -0
- package/dist/core/bash-executor.d.ts +32 -0
- package/dist/core/bash-executor.d.ts.map +1 -0
- package/dist/core/bash-executor.js +111 -0
- package/dist/core/bash-executor.js.map +1 -0
- package/dist/core/compaction/branch-summarization.d.ts +92 -0
- package/dist/core/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/core/compaction/branch-summarization.js +241 -0
- package/dist/core/compaction/branch-summarization.js.map +1 -0
- package/dist/core/compaction/compaction.d.ts +123 -0
- package/dist/core/compaction/compaction.d.ts.map +1 -0
- package/dist/core/compaction/compaction.js +682 -0
- package/dist/core/compaction/compaction.js.map +1 -0
- package/dist/core/compaction/index.d.ts +7 -0
- package/dist/core/compaction/index.d.ts.map +1 -0
- package/dist/core/compaction/index.js +7 -0
- package/dist/core/compaction/index.js.map +1 -0
- package/dist/core/compaction/utils.d.ts +38 -0
- package/dist/core/compaction/utils.d.ts.map +1 -0
- package/dist/core/compaction/utils.js +153 -0
- package/dist/core/compaction/utils.js.map +1 -0
- package/dist/core/defaults.d.ts +3 -0
- package/dist/core/defaults.d.ts.map +1 -0
- package/dist/core/defaults.js +2 -0
- package/dist/core/defaults.js.map +1 -0
- package/dist/core/diagnostics.d.ts +15 -0
- package/dist/core/diagnostics.d.ts.map +1 -0
- package/dist/core/diagnostics.js +2 -0
- package/dist/core/diagnostics.js.map +1 -0
- package/dist/core/event-bus.d.ts +9 -0
- package/dist/core/event-bus.d.ts.map +1 -0
- package/dist/core/event-bus.js +25 -0
- package/dist/core/event-bus.js.map +1 -0
- package/dist/core/exec.d.ts +29 -0
- package/dist/core/exec.d.ts.map +1 -0
- package/dist/core/exec.js +75 -0
- package/dist/core/exec.js.map +1 -0
- package/dist/core/experimental.d.ts +2 -0
- package/dist/core/experimental.d.ts.map +1 -0
- package/dist/core/experimental.js +4 -0
- package/dist/core/experimental.js.map +1 -0
- package/dist/core/export-html/ansi-to-html.d.ts +22 -0
- package/dist/core/export-html/ansi-to-html.d.ts.map +1 -0
- package/dist/core/export-html/ansi-to-html.js +249 -0
- package/dist/core/export-html/ansi-to-html.js.map +1 -0
- package/dist/core/export-html/index.d.ts +37 -0
- package/dist/core/export-html/index.d.ts.map +1 -0
- package/dist/core/export-html/index.js +226 -0
- package/dist/core/export-html/index.js.map +1 -0
- package/dist/core/export-html/template.css +1066 -0
- package/dist/core/export-html/template.html +55 -0
- package/dist/core/export-html/template.js +1864 -0
- package/dist/core/export-html/tool-renderer.d.ts +34 -0
- package/dist/core/export-html/tool-renderer.d.ts.map +1 -0
- package/dist/core/export-html/tool-renderer.js +108 -0
- package/dist/core/export-html/tool-renderer.js.map +1 -0
- package/dist/core/export-html/vendor/highlight.min.js +1213 -0
- package/dist/core/export-html/vendor/marked.min.js +78 -0
- package/dist/core/extensions/index.d.ts +12 -0
- package/dist/core/extensions/index.d.ts.map +1 -0
- package/dist/core/extensions/index.js +9 -0
- package/dist/core/extensions/index.js.map +1 -0
- package/dist/core/extensions/loader.d.ts +23 -0
- package/dist/core/extensions/loader.d.ts.map +1 -0
- package/dist/core/extensions/loader.js +535 -0
- package/dist/core/extensions/loader.js.map +1 -0
- package/dist/core/extensions/runner.d.ts +166 -0
- package/dist/core/extensions/runner.d.ts.map +1 -0
- package/dist/core/extensions/runner.js +876 -0
- package/dist/core/extensions/runner.js.map +1 -0
- package/dist/core/extensions/types.d.ts +1216 -0
- package/dist/core/extensions/types.d.ts.map +1 -0
- package/dist/core/extensions/types.js +45 -0
- package/dist/core/extensions/types.js.map +1 -0
- package/dist/core/extensions/wrapper.d.ts +20 -0
- package/dist/core/extensions/wrapper.d.ts.map +1 -0
- package/dist/core/extensions/wrapper.js +22 -0
- package/dist/core/extensions/wrapper.js.map +1 -0
- package/dist/core/footer-data-provider.d.ts +54 -0
- package/dist/core/footer-data-provider.d.ts.map +1 -0
- package/dist/core/footer-data-provider.js +338 -0
- package/dist/core/footer-data-provider.js.map +1 -0
- package/dist/core/http-dispatcher.d.ts +22 -0
- package/dist/core/http-dispatcher.d.ts.map +1 -0
- package/dist/core/http-dispatcher.js +91 -0
- package/dist/core/http-dispatcher.js.map +1 -0
- package/dist/core/index.d.ts +13 -0
- package/dist/core/index.d.ts.map +1 -0
- package/dist/core/index.js +13 -0
- package/dist/core/index.js.map +1 -0
- package/dist/core/keybindings.d.ts +353 -0
- package/dist/core/keybindings.d.ts.map +1 -0
- package/dist/core/keybindings.js +295 -0
- package/dist/core/keybindings.js.map +1 -0
- package/dist/core/messages.d.ts +77 -0
- package/dist/core/messages.d.ts.map +1 -0
- package/dist/core/messages.js +123 -0
- package/dist/core/messages.js.map +1 -0
- package/dist/core/model-registry.d.ts +151 -0
- package/dist/core/model-registry.d.ts.map +1 -0
- package/dist/core/model-registry.js +750 -0
- package/dist/core/model-registry.js.map +1 -0
- package/dist/core/model-resolver.d.ts +111 -0
- package/dist/core/model-resolver.d.ts.map +1 -0
- package/dist/core/model-resolver.js +534 -0
- package/dist/core/model-resolver.js.map +1 -0
- package/dist/core/output-guard.d.ts +7 -0
- package/dist/core/output-guard.d.ts.map +1 -0
- package/dist/core/output-guard.js +89 -0
- package/dist/core/output-guard.js.map +1 -0
- package/dist/core/package-manager.d.ts +207 -0
- package/dist/core/package-manager.d.ts.map +1 -0
- package/dist/core/package-manager.js +2088 -0
- package/dist/core/package-manager.js.map +1 -0
- package/dist/core/pi-server-client.d.ts +47 -0
- package/dist/core/pi-server-client.d.ts.map +1 -0
- package/dist/core/pi-server-client.js +571 -0
- package/dist/core/pi-server-client.js.map +1 -0
- package/dist/core/pi-server-request.d.ts +14 -0
- package/dist/core/pi-server-request.d.ts.map +1 -0
- package/dist/core/pi-server-request.js +204 -0
- package/dist/core/pi-server-request.js.map +1 -0
- package/dist/core/project-trust.d.ts +15 -0
- package/dist/core/project-trust.d.ts.map +1 -0
- package/dist/core/project-trust.js +59 -0
- package/dist/core/project-trust.js.map +1 -0
- package/dist/core/prompt-templates.d.ts +53 -0
- package/dist/core/prompt-templates.d.ts.map +1 -0
- package/dist/core/prompt-templates.js +236 -0
- package/dist/core/prompt-templates.js.map +1 -0
- package/dist/core/provider-attribution.d.ts +4 -0
- package/dist/core/provider-attribution.d.ts.map +1 -0
- package/dist/core/provider-attribution.js +82 -0
- package/dist/core/provider-attribution.js.map +1 -0
- package/dist/core/provider-display-names.d.ts +2 -0
- package/dist/core/provider-display-names.d.ts.map +1 -0
- package/dist/core/provider-display-names.js +36 -0
- package/dist/core/provider-display-names.js.map +1 -0
- package/dist/core/resolve-config-value.d.ts +30 -0
- package/dist/core/resolve-config-value.d.ts.map +1 -0
- package/dist/core/resolve-config-value.js +247 -0
- package/dist/core/resolve-config-value.js.map +1 -0
- package/dist/core/resource-loader.d.ts +206 -0
- package/dist/core/resource-loader.d.ts.map +1 -0
- package/dist/core/resource-loader.js +817 -0
- package/dist/core/resource-loader.js.map +1 -0
- package/dist/core/sdk.d.ts +109 -0
- package/dist/core/sdk.d.ts.map +1 -0
- package/dist/core/sdk.js +313 -0
- package/dist/core/sdk.js.map +1 -0
- package/dist/core/session-cwd.d.ts +19 -0
- package/dist/core/session-cwd.d.ts.map +1 -0
- package/dist/core/session-cwd.js +38 -0
- package/dist/core/session-cwd.js.map +1 -0
- package/dist/core/session-manager.d.ts +334 -0
- package/dist/core/session-manager.d.ts.map +1 -0
- package/dist/core/session-manager.js +1284 -0
- package/dist/core/session-manager.js.map +1 -0
- package/dist/core/settings-manager.d.ts +291 -0
- package/dist/core/settings-manager.d.ts.map +1 -0
- package/dist/core/settings-manager.js +893 -0
- package/dist/core/settings-manager.js.map +1 -0
- package/dist/core/skills.d.ts +60 -0
- package/dist/core/skills.d.ts.map +1 -0
- package/dist/core/skills.js +387 -0
- package/dist/core/skills.js.map +1 -0
- package/dist/core/slash-commands.d.ts +14 -0
- package/dist/core/slash-commands.d.ts.map +1 -0
- package/dist/core/slash-commands.js +26 -0
- package/dist/core/slash-commands.js.map +1 -0
- package/dist/core/source-info.d.ts +18 -0
- package/dist/core/source-info.d.ts.map +1 -0
- package/dist/core/source-info.js +19 -0
- package/dist/core/source-info.js.map +1 -0
- package/dist/core/system-prompt.d.ts +28 -0
- package/dist/core/system-prompt.d.ts.map +1 -0
- package/dist/core/system-prompt.js +120 -0
- package/dist/core/system-prompt.js.map +1 -0
- package/dist/core/telemetry.d.ts +3 -0
- package/dist/core/telemetry.d.ts.map +1 -0
- package/dist/core/telemetry.js +9 -0
- package/dist/core/telemetry.js.map +1 -0
- package/dist/core/timings.d.ts +10 -0
- package/dist/core/timings.d.ts.map +1 -0
- package/dist/core/timings.js +41 -0
- package/dist/core/timings.js.map +1 -0
- package/dist/core/tools/bash.d.ts +68 -0
- package/dist/core/tools/bash.d.ts.map +1 -0
- package/dist/core/tools/bash.js +361 -0
- package/dist/core/tools/bash.js.map +1 -0
- package/dist/core/tools/edit-diff.d.ts +106 -0
- package/dist/core/tools/edit-diff.d.ts.map +1 -0
- package/dist/core/tools/edit-diff.js +424 -0
- package/dist/core/tools/edit-diff.js.map +1 -0
- package/dist/core/tools/edit.d.ts +51 -0
- package/dist/core/tools/edit.d.ts.map +1 -0
- package/dist/core/tools/edit.js +284 -0
- package/dist/core/tools/edit.js.map +1 -0
- package/dist/core/tools/file-mutation-queue.d.ts +6 -0
- package/dist/core/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/core/tools/file-mutation-queue.js +52 -0
- package/dist/core/tools/file-mutation-queue.js.map +1 -0
- package/dist/core/tools/find.d.ts +35 -0
- package/dist/core/tools/find.d.ts.map +1 -0
- package/dist/core/tools/find.js +305 -0
- package/dist/core/tools/find.js.map +1 -0
- package/dist/core/tools/grep.d.ts +37 -0
- package/dist/core/tools/grep.d.ts.map +1 -0
- package/dist/core/tools/grep.js +304 -0
- package/dist/core/tools/grep.js.map +1 -0
- package/dist/core/tools/index.d.ts +40 -0
- package/dist/core/tools/index.d.ts.map +1 -0
- package/dist/core/tools/index.js +112 -0
- package/dist/core/tools/index.js.map +1 -0
- package/dist/core/tools/ls.d.ts +37 -0
- package/dist/core/tools/ls.d.ts.map +1 -0
- package/dist/core/tools/ls.js +167 -0
- package/dist/core/tools/ls.js.map +1 -0
- package/dist/core/tools/output-accumulator.d.ts +52 -0
- package/dist/core/tools/output-accumulator.d.ts.map +1 -0
- package/dist/core/tools/output-accumulator.js +184 -0
- package/dist/core/tools/output-accumulator.js.map +1 -0
- package/dist/core/tools/path-utils.d.ts +10 -0
- package/dist/core/tools/path-utils.d.ts.map +1 -0
- package/dist/core/tools/path-utils.js +99 -0
- package/dist/core/tools/path-utils.js.map +1 -0
- package/dist/core/tools/read.d.ts +35 -0
- package/dist/core/tools/read.d.ts.map +1 -0
- package/dist/core/tools/read.js +276 -0
- package/dist/core/tools/read.js.map +1 -0
- package/dist/core/tools/render-utils.d.ts +24 -0
- package/dist/core/tools/render-utils.d.ts.map +1 -0
- package/dist/core/tools/render-utils.js +65 -0
- package/dist/core/tools/render-utils.js.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts +14 -0
- package/dist/core/tools/tool-definition-wrapper.d.ts.map +1 -0
- package/dist/core/tools/tool-definition-wrapper.js +34 -0
- package/dist/core/tools/tool-definition-wrapper.js.map +1 -0
- package/dist/core/tools/truncate.d.ts +70 -0
- package/dist/core/tools/truncate.d.ts.map +1 -0
- package/dist/core/tools/truncate.js +215 -0
- package/dist/core/tools/truncate.js.map +1 -0
- package/dist/core/tools/write.d.ts +26 -0
- package/dist/core/tools/write.d.ts.map +1 -0
- package/dist/core/tools/write.js +197 -0
- package/dist/core/tools/write.js.map +1 -0
- package/dist/core/trust-manager.d.ts +36 -0
- package/dist/core/trust-manager.d.ts.map +1 -0
- package/dist/core/trust-manager.js +202 -0
- package/dist/core/trust-manager.js.map +1 -0
- package/dist/index.d.ts +33 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +46 -0
- package/dist/index.js.map +1 -0
- package/dist/main.d.ts +12 -0
- package/dist/main.d.ts.map +1 -0
- package/dist/main.js +699 -0
- package/dist/main.js.map +1 -0
- package/dist/migrations.d.ts +33 -0
- package/dist/migrations.d.ts.map +1 -0
- package/dist/migrations.js +281 -0
- package/dist/migrations.js.map +1 -0
- package/dist/modes/index.d.ts +9 -0
- package/dist/modes/index.d.ts.map +1 -0
- package/dist/modes/index.js +8 -0
- package/dist/modes/index.js.map +1 -0
- package/dist/modes/interactive/assets/clankolas.png +0 -0
- package/dist/modes/interactive/components/armin.d.ts +34 -0
- package/dist/modes/interactive/components/armin.d.ts.map +1 -0
- package/dist/modes/interactive/components/armin.js +333 -0
- package/dist/modes/interactive/components/armin.js.map +1 -0
- package/dist/modes/interactive/components/assistant-message.d.ts +22 -0
- package/dist/modes/interactive/components/assistant-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/assistant-message.js +129 -0
- package/dist/modes/interactive/components/assistant-message.js.map +1 -0
- package/dist/modes/interactive/components/bash-execution.d.ts +34 -0
- package/dist/modes/interactive/components/bash-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/bash-execution.js +175 -0
- package/dist/modes/interactive/components/bash-execution.js.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts +16 -0
- package/dist/modes/interactive/components/bordered-loader.d.ts.map +1 -0
- package/dist/modes/interactive/components/bordered-loader.js +54 -0
- package/dist/modes/interactive/components/bordered-loader.js.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts +16 -0
- package/dist/modes/interactive/components/branch-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/branch-summary-message.js +44 -0
- package/dist/modes/interactive/components/branch-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts +16 -0
- package/dist/modes/interactive/components/compaction-summary-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/compaction-summary-message.js +45 -0
- package/dist/modes/interactive/components/compaction-summary-message.js.map +1 -0
- package/dist/modes/interactive/components/config-selector.d.ts +71 -0
- package/dist/modes/interactive/components/config-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/config-selector.js +506 -0
- package/dist/modes/interactive/components/config-selector.js.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts +14 -0
- package/dist/modes/interactive/components/countdown-timer.d.ts.map +1 -0
- package/dist/modes/interactive/components/countdown-timer.js +33 -0
- package/dist/modes/interactive/components/countdown-timer.js.map +1 -0
- package/dist/modes/interactive/components/custom-editor.d.ts +21 -0
- package/dist/modes/interactive/components/custom-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-editor.js +70 -0
- package/dist/modes/interactive/components/custom-editor.js.map +1 -0
- package/dist/modes/interactive/components/custom-message.d.ts +20 -0
- package/dist/modes/interactive/components/custom-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/custom-message.js +79 -0
- package/dist/modes/interactive/components/custom-message.js.map +1 -0
- package/dist/modes/interactive/components/daxnuts.d.ts +23 -0
- package/dist/modes/interactive/components/daxnuts.d.ts.map +1 -0
- package/dist/modes/interactive/components/daxnuts.js +140 -0
- package/dist/modes/interactive/components/daxnuts.js.map +1 -0
- package/dist/modes/interactive/components/diff.d.ts +12 -0
- package/dist/modes/interactive/components/diff.d.ts.map +1 -0
- package/dist/modes/interactive/components/diff.js +133 -0
- package/dist/modes/interactive/components/diff.js.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts +15 -0
- package/dist/modes/interactive/components/dynamic-border.d.ts.map +1 -0
- package/dist/modes/interactive/components/dynamic-border.js +21 -0
- package/dist/modes/interactive/components/dynamic-border.js.map +1 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts +5 -0
- package/dist/modes/interactive/components/earendil-announcement.d.ts.map +1 -0
- package/dist/modes/interactive/components/earendil-announcement.js +40 -0
- package/dist/modes/interactive/components/earendil-announcement.js.map +1 -0
- package/dist/modes/interactive/components/extension-editor.d.ts +22 -0
- package/dist/modes/interactive/components/extension-editor.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-editor.js +128 -0
- package/dist/modes/interactive/components/extension-editor.js.map +1 -0
- package/dist/modes/interactive/components/extension-input.d.ts +23 -0
- package/dist/modes/interactive/components/extension-input.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-input.js +61 -0
- package/dist/modes/interactive/components/extension-input.js.map +1 -0
- package/dist/modes/interactive/components/extension-selector.d.ts +26 -0
- package/dist/modes/interactive/components/extension-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/extension-selector.js +83 -0
- package/dist/modes/interactive/components/extension-selector.js.map +1 -0
- package/dist/modes/interactive/components/first-time-setup.d.ts +25 -0
- package/dist/modes/interactive/components/first-time-setup.d.ts.map +1 -0
- package/dist/modes/interactive/components/first-time-setup.js +103 -0
- package/dist/modes/interactive/components/first-time-setup.js.map +1 -0
- package/dist/modes/interactive/components/footer.d.ts +28 -0
- package/dist/modes/interactive/components/footer.d.ts.map +1 -0
- package/dist/modes/interactive/components/footer.js +221 -0
- package/dist/modes/interactive/components/footer.js.map +1 -0
- package/dist/modes/interactive/components/index.d.ts +34 -0
- package/dist/modes/interactive/components/index.d.ts.map +1 -0
- package/dist/modes/interactive/components/index.js +35 -0
- package/dist/modes/interactive/components/index.js.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts +13 -0
- package/dist/modes/interactive/components/keybinding-hints.d.ts.map +1 -0
- package/dist/modes/interactive/components/keybinding-hints.js +36 -0
- package/dist/modes/interactive/components/keybinding-hints.js.map +1 -0
- package/dist/modes/interactive/components/login-dialog.d.ts +52 -0
- package/dist/modes/interactive/components/login-dialog.d.ts.map +1 -0
- package/dist/modes/interactive/components/login-dialog.js +179 -0
- package/dist/modes/interactive/components/login-dialog.js.map +1 -0
- package/dist/modes/interactive/components/model-selector.d.ts +47 -0
- package/dist/modes/interactive/components/model-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/model-selector.js +279 -0
- package/dist/modes/interactive/components/model-selector.js.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts +31 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/oauth-selector.js +165 -0
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts +42 -0
- package/dist/modes/interactive/components/scoped-models-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/scoped-models-selector.js +293 -0
- package/dist/modes/interactive/components/scoped-models-selector.js.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts +23 -0
- package/dist/modes/interactive/components/session-selector-search.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector-search.js +155 -0
- package/dist/modes/interactive/components/session-selector-search.js.map +1 -0
- package/dist/modes/interactive/components/session-selector.d.ts +95 -0
- package/dist/modes/interactive/components/session-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/session-selector.js +867 -0
- package/dist/modes/interactive/components/session-selector.js.map +1 -0
- package/dist/modes/interactive/components/settings-selector.d.ts +75 -0
- package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/settings-selector.js +582 -0
- package/dist/modes/interactive/components/settings-selector.js.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts +10 -0
- package/dist/modes/interactive/components/show-images-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/show-images-selector.js +39 -0
- package/dist/modes/interactive/components/show-images-selector.js.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts +17 -0
- package/dist/modes/interactive/components/skill-invocation-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/skill-invocation-message.js +47 -0
- package/dist/modes/interactive/components/skill-invocation-message.js.map +1 -0
- package/dist/modes/interactive/components/status-indicator.d.ts +28 -0
- package/dist/modes/interactive/components/status-indicator.d.ts.map +1 -0
- package/dist/modes/interactive/components/status-indicator.js +60 -0
- package/dist/modes/interactive/components/status-indicator.js.map +1 -0
- package/dist/modes/interactive/components/theme-selector.d.ts +11 -0
- package/dist/modes/interactive/components/theme-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/theme-selector.js +50 -0
- package/dist/modes/interactive/components/theme-selector.js.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts +11 -0
- package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/thinking-selector.js +51 -0
- package/dist/modes/interactive/components/thinking-selector.js.map +1 -0
- package/dist/modes/interactive/components/tool-execution.d.ts +63 -0
- package/dist/modes/interactive/components/tool-execution.d.ts.map +1 -0
- package/dist/modes/interactive/components/tool-execution.js +317 -0
- package/dist/modes/interactive/components/tool-execution.js.map +1 -0
- package/dist/modes/interactive/components/tree-selector.d.ts +89 -0
- package/dist/modes/interactive/components/tree-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/tree-selector.js +1208 -0
- package/dist/modes/interactive/components/tree-selector.js.map +1 -0
- package/dist/modes/interactive/components/trust-selector.d.ts +23 -0
- package/dist/modes/interactive/components/trust-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/trust-selector.js +91 -0
- package/dist/modes/interactive/components/trust-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts +30 -0
- package/dist/modes/interactive/components/user-message-selector.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message-selector.js +114 -0
- package/dist/modes/interactive/components/user-message-selector.js.map +1 -0
- package/dist/modes/interactive/components/user-message.d.ts +14 -0
- package/dist/modes/interactive/components/user-message.d.ts.map +1 -0
- package/dist/modes/interactive/components/user-message.js +42 -0
- package/dist/modes/interactive/components/user-message.js.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts +24 -0
- package/dist/modes/interactive/components/visual-truncate.d.ts.map +1 -0
- package/dist/modes/interactive/components/visual-truncate.js +33 -0
- package/dist/modes/interactive/components/visual-truncate.js.map +1 -0
- package/dist/modes/interactive/interactive-mode.d.ts +380 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -0
- package/dist/modes/interactive/interactive-mode.js +4782 -0
- package/dist/modes/interactive/interactive-mode.js.map +1 -0
- package/dist/modes/interactive/model-search.d.ts +12 -0
- package/dist/modes/interactive/model-search.d.ts.map +1 -0
- package/dist/modes/interactive/model-search.js +15 -0
- package/dist/modes/interactive/model-search.js.map +1 -0
- package/dist/modes/interactive/theme/dark.json +86 -0
- package/dist/modes/interactive/theme/light.json +85 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts +29 -0
- package/dist/modes/interactive/theme/theme-controller.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme-controller.js +102 -0
- package/dist/modes/interactive/theme/theme-controller.js.map +1 -0
- package/dist/modes/interactive/theme/theme-schema.json +336 -0
- package/dist/modes/interactive/theme/theme.d.ts +119 -0
- package/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/dist/modes/interactive/theme/theme.js +1056 -0
- package/dist/modes/interactive/theme/theme.js.map +1 -0
- package/dist/modes/print-mode.d.ts +28 -0
- package/dist/modes/print-mode.d.ts.map +1 -0
- package/dist/modes/print-mode.js +132 -0
- package/dist/modes/print-mode.js.map +1 -0
- package/dist/modes/rpc/jsonl.d.ts +17 -0
- package/dist/modes/rpc/jsonl.d.ts.map +1 -0
- package/dist/modes/rpc/jsonl.js +49 -0
- package/dist/modes/rpc/jsonl.js.map +1 -0
- package/dist/modes/rpc/rpc-client.d.ts +242 -0
- package/dist/modes/rpc/rpc-client.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-client.js +481 -0
- package/dist/modes/rpc/rpc-client.js.map +1 -0
- package/dist/modes/rpc/rpc-mode.d.ts +20 -0
- package/dist/modes/rpc/rpc-mode.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-mode.js +632 -0
- package/dist/modes/rpc/rpc-mode.js.map +1 -0
- package/dist/modes/rpc/rpc-types.d.ts +446 -0
- package/dist/modes/rpc/rpc-types.d.ts.map +1 -0
- package/dist/modes/rpc/rpc-types.js +8 -0
- package/dist/modes/rpc/rpc-types.js.map +1 -0
- package/dist/package-manager-cli.d.ts +8 -0
- package/dist/package-manager-cli.d.ts.map +1 -0
- package/dist/package-manager-cli.js +659 -0
- package/dist/package-manager-cli.js.map +1 -0
- package/dist/pi-client-cli.d.ts +3 -0
- package/dist/pi-client-cli.d.ts.map +1 -0
- package/dist/pi-client-cli.js +10 -0
- package/dist/pi-client-cli.js.map +1 -0
- package/dist/rpc-entry.d.ts +3 -0
- package/dist/rpc-entry.d.ts.map +1 -0
- package/dist/rpc-entry.js +10 -0
- package/dist/rpc-entry.js.map +1 -0
- package/dist/utils/ansi.d.ts +2 -0
- package/dist/utils/ansi.d.ts.map +1 -0
- package/dist/utils/ansi.js +52 -0
- package/dist/utils/ansi.js.map +1 -0
- package/dist/utils/changelog.d.ts +22 -0
- package/dist/utils/changelog.d.ts.map +1 -0
- package/dist/utils/changelog.js +165 -0
- package/dist/utils/changelog.js.map +1 -0
- package/dist/utils/child-process.d.ts +18 -0
- package/dist/utils/child-process.d.ts.map +1 -0
- package/dist/utils/child-process.js +106 -0
- package/dist/utils/child-process.js.map +1 -0
- package/dist/utils/clipboard-image.d.ts +11 -0
- package/dist/utils/clipboard-image.d.ts.map +1 -0
- package/dist/utils/clipboard-image.js +245 -0
- package/dist/utils/clipboard-image.js.map +1 -0
- package/dist/utils/clipboard-native.d.ts +10 -0
- package/dist/utils/clipboard-native.d.ts.map +1 -0
- package/dist/utils/clipboard-native.js +20 -0
- package/dist/utils/clipboard-native.js.map +1 -0
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.d.ts.map +1 -0
- package/dist/utils/clipboard.js +117 -0
- package/dist/utils/clipboard.js.map +1 -0
- package/dist/utils/deprecation.d.ts +4 -0
- package/dist/utils/deprecation.d.ts.map +1 -0
- package/dist/utils/deprecation.js +13 -0
- package/dist/utils/deprecation.js.map +1 -0
- package/dist/utils/exif-orientation.d.ts +5 -0
- package/dist/utils/exif-orientation.d.ts.map +1 -0
- package/dist/utils/exif-orientation.js +158 -0
- package/dist/utils/exif-orientation.js.map +1 -0
- package/dist/utils/frontmatter.d.ts +8 -0
- package/dist/utils/frontmatter.d.ts.map +1 -0
- package/dist/utils/frontmatter.js +26 -0
- package/dist/utils/frontmatter.js.map +1 -0
- package/dist/utils/fs-watch.d.ts +5 -0
- package/dist/utils/fs-watch.d.ts.map +1 -0
- package/dist/utils/fs-watch.js +25 -0
- package/dist/utils/fs-watch.js.map +1 -0
- package/dist/utils/git.d.ts +26 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +195 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/html.d.ts +7 -0
- package/dist/utils/html.d.ts.map +1 -0
- package/dist/utils/html.js +40 -0
- package/dist/utils/html.js.map +1 -0
- package/dist/utils/image-convert.d.ts +10 -0
- package/dist/utils/image-convert.d.ts.map +1 -0
- package/dist/utils/image-convert.js +45 -0
- package/dist/utils/image-convert.js.map +1 -0
- package/dist/utils/image-process.d.ts +18 -0
- package/dist/utils/image-process.d.ts.map +1 -0
- package/dist/utils/image-process.js +83 -0
- package/dist/utils/image-process.js.map +1 -0
- package/dist/utils/image-resize-core.d.ts +30 -0
- package/dist/utils/image-resize-core.d.ts.map +1 -0
- package/dist/utils/image-resize-core.js +124 -0
- package/dist/utils/image-resize-core.js.map +1 -0
- package/dist/utils/image-resize-worker.d.ts +2 -0
- package/dist/utils/image-resize-worker.d.ts.map +1 -0
- package/dist/utils/image-resize-worker.js +31 -0
- package/dist/utils/image-resize-worker.js.map +1 -0
- package/dist/utils/image-resize.d.ts +16 -0
- package/dist/utils/image-resize.d.ts.map +1 -0
- package/dist/utils/image-resize.js +97 -0
- package/dist/utils/image-resize.js.map +1 -0
- package/dist/utils/json.d.ts +3 -0
- package/dist/utils/json.d.ts.map +1 -0
- package/dist/utils/json.js +7 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/mime.d.ts +3 -0
- package/dist/utils/mime.d.ts.map +1 -0
- package/dist/utils/mime.js +110 -0
- package/dist/utils/mime.js.map +1 -0
- package/dist/utils/open-browser.d.ts +9 -0
- package/dist/utils/open-browser.d.ts.map +1 -0
- package/dist/utils/open-browser.js +22 -0
- package/dist/utils/open-browser.js.map +1 -0
- package/dist/utils/paths.d.ts +31 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +92 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/photon.d.ts +21 -0
- package/dist/utils/photon.d.ts.map +1 -0
- package/dist/utils/photon.js +121 -0
- package/dist/utils/photon.js.map +1 -0
- package/dist/utils/pi-user-agent.d.ts +2 -0
- package/dist/utils/pi-user-agent.d.ts.map +1 -0
- package/dist/utils/pi-user-agent.js +5 -0
- package/dist/utils/pi-user-agent.js.map +1 -0
- package/dist/utils/shell.d.ts +31 -0
- package/dist/utils/shell.d.ts.map +1 -0
- package/dist/utils/shell.js +202 -0
- package/dist/utils/shell.js.map +1 -0
- package/dist/utils/sleep.d.ts +5 -0
- package/dist/utils/sleep.d.ts.map +1 -0
- package/dist/utils/sleep.js +17 -0
- package/dist/utils/sleep.js.map +1 -0
- package/dist/utils/syntax-highlight.d.ts +12 -0
- package/dist/utils/syntax-highlight.d.ts.map +1 -0
- package/dist/utils/syntax-highlight.js +118 -0
- package/dist/utils/syntax-highlight.js.map +1 -0
- package/dist/utils/tools-manager.d.ts +3 -0
- package/dist/utils/tools-manager.d.ts.map +1 -0
- package/dist/utils/tools-manager.js +328 -0
- package/dist/utils/tools-manager.js.map +1 -0
- package/dist/utils/version-check.d.ts +15 -0
- package/dist/utils/version-check.d.ts.map +1 -0
- package/dist/utils/version-check.js +59 -0
- package/dist/utils/version-check.js.map +1 -0
- package/dist/utils/windows-self-update.d.ts +3 -0
- package/dist/utils/windows-self-update.d.ts.map +1 -0
- package/dist/utils/windows-self-update.js +77 -0
- package/dist/utils/windows-self-update.js.map +1 -0
- package/docs/compaction.md +396 -0
- package/docs/containerization.md +111 -0
- package/docs/custom-provider.md +737 -0
- package/docs/development.md +71 -0
- package/docs/docs.json +156 -0
- package/docs/extensions.md +2695 -0
- package/docs/images/doom-extension.png +0 -0
- package/docs/images/exy.png +0 -0
- package/docs/images/interactive-mode.png +0 -0
- package/docs/images/tree-view.png +0 -0
- package/docs/index.md +82 -0
- package/docs/json.md +82 -0
- package/docs/keybindings.md +197 -0
- package/docs/models.md +495 -0
- package/docs/packages.md +227 -0
- package/docs/prompt-templates.md +95 -0
- package/docs/providers.md +275 -0
- package/docs/quickstart.md +165 -0
- package/docs/rpc.md +1470 -0
- package/docs/sdk.md +1143 -0
- package/docs/security.md +59 -0
- package/docs/session-format.md +412 -0
- package/docs/sessions.md +145 -0
- package/docs/settings.md +318 -0
- package/docs/shell-aliases.md +13 -0
- package/docs/skills.md +231 -0
- package/docs/terminal-setup.md +142 -0
- package/docs/termux.md +127 -0
- package/docs/themes.md +295 -0
- package/docs/tmux.md +63 -0
- package/docs/tui.md +927 -0
- package/docs/usage.md +308 -0
- package/docs/windows.md +17 -0
- package/examples/README.md +25 -0
- package/examples/extensions/README.md +211 -0
- package/examples/extensions/auto-commit-on-exit.ts +49 -0
- package/examples/extensions/bash-spawn-hook.ts +30 -0
- package/examples/extensions/bookmark.ts +50 -0
- package/examples/extensions/border-status-editor.ts +150 -0
- package/examples/extensions/built-in-tool-renderer.ts +249 -0
- package/examples/extensions/claude-rules.ts +86 -0
- package/examples/extensions/commands.ts +72 -0
- package/examples/extensions/confirm-destructive.ts +59 -0
- package/examples/extensions/custom-compaction.ts +128 -0
- package/examples/extensions/custom-footer.ts +64 -0
- package/examples/extensions/custom-header.ts +73 -0
- package/examples/extensions/custom-provider-anthropic/index.ts +604 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +24 -0
- package/examples/extensions/custom-provider-anthropic/package.json +19 -0
- package/examples/extensions/custom-provider-gitlab-duo/index.ts +404 -0
- package/examples/extensions/custom-provider-gitlab-duo/package.json +16 -0
- package/examples/extensions/custom-provider-gitlab-duo/test.ts +82 -0
- package/examples/extensions/dirty-repo-guard.ts +56 -0
- package/examples/extensions/doom-overlay/README.md +46 -0
- package/examples/extensions/doom-overlay/doom/build/doom.js +21 -0
- package/examples/extensions/doom-overlay/doom/build/doom.wasm +0 -0
- package/examples/extensions/doom-overlay/doom/build.sh +152 -0
- package/examples/extensions/doom-overlay/doom/doomgeneric_pi.c +72 -0
- package/examples/extensions/doom-overlay/doom-component.ts +132 -0
- package/examples/extensions/doom-overlay/doom-engine.ts +173 -0
- package/examples/extensions/doom-overlay/doom-keys.ts +104 -0
- package/examples/extensions/doom-overlay/index.ts +74 -0
- package/examples/extensions/doom-overlay/wad-finder.ts +51 -0
- package/examples/extensions/dynamic-resources/SKILL.md +8 -0
- package/examples/extensions/dynamic-resources/dynamic.json +79 -0
- package/examples/extensions/dynamic-resources/dynamic.md +5 -0
- package/examples/extensions/dynamic-resources/index.ts +15 -0
- package/examples/extensions/dynamic-tools.ts +74 -0
- package/examples/extensions/event-bus.ts +43 -0
- package/examples/extensions/file-trigger.ts +41 -0
- package/examples/extensions/git-checkpoint.ts +53 -0
- package/examples/extensions/git-merge-and-resolve.ts +115 -0
- package/examples/extensions/github-issue-autocomplete.ts +185 -0
- package/examples/extensions/gondolin/index.ts +531 -0
- package/examples/extensions/gondolin/package-lock.json +185 -0
- package/examples/extensions/gondolin/package.json +19 -0
- package/examples/extensions/handoff.ts +191 -0
- package/examples/extensions/hello.ts +26 -0
- package/examples/extensions/hidden-thinking-label.ts +53 -0
- package/examples/extensions/inline-bash.ts +94 -0
- package/examples/extensions/input-transform-streaming.ts +39 -0
- package/examples/extensions/input-transform.ts +43 -0
- package/examples/extensions/interactive-shell.ts +196 -0
- package/examples/extensions/mac-system-theme.ts +47 -0
- package/examples/extensions/message-renderer.ts +59 -0
- package/examples/extensions/minimal-mode.ts +426 -0
- package/examples/extensions/modal-editor.ts +85 -0
- package/examples/extensions/model-status.ts +31 -0
- package/examples/extensions/notify.ts +55 -0
- package/examples/extensions/overlay-qa-tests.ts +1450 -0
- package/examples/extensions/overlay-test.ts +153 -0
- package/examples/extensions/permission-gate.ts +34 -0
- package/examples/extensions/pirate.ts +47 -0
- package/examples/extensions/plan-mode/README.md +66 -0
- package/examples/extensions/plan-mode/index.ts +390 -0
- package/examples/extensions/plan-mode/utils.ts +168 -0
- package/examples/extensions/preset.ts +436 -0
- package/examples/extensions/project-trust.ts +64 -0
- package/examples/extensions/prompt-customizer.ts +97 -0
- package/examples/extensions/protected-paths.ts +30 -0
- package/examples/extensions/provider-payload.ts +18 -0
- package/examples/extensions/qna.ts +122 -0
- package/examples/extensions/question.ts +285 -0
- package/examples/extensions/questionnaire.ts +448 -0
- package/examples/extensions/rainbow-editor.ts +88 -0
- package/examples/extensions/reload-runtime.ts +37 -0
- package/examples/extensions/rpc-demo.ts +118 -0
- package/examples/extensions/sandbox/index.ts +321 -0
- package/examples/extensions/sandbox/package-lock.json +92 -0
- package/examples/extensions/sandbox/package.json +19 -0
- package/examples/extensions/send-user-message.ts +97 -0
- package/examples/extensions/session-name.ts +27 -0
- package/examples/extensions/shutdown-command.ts +63 -0
- package/examples/extensions/snake.ts +343 -0
- package/examples/extensions/space-invaders.ts +560 -0
- package/examples/extensions/ssh.ts +220 -0
- package/examples/extensions/status-line.ts +32 -0
- package/examples/extensions/structured-output.ts +65 -0
- package/examples/extensions/subagent/README.md +175 -0
- package/examples/extensions/subagent/agents/planner.md +37 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/subagent/agents/scout.md +50 -0
- package/examples/extensions/subagent/agents/worker.md +24 -0
- package/examples/extensions/subagent/agents.ts +126 -0
- package/examples/extensions/subagent/index.ts +1015 -0
- package/examples/extensions/subagent/prompts/implement-and-review.md +10 -0
- package/examples/extensions/subagent/prompts/implement.md +10 -0
- package/examples/extensions/subagent/prompts/scout-and-plan.md +9 -0
- package/examples/extensions/summarize.ts +207 -0
- package/examples/extensions/system-prompt-header.ts +17 -0
- package/examples/extensions/tic-tac-toe.ts +1008 -0
- package/examples/extensions/timed-confirm.ts +70 -0
- package/examples/extensions/titlebar-spinner.ts +58 -0
- package/examples/extensions/todo.ts +297 -0
- package/examples/extensions/tool-override.ts +144 -0
- package/examples/extensions/tools.ts +146 -0
- package/examples/extensions/trigger-compact.ts +50 -0
- package/examples/extensions/truncated-tool.ts +195 -0
- package/examples/extensions/widget-placement.ts +9 -0
- package/examples/extensions/with-deps/index.ts +32 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +22 -0
- package/examples/extensions/working-indicator.ts +123 -0
- package/examples/extensions/working-message-test.ts +25 -0
- package/examples/rpc-extension-ui.ts +632 -0
- package/examples/sdk/01-minimal.ts +26 -0
- package/examples/sdk/02-custom-model.ts +53 -0
- package/examples/sdk/03-custom-prompt.ts +75 -0
- package/examples/sdk/04-skills.ts +55 -0
- package/examples/sdk/05-tools.ts +48 -0
- package/examples/sdk/06-extensions.ts +99 -0
- package/examples/sdk/07-context-files.ts +47 -0
- package/examples/sdk/08-prompt-templates.ts +51 -0
- package/examples/sdk/09-api-keys-and-oauth.ts +52 -0
- package/examples/sdk/10-settings.ts +53 -0
- package/examples/sdk/11-sessions.ts +52 -0
- package/examples/sdk/12-full-control.ts +77 -0
- package/examples/sdk/13-session-runtime.ts +67 -0
- package/examples/sdk/README.md +144 -0
- package/npm-shrinkwrap.json +1824 -0
- package/package.json +103 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { applyExifOrientation } from "./exif-orientation.js";
|
|
2
|
+
import { loadPhoton } from "./photon.js";
|
|
3
|
+
export async function convertImageBytesToPng(bytes) {
|
|
4
|
+
const photon = await loadPhoton();
|
|
5
|
+
if (!photon) {
|
|
6
|
+
// Photon not available, can't convert
|
|
7
|
+
return null;
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const rawImage = photon.PhotonImage.new_from_byteslice(bytes);
|
|
11
|
+
const image = applyExifOrientation(photon, rawImage, bytes);
|
|
12
|
+
if (image !== rawImage)
|
|
13
|
+
rawImage.free();
|
|
14
|
+
try {
|
|
15
|
+
return new Uint8Array(image.get_bytes());
|
|
16
|
+
}
|
|
17
|
+
finally {
|
|
18
|
+
image.free();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
catch {
|
|
22
|
+
// Conversion failed
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert image to PNG format for terminal display.
|
|
28
|
+
* Kitty graphics protocol requires PNG format (f=100).
|
|
29
|
+
*/
|
|
30
|
+
export async function convertToPng(base64Data, mimeType) {
|
|
31
|
+
// Already PNG, no conversion needed
|
|
32
|
+
if (mimeType === "image/png") {
|
|
33
|
+
return { data: base64Data, mimeType };
|
|
34
|
+
}
|
|
35
|
+
const bytes = new Uint8Array(Buffer.from(base64Data, "base64"));
|
|
36
|
+
const pngBytes = await convertImageBytesToPng(bytes);
|
|
37
|
+
if (!pngBytes) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
data: Buffer.from(pngBytes).toString("base64"),
|
|
42
|
+
mimeType: "image/png",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//# sourceMappingURL=image-convert.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-convert.js","sourceRoot":"","sources":["../../src/utils/image-convert.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAAC,KAAiB,EAA8B;IAC3F,MAAM,MAAM,GAAG,MAAM,UAAU,EAAE,CAAC;IAClC,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,sCAAsC;QACtC,OAAO,IAAI,CAAC;IACb,CAAC;IAED,IAAI,CAAC;QACJ,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAC9D,MAAM,KAAK,GAAG,oBAAoB,CAAC,MAAM,EAAE,QAAQ,EAAE,KAAK,CAAC,CAAC;QAC5D,IAAI,KAAK,KAAK,QAAQ;YAAE,QAAQ,CAAC,IAAI,EAAE,CAAC;QACxC,IAAI,CAAC;YACJ,OAAO,IAAI,UAAU,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC,CAAC;QAC1C,CAAC;gBAAS,CAAC;YACV,KAAK,CAAC,IAAI,EAAE,CAAC;QACd,CAAC;IACF,CAAC;IAAC,MAAM,CAAC;QACR,oBAAoB;QACpB,OAAO,IAAI,CAAC;IACb,CAAC;AAAA,CACD;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CACjC,UAAkB,EAClB,QAAgB,EACqC;IACrD,oCAAoC;IACpC,IAAI,QAAQ,KAAK,WAAW,EAAE,CAAC;QAC9B,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC;IACvC,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC;IAChE,MAAM,QAAQ,GAAG,MAAM,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACrD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACf,OAAO,IAAI,CAAC;IACb,CAAC;IAED,OAAO;QACN,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC9C,QAAQ,EAAE,WAAW;KACrB,CAAC;AAAA,CACF","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport async function convertImageBytesToPng(bytes: Uint8Array): Promise<Uint8Array | null> {\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\t// Photon not available, can't convert\n\t\treturn null;\n\t}\n\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(bytes);\n\t\tconst image = applyExifOrientation(photon, rawImage, bytes);\n\t\tif (image !== rawImage) rawImage.free();\n\t\ttry {\n\t\t\treturn new Uint8Array(image.get_bytes());\n\t\t} finally {\n\t\t\timage.free();\n\t\t}\n\t} catch {\n\t\t// Conversion failed\n\t\treturn null;\n\t}\n}\n\n/**\n * Convert image to PNG format for terminal display.\n * Kitty graphics protocol requires PNG format (f=100).\n */\nexport async function convertToPng(\n\tbase64Data: string,\n\tmimeType: string,\n): Promise<{ data: string; mimeType: string } | null> {\n\t// Already PNG, no conversion needed\n\tif (mimeType === \"image/png\") {\n\t\treturn { data: base64Data, mimeType };\n\t}\n\n\tconst bytes = new Uint8Array(Buffer.from(base64Data, \"base64\"));\n\tconst pngBytes = await convertImageBytesToPng(bytes);\n\tif (!pngBytes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tdata: Buffer.from(pngBytes).toString(\"base64\"),\n\t\tmimeType: \"image/png\",\n\t};\n}\n"]}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type ImageResizeOptions } from "./image-resize.ts";
|
|
2
|
+
export interface ProcessImageOptions {
|
|
3
|
+
/** Whether to resize images to inline provider limits. Default: true */
|
|
4
|
+
autoResizeImages?: boolean;
|
|
5
|
+
/** Optional resize overrides. Uses resizeImage defaults when omitted. */
|
|
6
|
+
resizeOptions?: ImageResizeOptions;
|
|
7
|
+
}
|
|
8
|
+
export type ProcessImageResult = {
|
|
9
|
+
ok: true;
|
|
10
|
+
data: string;
|
|
11
|
+
mimeType: string;
|
|
12
|
+
hints: string[];
|
|
13
|
+
} | {
|
|
14
|
+
ok: false;
|
|
15
|
+
message: string;
|
|
16
|
+
};
|
|
17
|
+
export declare function processImage(bytes: Uint8Array, mimeType: string, options?: ProcessImageOptions): Promise<ProcessImageResult>;
|
|
18
|
+
//# sourceMappingURL=image-process.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-process.d.ts","sourceRoot":"","sources":["../../src/utils/image-process.ts"],"names":[],"mappings":"AACA,OAAO,EAAuB,KAAK,kBAAkB,EAAe,MAAM,mBAAmB,CAAC;AAE9F,MAAM,WAAW,mBAAmB;IACnC,wEAAwE;IACxE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,yEAAyE;IACzE,aAAa,CAAC,EAAE,kBAAkB,CAAC;CACnC;AAED,MAAM,MAAM,kBAAkB,GAC3B;IACA,EAAE,EAAE,IAAI,CAAC;IACT,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,EAAE,CAAC;CACf,GACD;IACA,EAAE,EAAE,KAAK,CAAC;IACV,OAAO,EAAE,MAAM,CAAC;CACf,CAAC;AAmDL,wBAAsB,YAAY,CACjC,KAAK,EAAE,UAAU,EACjB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CA2C7B","sourcesContent":["import { convertImageBytesToPng } from \"./image-convert.ts\";\nimport { formatDimensionNote, type ImageResizeOptions, resizeImage } from \"./image-resize.ts\";\n\nexport interface ProcessImageOptions {\n\t/** Whether to resize images to inline provider limits. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Optional resize overrides. Uses resizeImage defaults when omitted. */\n\tresizeOptions?: ImageResizeOptions;\n}\n\nexport type ProcessImageResult =\n\t| {\n\t\t\tok: true;\n\t\t\tdata: string;\n\t\t\tmimeType: string;\n\t\t\thints: string[];\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\tmessage: string;\n\t };\n\ninterface NormalizedImage {\n\tbytes: Uint8Array;\n\tmimeType: string;\n\tconvertedFrom?: string;\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nfunction normalizeSupportedImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"image/png\";\n\t\tcase \"image/jpeg\":\n\t\tcase \"image/jpg\":\n\t\t\treturn \"image/jpeg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"image/gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"image/webp\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nasync function normalizeImage(bytes: Uint8Array, mimeType: string): Promise<NormalizedImage | null> {\n\tconst normalizedMimeType = normalizeSupportedImageMimeType(mimeType);\n\tif (normalizedMimeType) {\n\t\treturn { bytes, mimeType: normalizedMimeType };\n\t}\n\n\tconst pngBytes = await convertImageBytesToPng(bytes);\n\tif (!pngBytes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tbytes: pngBytes,\n\t\tmimeType: \"image/png\",\n\t\tconvertedFrom: baseMimeType(mimeType),\n\t};\n}\n\nfunction conversionHint(from: string | undefined, to: string): string | undefined {\n\tif (!from || from === to) return undefined;\n\treturn `[Image converted from ${from} to ${to}.]`;\n}\n\nexport async function processImage(\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ProcessImageOptions,\n): Promise<ProcessImageResult> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst normalized = await normalizeImage(bytes, mimeType);\n\tif (!normalized) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: \"[Image omitted: could not be converted to a supported inline image format.]\",\n\t\t};\n\t}\n\n\tif (autoResizeImages) {\n\t\tconst resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);\n\t\tif (!resized) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: \"[Image omitted: could not be resized below the inline image size limit.]\",\n\t\t\t};\n\t\t}\n\n\t\tconst hints: string[] = [];\n\t\tconst convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);\n\t\tif (convertedHint) hints.push(convertedHint);\n\t\tconst dimensionNote = formatDimensionNote(resized);\n\t\tif (dimensionNote) hints.push(dimensionNote);\n\n\t\treturn {\n\t\t\tok: true,\n\t\t\tdata: resized.data,\n\t\t\tmimeType: resized.mimeType,\n\t\t\thints,\n\t\t};\n\t}\n\n\tconst hints: string[] = [];\n\tconst convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);\n\tif (convertedHint) hints.push(convertedHint);\n\n\treturn {\n\t\tok: true,\n\t\tdata: Buffer.from(normalized.bytes).toString(\"base64\"),\n\t\tmimeType: normalized.mimeType,\n\t\thints,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { convertImageBytesToPng } from "./image-convert.js";
|
|
2
|
+
import { formatDimensionNote, resizeImage } from "./image-resize.js";
|
|
3
|
+
function baseMimeType(mimeType) {
|
|
4
|
+
return mimeType.split(";")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();
|
|
5
|
+
}
|
|
6
|
+
function normalizeSupportedImageMimeType(mimeType) {
|
|
7
|
+
switch (baseMimeType(mimeType)) {
|
|
8
|
+
case "image/png":
|
|
9
|
+
return "image/png";
|
|
10
|
+
case "image/jpeg":
|
|
11
|
+
case "image/jpg":
|
|
12
|
+
return "image/jpeg";
|
|
13
|
+
case "image/gif":
|
|
14
|
+
return "image/gif";
|
|
15
|
+
case "image/webp":
|
|
16
|
+
return "image/webp";
|
|
17
|
+
default:
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
async function normalizeImage(bytes, mimeType) {
|
|
22
|
+
const normalizedMimeType = normalizeSupportedImageMimeType(mimeType);
|
|
23
|
+
if (normalizedMimeType) {
|
|
24
|
+
return { bytes, mimeType: normalizedMimeType };
|
|
25
|
+
}
|
|
26
|
+
const pngBytes = await convertImageBytesToPng(bytes);
|
|
27
|
+
if (!pngBytes) {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
bytes: pngBytes,
|
|
32
|
+
mimeType: "image/png",
|
|
33
|
+
convertedFrom: baseMimeType(mimeType),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function conversionHint(from, to) {
|
|
37
|
+
if (!from || from === to)
|
|
38
|
+
return undefined;
|
|
39
|
+
return `[Image converted from ${from} to ${to}.]`;
|
|
40
|
+
}
|
|
41
|
+
export async function processImage(bytes, mimeType, options) {
|
|
42
|
+
const autoResizeImages = options?.autoResizeImages ?? true;
|
|
43
|
+
const normalized = await normalizeImage(bytes, mimeType);
|
|
44
|
+
if (!normalized) {
|
|
45
|
+
return {
|
|
46
|
+
ok: false,
|
|
47
|
+
message: "[Image omitted: could not be converted to a supported inline image format.]",
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
if (autoResizeImages) {
|
|
51
|
+
const resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);
|
|
52
|
+
if (!resized) {
|
|
53
|
+
return {
|
|
54
|
+
ok: false,
|
|
55
|
+
message: "[Image omitted: could not be resized below the inline image size limit.]",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const hints = [];
|
|
59
|
+
const convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);
|
|
60
|
+
if (convertedHint)
|
|
61
|
+
hints.push(convertedHint);
|
|
62
|
+
const dimensionNote = formatDimensionNote(resized);
|
|
63
|
+
if (dimensionNote)
|
|
64
|
+
hints.push(dimensionNote);
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
data: resized.data,
|
|
68
|
+
mimeType: resized.mimeType,
|
|
69
|
+
hints,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
const hints = [];
|
|
73
|
+
const convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);
|
|
74
|
+
if (convertedHint)
|
|
75
|
+
hints.push(convertedHint);
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
data: Buffer.from(normalized.bytes).toString("base64"),
|
|
79
|
+
mimeType: normalized.mimeType,
|
|
80
|
+
hints,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=image-process.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-process.js","sourceRoot":"","sources":["../../src/utils/image-process.ts"],"names":[],"mappings":"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","sourcesContent":["import { convertImageBytesToPng } from \"./image-convert.ts\";\nimport { formatDimensionNote, type ImageResizeOptions, resizeImage } from \"./image-resize.ts\";\n\nexport interface ProcessImageOptions {\n\t/** Whether to resize images to inline provider limits. Default: true */\n\tautoResizeImages?: boolean;\n\t/** Optional resize overrides. Uses resizeImage defaults when omitted. */\n\tresizeOptions?: ImageResizeOptions;\n}\n\nexport type ProcessImageResult =\n\t| {\n\t\t\tok: true;\n\t\t\tdata: string;\n\t\t\tmimeType: string;\n\t\t\thints: string[];\n\t }\n\t| {\n\t\t\tok: false;\n\t\t\tmessage: string;\n\t };\n\ninterface NormalizedImage {\n\tbytes: Uint8Array;\n\tmimeType: string;\n\tconvertedFrom?: string;\n}\n\nfunction baseMimeType(mimeType: string): string {\n\treturn mimeType.split(\";\")[0]?.trim().toLowerCase() ?? mimeType.toLowerCase();\n}\n\nfunction normalizeSupportedImageMimeType(mimeType: string): string | null {\n\tswitch (baseMimeType(mimeType)) {\n\t\tcase \"image/png\":\n\t\t\treturn \"image/png\";\n\t\tcase \"image/jpeg\":\n\t\tcase \"image/jpg\":\n\t\t\treturn \"image/jpeg\";\n\t\tcase \"image/gif\":\n\t\t\treturn \"image/gif\";\n\t\tcase \"image/webp\":\n\t\t\treturn \"image/webp\";\n\t\tdefault:\n\t\t\treturn null;\n\t}\n}\n\nasync function normalizeImage(bytes: Uint8Array, mimeType: string): Promise<NormalizedImage | null> {\n\tconst normalizedMimeType = normalizeSupportedImageMimeType(mimeType);\n\tif (normalizedMimeType) {\n\t\treturn { bytes, mimeType: normalizedMimeType };\n\t}\n\n\tconst pngBytes = await convertImageBytesToPng(bytes);\n\tif (!pngBytes) {\n\t\treturn null;\n\t}\n\n\treturn {\n\t\tbytes: pngBytes,\n\t\tmimeType: \"image/png\",\n\t\tconvertedFrom: baseMimeType(mimeType),\n\t};\n}\n\nfunction conversionHint(from: string | undefined, to: string): string | undefined {\n\tif (!from || from === to) return undefined;\n\treturn `[Image converted from ${from} to ${to}.]`;\n}\n\nexport async function processImage(\n\tbytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ProcessImageOptions,\n): Promise<ProcessImageResult> {\n\tconst autoResizeImages = options?.autoResizeImages ?? true;\n\tconst normalized = await normalizeImage(bytes, mimeType);\n\tif (!normalized) {\n\t\treturn {\n\t\t\tok: false,\n\t\t\tmessage: \"[Image omitted: could not be converted to a supported inline image format.]\",\n\t\t};\n\t}\n\n\tif (autoResizeImages) {\n\t\tconst resized = await resizeImage(normalized.bytes, normalized.mimeType, options?.resizeOptions);\n\t\tif (!resized) {\n\t\t\treturn {\n\t\t\t\tok: false,\n\t\t\t\tmessage: \"[Image omitted: could not be resized below the inline image size limit.]\",\n\t\t\t};\n\t\t}\n\n\t\tconst hints: string[] = [];\n\t\tconst convertedHint = conversionHint(normalized.convertedFrom, resized.mimeType);\n\t\tif (convertedHint) hints.push(convertedHint);\n\t\tconst dimensionNote = formatDimensionNote(resized);\n\t\tif (dimensionNote) hints.push(dimensionNote);\n\n\t\treturn {\n\t\t\tok: true,\n\t\t\tdata: resized.data,\n\t\t\tmimeType: resized.mimeType,\n\t\t\thints,\n\t\t};\n\t}\n\n\tconst hints: string[] = [];\n\tconst convertedHint = conversionHint(normalized.convertedFrom, normalized.mimeType);\n\tif (convertedHint) hints.push(convertedHint);\n\n\treturn {\n\t\tok: true,\n\t\tdata: Buffer.from(normalized.bytes).toString(\"base64\"),\n\t\tmimeType: normalized.mimeType,\n\t\thints,\n\t};\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface ImageResizeOptions {
|
|
2
|
+
maxWidth?: number;
|
|
3
|
+
maxHeight?: number;
|
|
4
|
+
maxBytes?: number;
|
|
5
|
+
jpegQuality?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface ResizedImage {
|
|
8
|
+
data: string;
|
|
9
|
+
mimeType: string;
|
|
10
|
+
originalWidth: number;
|
|
11
|
+
originalHeight: number;
|
|
12
|
+
width: number;
|
|
13
|
+
height: number;
|
|
14
|
+
wasResized: boolean;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
18
|
+
* Returns null if the image cannot be resized below maxBytes.
|
|
19
|
+
*
|
|
20
|
+
* Uses Photon (Rust/WASM) for image processing. If Photon is not available,
|
|
21
|
+
* returns null.
|
|
22
|
+
*
|
|
23
|
+
* Strategy for staying under maxBytes:
|
|
24
|
+
* 1. First resize to maxWidth/maxHeight
|
|
25
|
+
* 2. Try both PNG and JPEG formats, pick the smaller one
|
|
26
|
+
* 3. If still too large, try JPEG with decreasing quality
|
|
27
|
+
* 4. If still too large, progressively reduce dimensions until 1x1
|
|
28
|
+
*/
|
|
29
|
+
export declare function resizeImageInProcess(inputBytes: Uint8Array, mimeType: string, options?: ImageResizeOptions): Promise<ResizedImage | null>;
|
|
30
|
+
//# sourceMappingURL=image-resize-core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-resize-core.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,YAAY;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACpB;AA2BD;;;;;;;;;;;;GAYG;AACH,wBAAsB,oBAAoB,CACzC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqG9B","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport interface ImageResizeOptions {\n\tmaxWidth?: number; // Default: 2000\n\tmaxHeight?: number; // Default: 2000\n\tmaxBytes?: number; // Default: 4.5MB of base64 payload (below Anthropic's 5MB limit)\n\tjpegQuality?: number; // Default: 80\n}\n\nexport interface ResizedImage {\n\tdata: string; // base64\n\tmimeType: string;\n\toriginalWidth: number;\n\toriginalHeight: number;\n\twidth: number;\n\theight: number;\n\twasResized: boolean;\n}\n\n// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.\nconst DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;\n\nconst DEFAULT_OPTIONS: Required<ImageResizeOptions> = {\n\tmaxWidth: 2000,\n\tmaxHeight: 2000,\n\tmaxBytes: DEFAULT_MAX_BYTES,\n\tjpegQuality: 80,\n};\n\ninterface EncodedCandidate {\n\tdata: string;\n\tencodedSize: number;\n\tmimeType: string;\n}\n\nfunction encodeCandidate(buffer: Uint8Array, mimeType: string): EncodedCandidate {\n\tconst data = Buffer.from(buffer).toString(\"base64\");\n\treturn {\n\t\tdata,\n\t\tencodedSize: Buffer.byteLength(data, \"utf-8\"),\n\t\tmimeType,\n\t};\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Returns null if the image cannot be resized below maxBytes.\n *\n * Uses Photon (Rust/WASM) for image processing. If Photon is not available,\n * returns null.\n *\n * Strategy for staying under maxBytes:\n * 1. First resize to maxWidth/maxHeight\n * 2. Try both PNG and JPEG formats, pick the smaller one\n * 3. If still too large, try JPEG with decreasing quality\n * 4. If still too large, progressively reduce dimensions until 1x1\n */\nexport async function resizeImageInProcess(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst opts = { ...DEFAULT_OPTIONS, ...options };\n\tconst inputBase64Size = Math.ceil(inputBytes.byteLength / 3) * 4;\n\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\tlet image: ReturnType<typeof photon.PhotonImage.new_from_byteslice> | undefined;\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);\n\t\timage = applyExifOrientation(photon, rawImage, inputBytes);\n\t\tif (image !== rawImage) rawImage.free();\n\n\t\tconst originalWidth = image.get_width();\n\t\tconst originalHeight = image.get_height();\n\t\tconst format = mimeType.split(\"/\")[1] ?? \"png\";\n\n\t\t// Check if already within all limits (dimensions AND encoded size)\n\t\tif (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {\n\t\t\treturn {\n\t\t\t\tdata: Buffer.from(inputBytes).toString(\"base64\"),\n\t\t\t\tmimeType: mimeType || `image/${format}`,\n\t\t\t\toriginalWidth,\n\t\t\t\toriginalHeight,\n\t\t\t\twidth: originalWidth,\n\t\t\t\theight: originalHeight,\n\t\t\t\twasResized: false,\n\t\t\t};\n\t\t}\n\n\t\t// Calculate initial dimensions respecting max limits\n\t\tlet targetWidth = originalWidth;\n\t\tlet targetHeight = originalHeight;\n\n\t\tif (targetWidth > opts.maxWidth) {\n\t\t\ttargetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);\n\t\t\ttargetWidth = opts.maxWidth;\n\t\t}\n\t\tif (targetHeight > opts.maxHeight) {\n\t\t\ttargetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);\n\t\t\ttargetHeight = opts.maxHeight;\n\t\t}\n\n\t\tfunction tryEncodings(width: number, height: number, jpegQualities: number[]): EncodedCandidate[] {\n\t\t\tconst resized = photon!.resize(image!, width, height, photon!.SamplingFilter.Lanczos3);\n\n\t\t\ttry {\n\t\t\t\tconst candidates: EncodedCandidate[] = [encodeCandidate(resized.get_bytes(), \"image/png\")];\n\t\t\t\tfor (const quality of jpegQualities) {\n\t\t\t\t\tcandidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), \"image/jpeg\"));\n\t\t\t\t}\n\t\t\t\treturn candidates;\n\t\t\t} finally {\n\t\t\t\tresized.free();\n\t\t\t}\n\t\t}\n\n\t\tconst qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));\n\t\tlet currentWidth = targetWidth;\n\t\tlet currentHeight = targetHeight;\n\n\t\twhile (true) {\n\t\t\tconst candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tif (candidate.encodedSize < opts.maxBytes) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: candidate.data,\n\t\t\t\t\t\tmimeType: candidate.mimeType,\n\t\t\t\t\t\toriginalWidth,\n\t\t\t\t\t\toriginalHeight,\n\t\t\t\t\t\twidth: currentWidth,\n\t\t\t\t\t\theight: currentHeight,\n\t\t\t\t\t\twasResized: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentWidth === 1 && currentHeight === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));\n\t\t\tconst nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));\n\t\t\tif (nextWidth === currentWidth && nextHeight === currentHeight) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcurrentWidth = nextWidth;\n\t\t\tcurrentHeight = nextHeight;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tif (image) {\n\t\t\timage.free();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { applyExifOrientation } from "./exif-orientation.js";
|
|
2
|
+
import { loadPhoton } from "./photon.js";
|
|
3
|
+
// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.
|
|
4
|
+
const DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;
|
|
5
|
+
const DEFAULT_OPTIONS = {
|
|
6
|
+
maxWidth: 2000,
|
|
7
|
+
maxHeight: 2000,
|
|
8
|
+
maxBytes: DEFAULT_MAX_BYTES,
|
|
9
|
+
jpegQuality: 80,
|
|
10
|
+
};
|
|
11
|
+
function encodeCandidate(buffer, mimeType) {
|
|
12
|
+
const data = Buffer.from(buffer).toString("base64");
|
|
13
|
+
return {
|
|
14
|
+
data,
|
|
15
|
+
encodedSize: Buffer.byteLength(data, "utf-8"),
|
|
16
|
+
mimeType,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
21
|
+
* Returns null if the image cannot be resized below maxBytes.
|
|
22
|
+
*
|
|
23
|
+
* Uses Photon (Rust/WASM) for image processing. If Photon is not available,
|
|
24
|
+
* returns null.
|
|
25
|
+
*
|
|
26
|
+
* Strategy for staying under maxBytes:
|
|
27
|
+
* 1. First resize to maxWidth/maxHeight
|
|
28
|
+
* 2. Try both PNG and JPEG formats, pick the smaller one
|
|
29
|
+
* 3. If still too large, try JPEG with decreasing quality
|
|
30
|
+
* 4. If still too large, progressively reduce dimensions until 1x1
|
|
31
|
+
*/
|
|
32
|
+
export async function resizeImageInProcess(inputBytes, mimeType, options) {
|
|
33
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
34
|
+
const inputBase64Size = Math.ceil(inputBytes.byteLength / 3) * 4;
|
|
35
|
+
const photon = await loadPhoton();
|
|
36
|
+
if (!photon) {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
let image;
|
|
40
|
+
try {
|
|
41
|
+
const rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);
|
|
42
|
+
image = applyExifOrientation(photon, rawImage, inputBytes);
|
|
43
|
+
if (image !== rawImage)
|
|
44
|
+
rawImage.free();
|
|
45
|
+
const originalWidth = image.get_width();
|
|
46
|
+
const originalHeight = image.get_height();
|
|
47
|
+
const format = mimeType.split("/")[1] ?? "png";
|
|
48
|
+
// Check if already within all limits (dimensions AND encoded size)
|
|
49
|
+
if (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {
|
|
50
|
+
return {
|
|
51
|
+
data: Buffer.from(inputBytes).toString("base64"),
|
|
52
|
+
mimeType: mimeType || `image/${format}`,
|
|
53
|
+
originalWidth,
|
|
54
|
+
originalHeight,
|
|
55
|
+
width: originalWidth,
|
|
56
|
+
height: originalHeight,
|
|
57
|
+
wasResized: false,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// Calculate initial dimensions respecting max limits
|
|
61
|
+
let targetWidth = originalWidth;
|
|
62
|
+
let targetHeight = originalHeight;
|
|
63
|
+
if (targetWidth > opts.maxWidth) {
|
|
64
|
+
targetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);
|
|
65
|
+
targetWidth = opts.maxWidth;
|
|
66
|
+
}
|
|
67
|
+
if (targetHeight > opts.maxHeight) {
|
|
68
|
+
targetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);
|
|
69
|
+
targetHeight = opts.maxHeight;
|
|
70
|
+
}
|
|
71
|
+
function tryEncodings(width, height, jpegQualities) {
|
|
72
|
+
const resized = photon.resize(image, width, height, photon.SamplingFilter.Lanczos3);
|
|
73
|
+
try {
|
|
74
|
+
const candidates = [encodeCandidate(resized.get_bytes(), "image/png")];
|
|
75
|
+
for (const quality of jpegQualities) {
|
|
76
|
+
candidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), "image/jpeg"));
|
|
77
|
+
}
|
|
78
|
+
return candidates;
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
resized.free();
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
const qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));
|
|
85
|
+
let currentWidth = targetWidth;
|
|
86
|
+
let currentHeight = targetHeight;
|
|
87
|
+
while (true) {
|
|
88
|
+
const candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);
|
|
89
|
+
for (const candidate of candidates) {
|
|
90
|
+
if (candidate.encodedSize < opts.maxBytes) {
|
|
91
|
+
return {
|
|
92
|
+
data: candidate.data,
|
|
93
|
+
mimeType: candidate.mimeType,
|
|
94
|
+
originalWidth,
|
|
95
|
+
originalHeight,
|
|
96
|
+
width: currentWidth,
|
|
97
|
+
height: currentHeight,
|
|
98
|
+
wasResized: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (currentWidth === 1 && currentHeight === 1) {
|
|
103
|
+
break;
|
|
104
|
+
}
|
|
105
|
+
const nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));
|
|
106
|
+
const nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));
|
|
107
|
+
if (nextWidth === currentWidth && nextHeight === currentHeight) {
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
currentWidth = nextWidth;
|
|
111
|
+
currentHeight = nextHeight;
|
|
112
|
+
}
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
catch {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
finally {
|
|
119
|
+
if (image) {
|
|
120
|
+
image.free();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
//# sourceMappingURL=image-resize-core.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-resize-core.js","sourceRoot":"","sources":["../../src/utils/image-resize-core.ts"],"names":[],"mappings":"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","sourcesContent":["import { applyExifOrientation } from \"./exif-orientation.ts\";\nimport { loadPhoton } from \"./photon.ts\";\n\nexport interface ImageResizeOptions {\n\tmaxWidth?: number; // Default: 2000\n\tmaxHeight?: number; // Default: 2000\n\tmaxBytes?: number; // Default: 4.5MB of base64 payload (below Anthropic's 5MB limit)\n\tjpegQuality?: number; // Default: 80\n}\n\nexport interface ResizedImage {\n\tdata: string; // base64\n\tmimeType: string;\n\toriginalWidth: number;\n\toriginalHeight: number;\n\twidth: number;\n\theight: number;\n\twasResized: boolean;\n}\n\n// 4.5MB of base64 payload. Provides headroom below Anthropic's 5MB limit.\nconst DEFAULT_MAX_BYTES = 4.5 * 1024 * 1024;\n\nconst DEFAULT_OPTIONS: Required<ImageResizeOptions> = {\n\tmaxWidth: 2000,\n\tmaxHeight: 2000,\n\tmaxBytes: DEFAULT_MAX_BYTES,\n\tjpegQuality: 80,\n};\n\ninterface EncodedCandidate {\n\tdata: string;\n\tencodedSize: number;\n\tmimeType: string;\n}\n\nfunction encodeCandidate(buffer: Uint8Array, mimeType: string): EncodedCandidate {\n\tconst data = Buffer.from(buffer).toString(\"base64\");\n\treturn {\n\t\tdata,\n\t\tencodedSize: Buffer.byteLength(data, \"utf-8\"),\n\t\tmimeType,\n\t};\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Returns null if the image cannot be resized below maxBytes.\n *\n * Uses Photon (Rust/WASM) for image processing. If Photon is not available,\n * returns null.\n *\n * Strategy for staying under maxBytes:\n * 1. First resize to maxWidth/maxHeight\n * 2. Try both PNG and JPEG formats, pick the smaller one\n * 3. If still too large, try JPEG with decreasing quality\n * 4. If still too large, progressively reduce dimensions until 1x1\n */\nexport async function resizeImageInProcess(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst opts = { ...DEFAULT_OPTIONS, ...options };\n\tconst inputBase64Size = Math.ceil(inputBytes.byteLength / 3) * 4;\n\n\tconst photon = await loadPhoton();\n\tif (!photon) {\n\t\treturn null;\n\t}\n\n\tlet image: ReturnType<typeof photon.PhotonImage.new_from_byteslice> | undefined;\n\ttry {\n\t\tconst rawImage = photon.PhotonImage.new_from_byteslice(inputBytes);\n\t\timage = applyExifOrientation(photon, rawImage, inputBytes);\n\t\tif (image !== rawImage) rawImage.free();\n\n\t\tconst originalWidth = image.get_width();\n\t\tconst originalHeight = image.get_height();\n\t\tconst format = mimeType.split(\"/\")[1] ?? \"png\";\n\n\t\t// Check if already within all limits (dimensions AND encoded size)\n\t\tif (originalWidth <= opts.maxWidth && originalHeight <= opts.maxHeight && inputBase64Size < opts.maxBytes) {\n\t\t\treturn {\n\t\t\t\tdata: Buffer.from(inputBytes).toString(\"base64\"),\n\t\t\t\tmimeType: mimeType || `image/${format}`,\n\t\t\t\toriginalWidth,\n\t\t\t\toriginalHeight,\n\t\t\t\twidth: originalWidth,\n\t\t\t\theight: originalHeight,\n\t\t\t\twasResized: false,\n\t\t\t};\n\t\t}\n\n\t\t// Calculate initial dimensions respecting max limits\n\t\tlet targetWidth = originalWidth;\n\t\tlet targetHeight = originalHeight;\n\n\t\tif (targetWidth > opts.maxWidth) {\n\t\t\ttargetHeight = Math.round((targetHeight * opts.maxWidth) / targetWidth);\n\t\t\ttargetWidth = opts.maxWidth;\n\t\t}\n\t\tif (targetHeight > opts.maxHeight) {\n\t\t\ttargetWidth = Math.round((targetWidth * opts.maxHeight) / targetHeight);\n\t\t\ttargetHeight = opts.maxHeight;\n\t\t}\n\n\t\tfunction tryEncodings(width: number, height: number, jpegQualities: number[]): EncodedCandidate[] {\n\t\t\tconst resized = photon!.resize(image!, width, height, photon!.SamplingFilter.Lanczos3);\n\n\t\t\ttry {\n\t\t\t\tconst candidates: EncodedCandidate[] = [encodeCandidate(resized.get_bytes(), \"image/png\")];\n\t\t\t\tfor (const quality of jpegQualities) {\n\t\t\t\t\tcandidates.push(encodeCandidate(resized.get_bytes_jpeg(quality), \"image/jpeg\"));\n\t\t\t\t}\n\t\t\t\treturn candidates;\n\t\t\t} finally {\n\t\t\t\tresized.free();\n\t\t\t}\n\t\t}\n\n\t\tconst qualitySteps = Array.from(new Set([opts.jpegQuality, 85, 70, 55, 40]));\n\t\tlet currentWidth = targetWidth;\n\t\tlet currentHeight = targetHeight;\n\n\t\twhile (true) {\n\t\t\tconst candidates = tryEncodings(currentWidth, currentHeight, qualitySteps);\n\t\t\tfor (const candidate of candidates) {\n\t\t\t\tif (candidate.encodedSize < opts.maxBytes) {\n\t\t\t\t\treturn {\n\t\t\t\t\t\tdata: candidate.data,\n\t\t\t\t\t\tmimeType: candidate.mimeType,\n\t\t\t\t\t\toriginalWidth,\n\t\t\t\t\t\toriginalHeight,\n\t\t\t\t\t\twidth: currentWidth,\n\t\t\t\t\t\theight: currentHeight,\n\t\t\t\t\t\twasResized: true,\n\t\t\t\t\t};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (currentWidth === 1 && currentHeight === 1) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tconst nextWidth = currentWidth === 1 ? 1 : Math.max(1, Math.floor(currentWidth * 0.75));\n\t\t\tconst nextHeight = currentHeight === 1 ? 1 : Math.max(1, Math.floor(currentHeight * 0.75));\n\t\t\tif (nextWidth === currentWidth && nextHeight === currentHeight) {\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcurrentWidth = nextWidth;\n\t\t\tcurrentHeight = nextHeight;\n\t\t}\n\n\t\treturn null;\n\t} catch {\n\t\treturn null;\n\t} finally {\n\t\tif (image) {\n\t\t\timage.free();\n\t\t}\n\t}\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-resize-worker.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize-worker.ts"],"names":[],"mappings":"","sourcesContent":["import { parentPort } from \"node:worker_threads\";\nimport { type ImageResizeOptions, type ResizedImage, resizeImageInProcess } from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerRequest {\n\tinputBytes: Uint8Array;\n\tmimeType: string;\n\toptions?: ImageResizeOptions;\n}\n\ninterface ResizeImageWorkerResponse {\n\tresult?: ResizedImage | null;\n\terror?: string;\n}\n\nfunction isResizeImageWorkerRequest(value: unknown): value is ResizeImageWorkerRequest {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Record<string, unknown>;\n\treturn record.inputBytes instanceof Uint8Array && typeof record.mimeType === \"string\";\n}\n\nconst port = parentPort;\nif (!port) {\n\tthrow new Error(\"image resize worker requires parentPort\");\n}\n\nport.once(\"message\", (message: unknown) => {\n\tvoid (async () => {\n\t\ttry {\n\t\t\tif (!isResizeImageWorkerRequest(message)) {\n\t\t\t\tthrow new Error(\"Invalid image resize worker request\");\n\t\t\t}\n\t\t\tconst result = await resizeImageInProcess(message.inputBytes, message.mimeType, message.options);\n\t\t\tconst response: ResizeImageWorkerResponse = { result };\n\t\t\tport.postMessage(response);\n\t\t} catch (error) {\n\t\t\tconst response: ResizeImageWorkerResponse = {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t\tport.postMessage(response);\n\t\t}\n\t})();\n});\n"]}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { parentPort } from "node:worker_threads";
|
|
2
|
+
import { resizeImageInProcess } from "./image-resize-core.js";
|
|
3
|
+
function isResizeImageWorkerRequest(value) {
|
|
4
|
+
if (!value || typeof value !== "object")
|
|
5
|
+
return false;
|
|
6
|
+
const record = value;
|
|
7
|
+
return record.inputBytes instanceof Uint8Array && typeof record.mimeType === "string";
|
|
8
|
+
}
|
|
9
|
+
const port = parentPort;
|
|
10
|
+
if (!port) {
|
|
11
|
+
throw new Error("image resize worker requires parentPort");
|
|
12
|
+
}
|
|
13
|
+
port.once("message", (message) => {
|
|
14
|
+
void (async () => {
|
|
15
|
+
try {
|
|
16
|
+
if (!isResizeImageWorkerRequest(message)) {
|
|
17
|
+
throw new Error("Invalid image resize worker request");
|
|
18
|
+
}
|
|
19
|
+
const result = await resizeImageInProcess(message.inputBytes, message.mimeType, message.options);
|
|
20
|
+
const response = { result };
|
|
21
|
+
port.postMessage(response);
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
const response = {
|
|
25
|
+
error: error instanceof Error ? error.message : String(error),
|
|
26
|
+
};
|
|
27
|
+
port.postMessage(response);
|
|
28
|
+
}
|
|
29
|
+
})();
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=image-resize-worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-resize-worker.js","sourceRoot":"","sources":["../../src/utils/image-resize-worker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAA8C,oBAAoB,EAAE,MAAM,wBAAwB,CAAC;AAa1G,SAAS,0BAA0B,CAAC,KAAc,EAAqC;IACtF,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,MAAM,GAAG,KAAgC,CAAC;IAChD,OAAO,MAAM,CAAC,UAAU,YAAY,UAAU,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,QAAQ,CAAC;AAAA,CACtF;AAED,MAAM,IAAI,GAAG,UAAU,CAAC;AACxB,IAAI,CAAC,IAAI,EAAE,CAAC;IACX,MAAM,IAAI,KAAK,CAAC,yCAAyC,CAAC,CAAC;AAC5D,CAAC;AAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC,OAAgB,EAAE,EAAE,CAAC;IAC1C,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC;QACjB,IAAI,CAAC;YACJ,IAAI,CAAC,0BAA0B,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC1C,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;YACxD,CAAC;YACD,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;YACjG,MAAM,QAAQ,GAA8B,EAAE,MAAM,EAAE,CAAC;YACvD,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YAChB,MAAM,QAAQ,GAA8B;gBAC3C,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;aAC7D,CAAC;YACF,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QAC5B,CAAC;IAAA,CACD,CAAC,EAAE,CAAC;AAAA,CACL,CAAC,CAAC","sourcesContent":["import { parentPort } from \"node:worker_threads\";\nimport { type ImageResizeOptions, type ResizedImage, resizeImageInProcess } from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerRequest {\n\tinputBytes: Uint8Array;\n\tmimeType: string;\n\toptions?: ImageResizeOptions;\n}\n\ninterface ResizeImageWorkerResponse {\n\tresult?: ResizedImage | null;\n\terror?: string;\n}\n\nfunction isResizeImageWorkerRequest(value: unknown): value is ResizeImageWorkerRequest {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst record = value as Record<string, unknown>;\n\treturn record.inputBytes instanceof Uint8Array && typeof record.mimeType === \"string\";\n}\n\nconst port = parentPort;\nif (!port) {\n\tthrow new Error(\"image resize worker requires parentPort\");\n}\n\nport.once(\"message\", (message: unknown) => {\n\tvoid (async () => {\n\t\ttry {\n\t\t\tif (!isResizeImageWorkerRequest(message)) {\n\t\t\t\tthrow new Error(\"Invalid image resize worker request\");\n\t\t\t}\n\t\t\tconst result = await resizeImageInProcess(message.inputBytes, message.mimeType, message.options);\n\t\t\tconst response: ResizeImageWorkerResponse = { result };\n\t\t\tport.postMessage(response);\n\t\t} catch (error) {\n\t\t\tconst response: ResizeImageWorkerResponse = {\n\t\t\t\terror: error instanceof Error ? error.message : String(error),\n\t\t\t};\n\t\t\tport.postMessage(response);\n\t\t}\n\t})();\n});\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ImageResizeOptions, type ResizedImage } from "./image-resize-core.ts";
|
|
2
|
+
export type { ImageResizeOptions, ResizedImage } from "./image-resize-core.ts";
|
|
3
|
+
/**
|
|
4
|
+
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
5
|
+
* Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not
|
|
6
|
+
* block the TUI event loop. If the worker cannot be loaded (for example in some
|
|
7
|
+
* Bun compiled executable layouts), fall back to in-process resizing so image
|
|
8
|
+
* reads still work.
|
|
9
|
+
*/
|
|
10
|
+
export declare function resizeImage(inputBytes: Uint8Array, mimeType: string, options?: ImageResizeOptions): Promise<ResizedImage | null>;
|
|
11
|
+
/**
|
|
12
|
+
* Format a dimension note for resized images.
|
|
13
|
+
* This helps the model understand the coordinate mapping.
|
|
14
|
+
*/
|
|
15
|
+
export declare function formatDimensionNote(result: ResizedImage): string | undefined;
|
|
16
|
+
//# sourceMappingURL=image-resize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-resize.d.ts","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,YAAY,EAAwB,MAAM,wBAAwB,CAAC;AAE1G,YAAY,EAAE,kBAAkB,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AA0E/E;;;;;;GAMG;AACH,wBAAsB,WAAW,CAChC,UAAU,EAAE,UAAU,EACtB,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,kBAAkB,GAC1B,OAAO,CAAC,YAAY,GAAG,IAAI,CAAC,CAqB9B;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,YAAY,GAAG,MAAM,GAAG,SAAS,CAO5E","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport { type ImageResizeOptions, type ResizedImage, resizeImageInProcess } from \"./image-resize-core.ts\";\n\nexport type { ImageResizeOptions, ResizedImage } from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerResponse {\n\tresult?: ResizedImage | null;\n\terror?: string;\n}\n\nfunction toTransferableBytes(input: Uint8Array): Uint8Array<ArrayBuffer> {\n\t// Transfer detaches the buffer, so transfer a worker-owned copy and leave the\n\t// caller's bytes intact.\n\treturn new Uint8Array(input);\n}\n\nfunction isResizeImageWorkerResponse(value: unknown): value is ResizeImageWorkerResponse {\n\treturn value !== null && typeof value === \"object\";\n}\n\nfunction createResizeWorker(workerSpecifier: string | URL): Worker {\n\treturn new Worker(workerSpecifier);\n}\n\nasync function resizeImageInWorker(\n\tworkerSpecifier: string | URL,\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst worker = createResizeWorker(workerSpecifier);\n\ttry {\n\t\tconst inputBytesForWorker = toTransferableBytes(inputBytes);\n\t\treturn await new Promise<ResizedImage | null>((resolve, reject) => {\n\t\t\tlet settled = false;\n\t\t\tconst settle = (result: ResizedImage | null): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve(result);\n\t\t\t};\n\t\t\tconst fail = (error: Error): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\treject(error);\n\t\t\t};\n\n\t\t\tworker.once(\"message\", (message: unknown) => {\n\t\t\t\tif (!isResizeImageWorkerResponse(message)) {\n\t\t\t\t\tfail(new Error(\"Invalid image resize worker response\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (message.error) {\n\t\t\t\t\tfail(new Error(message.error));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettle(message.result ?? null);\n\t\t\t});\n\t\t\tworker.once(\"error\", fail);\n\t\t\tworker.once(\"exit\", (code) => {\n\t\t\t\tif (!settled) {\n\t\t\t\t\tfail(new Error(`Image resize worker exited with code ${code}`));\n\t\t\t\t}\n\t\t\t});\n\t\t\tworker.postMessage(\n\t\t\t\t{\n\t\t\t\t\tinputBytes: inputBytesForWorker,\n\t\t\t\t\tmimeType,\n\t\t\t\t\toptions,\n\t\t\t\t},\n\t\t\t\t[inputBytesForWorker.buffer],\n\t\t\t);\n\t\t});\n\t} finally {\n\t\tvoid worker.terminate().catch(() => undefined);\n\t}\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not\n * block the TUI event loop. If the worker cannot be loaded (for example in some\n * Bun compiled executable layouts), fall back to in-process resizing so image\n * reads still work.\n */\nexport async function resizeImage(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst isTypeScriptRuntime = import.meta.url.endsWith(\".ts\");\n\tconst workerUrl = new URL(\n\t\tisTypeScriptRuntime ? \"./image-resize-worker.ts\" : \"./image-resize-worker.js\",\n\t\timport.meta.url,\n\t);\n\n\t// Bun compiled executables resolve worker entrypoints by string path, not via\n\t// new URL(..., import.meta.url). Try the string path first under Bun so the\n\t// release binary uses the embedded worker instead of falling back in-process.\n\tif (typeof process.versions.bun === \"string\") {\n\t\ttry {\n\t\t\treturn await resizeImageInWorker(\"./src/utils/image-resize-worker.ts\", inputBytes, mimeType, options);\n\t\t} catch {}\n\t}\n\n\ttry {\n\t\treturn await resizeImageInWorker(workerUrl, inputBytes, mimeType, options);\n\t} catch {\n\t\treturn resizeImageInProcess(inputBytes, mimeType, options);\n\t}\n}\n\n/**\n * Format a dimension note for resized images.\n * This helps the model understand the coordinate mapping.\n */\nexport function formatDimensionNote(result: ResizedImage): string | undefined {\n\tif (!result.wasResized) {\n\t\treturn undefined;\n\t}\n\n\tconst scale = result.originalWidth / result.width;\n\treturn `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;\n}\n"]}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Worker } from "node:worker_threads";
|
|
2
|
+
import { resizeImageInProcess } from "./image-resize-core.js";
|
|
3
|
+
function toTransferableBytes(input) {
|
|
4
|
+
// Transfer detaches the buffer, so transfer a worker-owned copy and leave the
|
|
5
|
+
// caller's bytes intact.
|
|
6
|
+
return new Uint8Array(input);
|
|
7
|
+
}
|
|
8
|
+
function isResizeImageWorkerResponse(value) {
|
|
9
|
+
return value !== null && typeof value === "object";
|
|
10
|
+
}
|
|
11
|
+
function createResizeWorker(workerSpecifier) {
|
|
12
|
+
return new Worker(workerSpecifier);
|
|
13
|
+
}
|
|
14
|
+
async function resizeImageInWorker(workerSpecifier, inputBytes, mimeType, options) {
|
|
15
|
+
const worker = createResizeWorker(workerSpecifier);
|
|
16
|
+
try {
|
|
17
|
+
const inputBytesForWorker = toTransferableBytes(inputBytes);
|
|
18
|
+
return await new Promise((resolve, reject) => {
|
|
19
|
+
let settled = false;
|
|
20
|
+
const settle = (result) => {
|
|
21
|
+
if (settled)
|
|
22
|
+
return;
|
|
23
|
+
settled = true;
|
|
24
|
+
resolve(result);
|
|
25
|
+
};
|
|
26
|
+
const fail = (error) => {
|
|
27
|
+
if (settled)
|
|
28
|
+
return;
|
|
29
|
+
settled = true;
|
|
30
|
+
reject(error);
|
|
31
|
+
};
|
|
32
|
+
worker.once("message", (message) => {
|
|
33
|
+
if (!isResizeImageWorkerResponse(message)) {
|
|
34
|
+
fail(new Error("Invalid image resize worker response"));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (message.error) {
|
|
38
|
+
fail(new Error(message.error));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
settle(message.result ?? null);
|
|
42
|
+
});
|
|
43
|
+
worker.once("error", fail);
|
|
44
|
+
worker.once("exit", (code) => {
|
|
45
|
+
if (!settled) {
|
|
46
|
+
fail(new Error(`Image resize worker exited with code ${code}`));
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
worker.postMessage({
|
|
50
|
+
inputBytes: inputBytesForWorker,
|
|
51
|
+
mimeType,
|
|
52
|
+
options,
|
|
53
|
+
}, [inputBytesForWorker.buffer]);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
void worker.terminate().catch(() => undefined);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Resize an image to fit within the specified max dimensions and encoded file size.
|
|
62
|
+
* Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not
|
|
63
|
+
* block the TUI event loop. If the worker cannot be loaded (for example in some
|
|
64
|
+
* Bun compiled executable layouts), fall back to in-process resizing so image
|
|
65
|
+
* reads still work.
|
|
66
|
+
*/
|
|
67
|
+
export async function resizeImage(inputBytes, mimeType, options) {
|
|
68
|
+
const isTypeScriptRuntime = import.meta.url.endsWith(".ts");
|
|
69
|
+
const workerUrl = new URL(isTypeScriptRuntime ? "./image-resize-worker.ts" : "./image-resize-worker.js", import.meta.url);
|
|
70
|
+
// Bun compiled executables resolve worker entrypoints by string path, not via
|
|
71
|
+
// new URL(..., import.meta.url). Try the string path first under Bun so the
|
|
72
|
+
// release binary uses the embedded worker instead of falling back in-process.
|
|
73
|
+
if (typeof process.versions.bun === "string") {
|
|
74
|
+
try {
|
|
75
|
+
return await resizeImageInWorker("./src/utils/image-resize-worker.ts", inputBytes, mimeType, options);
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
}
|
|
79
|
+
try {
|
|
80
|
+
return await resizeImageInWorker(workerUrl, inputBytes, mimeType, options);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
return resizeImageInProcess(inputBytes, mimeType, options);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Format a dimension note for resized images.
|
|
88
|
+
* This helps the model understand the coordinate mapping.
|
|
89
|
+
*/
|
|
90
|
+
export function formatDimensionNote(result) {
|
|
91
|
+
if (!result.wasResized) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const scale = result.originalWidth / result.width;
|
|
95
|
+
return `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=image-resize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image-resize.js","sourceRoot":"","sources":["../../src/utils/image-resize.ts"],"names":[],"mappings":"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","sourcesContent":["import { Worker } from \"node:worker_threads\";\nimport { type ImageResizeOptions, type ResizedImage, resizeImageInProcess } from \"./image-resize-core.ts\";\n\nexport type { ImageResizeOptions, ResizedImage } from \"./image-resize-core.ts\";\n\ninterface ResizeImageWorkerResponse {\n\tresult?: ResizedImage | null;\n\terror?: string;\n}\n\nfunction toTransferableBytes(input: Uint8Array): Uint8Array<ArrayBuffer> {\n\t// Transfer detaches the buffer, so transfer a worker-owned copy and leave the\n\t// caller's bytes intact.\n\treturn new Uint8Array(input);\n}\n\nfunction isResizeImageWorkerResponse(value: unknown): value is ResizeImageWorkerResponse {\n\treturn value !== null && typeof value === \"object\";\n}\n\nfunction createResizeWorker(workerSpecifier: string | URL): Worker {\n\treturn new Worker(workerSpecifier);\n}\n\nasync function resizeImageInWorker(\n\tworkerSpecifier: string | URL,\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst worker = createResizeWorker(workerSpecifier);\n\ttry {\n\t\tconst inputBytesForWorker = toTransferableBytes(inputBytes);\n\t\treturn await new Promise<ResizedImage | null>((resolve, reject) => {\n\t\t\tlet settled = false;\n\t\t\tconst settle = (result: ResizedImage | null): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\tresolve(result);\n\t\t\t};\n\t\t\tconst fail = (error: Error): void => {\n\t\t\t\tif (settled) return;\n\t\t\t\tsettled = true;\n\t\t\t\treject(error);\n\t\t\t};\n\n\t\t\tworker.once(\"message\", (message: unknown) => {\n\t\t\t\tif (!isResizeImageWorkerResponse(message)) {\n\t\t\t\t\tfail(new Error(\"Invalid image resize worker response\"));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tif (message.error) {\n\t\t\t\t\tfail(new Error(message.error));\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tsettle(message.result ?? null);\n\t\t\t});\n\t\t\tworker.once(\"error\", fail);\n\t\t\tworker.once(\"exit\", (code) => {\n\t\t\t\tif (!settled) {\n\t\t\t\t\tfail(new Error(`Image resize worker exited with code ${code}`));\n\t\t\t\t}\n\t\t\t});\n\t\t\tworker.postMessage(\n\t\t\t\t{\n\t\t\t\t\tinputBytes: inputBytesForWorker,\n\t\t\t\t\tmimeType,\n\t\t\t\t\toptions,\n\t\t\t\t},\n\t\t\t\t[inputBytesForWorker.buffer],\n\t\t\t);\n\t\t});\n\t} finally {\n\t\tvoid worker.terminate().catch(() => undefined);\n\t}\n}\n\n/**\n * Resize an image to fit within the specified max dimensions and encoded file size.\n * Runs Photon in a worker thread so WASM decoding, resizing, and encoding do not\n * block the TUI event loop. If the worker cannot be loaded (for example in some\n * Bun compiled executable layouts), fall back to in-process resizing so image\n * reads still work.\n */\nexport async function resizeImage(\n\tinputBytes: Uint8Array,\n\tmimeType: string,\n\toptions?: ImageResizeOptions,\n): Promise<ResizedImage | null> {\n\tconst isTypeScriptRuntime = import.meta.url.endsWith(\".ts\");\n\tconst workerUrl = new URL(\n\t\tisTypeScriptRuntime ? \"./image-resize-worker.ts\" : \"./image-resize-worker.js\",\n\t\timport.meta.url,\n\t);\n\n\t// Bun compiled executables resolve worker entrypoints by string path, not via\n\t// new URL(..., import.meta.url). Try the string path first under Bun so the\n\t// release binary uses the embedded worker instead of falling back in-process.\n\tif (typeof process.versions.bun === \"string\") {\n\t\ttry {\n\t\t\treturn await resizeImageInWorker(\"./src/utils/image-resize-worker.ts\", inputBytes, mimeType, options);\n\t\t} catch {}\n\t}\n\n\ttry {\n\t\treturn await resizeImageInWorker(workerUrl, inputBytes, mimeType, options);\n\t} catch {\n\t\treturn resizeImageInProcess(inputBytes, mimeType, options);\n\t}\n}\n\n/**\n * Format a dimension note for resized images.\n * This helps the model understand the coordinate mapping.\n */\nexport function formatDimensionNote(result: ResizedImage): string | undefined {\n\tif (!result.wasResized) {\n\t\treturn undefined;\n\t}\n\n\tconst scale = result.originalWidth / result.width;\n\treturn `[Image: original ${result.originalWidth}x${result.originalHeight}, displayed at ${result.width}x${result.height}. Multiply coordinates by ${scale.toFixed(2)} to map to original image.]`;\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIvD","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */
|
|
2
|
+
export function stripJsonComments(input) {
|
|
3
|
+
return input
|
|
4
|
+
.replace(/"(?:\\.|[^"\\])*"|\/\/[^\n]*/g, (m) => (m[0] === '"' ? m : ""))
|
|
5
|
+
.replace(/"(?:\\.|[^"\\])*"|,(\s*[}\]])/g, (m, tail) => tail ?? (m[0] === '"' ? m : ""));
|
|
6
|
+
}
|
|
7
|
+
//# sourceMappingURL=json.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json.js","sourceRoot":"","sources":["../../src/utils/json.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,MAAM,UAAU,iBAAiB,CAAC,KAAa,EAAU;IACxD,OAAO,KAAK;SACV,OAAO,CAAC,+BAA+B,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;SACxE,OAAO,CAAC,gCAAgC,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;AAAA,CAC1F","sourcesContent":["/** Strip `//` line comments and trailing commas from JSON, leaving string literals untouched. */\nexport function stripJsonComments(input: string): string {\n\treturn input\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|\\/\\/[^\\n]*/g, (m) => (m[0] === '\"' ? m : \"\"))\n\t\t.replace(/\"(?:\\\\.|[^\"\\\\])*\"|,(\\s*[}\\]])/g, (m, tail) => tail ?? (m[0] === '\"' ? m : \"\"));\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../../src/utils/mime.ts"],"names":[],"mappings":"AAKA,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAiB9E;AAED,wBAAsB,oCAAoC,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CASnG","sourcesContent":["import { open } from \"node:fs/promises\";\n\nconst IMAGE_TYPE_SNIFF_BYTES = 4100;\nconst PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];\n\nexport function detectSupportedImageMimeType(buffer: Uint8Array): string | null {\n\tif (startsWith(buffer, [0xff, 0xd8, 0xff])) {\n\t\treturn buffer[3] === 0xf7 ? null : \"image/jpeg\";\n\t}\n\tif (startsWith(buffer, PNG_SIGNATURE)) {\n\t\treturn isPng(buffer) && !isAnimatedPng(buffer) ? \"image/png\" : null;\n\t}\n\tif (startsWithAscii(buffer, 0, \"GIF\")) {\n\t\treturn \"image/gif\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"RIFF\") && startsWithAscii(buffer, 8, \"WEBP\")) {\n\t\treturn \"image/webp\";\n\t}\n\tif (startsWithAscii(buffer, 0, \"BM\") && isBmp(buffer)) {\n\t\treturn \"image/bmp\";\n\t}\n\treturn null;\n}\n\nexport async function detectSupportedImageMimeTypeFromFile(filePath: string): Promise<string | null> {\n\tconst fileHandle = await open(filePath, \"r\");\n\ttry {\n\t\tconst buffer = Buffer.alloc(IMAGE_TYPE_SNIFF_BYTES);\n\t\tconst { bytesRead } = await fileHandle.read(buffer, 0, IMAGE_TYPE_SNIFF_BYTES, 0);\n\t\treturn detectSupportedImageMimeType(buffer.subarray(0, bytesRead));\n\t} finally {\n\t\tawait fileHandle.close();\n\t}\n}\n\nfunction isPng(buffer: Uint8Array): boolean {\n\treturn (\n\t\tbuffer.length >= 16 && readUint32BE(buffer, PNG_SIGNATURE.length) === 13 && startsWithAscii(buffer, 12, \"IHDR\")\n\t);\n}\n\nfunction isAnimatedPng(buffer: Uint8Array): boolean {\n\tlet offset = PNG_SIGNATURE.length;\n\twhile (offset + 8 <= buffer.length) {\n\t\tconst chunkLength = readUint32BE(buffer, offset);\n\t\tconst chunkTypeOffset = offset + 4;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"acTL\")) return true;\n\t\tif (startsWithAscii(buffer, chunkTypeOffset, \"IDAT\")) return false;\n\n\t\tconst nextOffset = offset + 8 + chunkLength + 4;\n\t\tif (nextOffset <= offset || nextOffset > buffer.length) return false;\n\t\toffset = nextOffset;\n\t}\n\treturn false;\n}\n\nfunction isBmp(buffer: Uint8Array): boolean {\n\tif (buffer.length < 26) return false;\n\n\tconst declaredFileSize = readUint32LE(buffer, 2);\n\tconst pixelDataOffset = readUint32LE(buffer, 10);\n\tconst dibHeaderSize = readUint32LE(buffer, 14);\n\tif (declaredFileSize !== 0 && declaredFileSize < 26) return false;\n\tif (pixelDataOffset < 14 + dibHeaderSize) return false;\n\tif (declaredFileSize !== 0 && pixelDataOffset >= declaredFileSize) return false;\n\n\tlet colorPlanes: number;\n\tlet bitsPerPixel: number;\n\tif (dibHeaderSize === 12) {\n\t\tcolorPlanes = readUint16LE(buffer, 22);\n\t\tbitsPerPixel = readUint16LE(buffer, 24);\n\t} else if (dibHeaderSize >= 40 && dibHeaderSize <= 124) {\n\t\tif (buffer.length < 30) return false;\n\t\tcolorPlanes = readUint16LE(buffer, 26);\n\t\tbitsPerPixel = readUint16LE(buffer, 28);\n\t} else {\n\t\treturn false;\n\t}\n\n\treturn colorPlanes === 1 && [1, 4, 8, 16, 24, 32].includes(bitsPerPixel);\n}\n\nfunction readUint16LE(buffer: Uint8Array, offset: number): number {\n\treturn (buffer[offset] ?? 0) + ((buffer[offset + 1] ?? 0) << 8);\n}\n\nfunction readUint32BE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) * 0x1000000 +\n\t\t((buffer[offset + 1] ?? 0) << 16) +\n\t\t((buffer[offset + 2] ?? 0) << 8) +\n\t\t(buffer[offset + 3] ?? 0)\n\t);\n}\n\nfunction readUint32LE(buffer: Uint8Array, offset: number): number {\n\treturn (\n\t\t(buffer[offset] ?? 0) +\n\t\t((buffer[offset + 1] ?? 0) << 8) +\n\t\t((buffer[offset + 2] ?? 0) << 16) +\n\t\t(buffer[offset + 3] ?? 0) * 0x1000000\n\t);\n}\n\nfunction startsWith(buffer: Uint8Array, bytes: number[]): boolean {\n\tif (buffer.length < bytes.length) return false;\n\treturn bytes.every((byte, index) => buffer[index] === byte);\n}\n\nfunction startsWithAscii(buffer: Uint8Array, offset: number, text: string): boolean {\n\tif (buffer.length < offset + text.length) return false;\n\tfor (let index = 0; index < text.length; index++) {\n\t\tif (buffer[offset + index] !== text.charCodeAt(index)) return false;\n\t}\n\treturn true;\n}\n"]}
|