mixdog 0.9.162 → 0.9.164
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/package.json +1 -1
- package/scripts/code-graph-description-contract.mjs +9 -14
- package/scripts/import-usage-ledger.mjs +10 -0
- package/scripts/pg-memory-diet-apply.mjs +95 -0
- package/scripts/pg-memory-diet-verify.mjs +83 -0
- package/scripts/pg-memory-diet.mjs +164 -0
- package/scripts/search-rg-parity.mjs +132 -0
- package/src/defaults/skills/browser-use/SKILL.md +4 -5
- package/src/defaults/skills/docx/SKILL.md +8 -6
- package/src/defaults/skills/docx/references/native-authoring.md +78 -2
- package/src/defaults/skills/goal-management/SKILL.md +88 -19
- package/src/defaults/skills/pdf/SKILL.md +9 -7
- package/src/defaults/skills/pptx/SKILL.md +19 -16
- package/src/defaults/skills/pptx/references/charts.md +143 -31
- package/src/defaults/skills/pptx/references/composition.md +70 -27
- package/src/defaults/skills/pptx/references/direction.md +9 -4
- package/src/defaults/skills/pptx/references/kit.md +858 -65
- package/src/defaults/skills/pptx/references/pictures.md +213 -6
- package/src/defaults/skills/skill-creator/SKILL.md +5 -5
- package/src/defaults/skills/video/SKILL.md +3 -3
- package/src/defaults/skills/xlsx/SKILL.md +8 -6
- package/src/defaults/skills/xlsx/references/report-design.md +41 -1
- package/src/headless-exec.mjs +10 -0
- package/src/lib/rules-builder.cjs +23 -68
- package/src/output-styles/common.md +2 -0
- package/src/rules/lead/01-general.md +13 -14
- package/src/rules/lead/lead-brief.md +11 -13
- package/src/rules/shared/00-general.md +9 -14
- package/src/rules/shared/10-tool-workflow.md +21 -48
- package/src/rules/shared/15-skills.md +7 -0
- package/src/rules/shared/30-exploration.md +13 -34
- package/src/rules/shared/35-implementation.md +7 -8
- package/src/rules/shared/40-editing.md +5 -23
- package/src/rules/shared/50-execution.md +7 -11
- package/src/rules/shared/60-verification.md +11 -18
- package/src/rules/shared/70-delivery.md +5 -4
- package/src/rules/shared/75-goal.md +7 -4
- package/src/rules/shared/80-memory.md +5 -8
- package/src/runtime/agent/orchestrator/agent-runtime/agent-dispatch.mjs +0 -1
- package/src/runtime/agent/orchestrator/agent-runtime/maintenance-route.mjs +2 -2
- package/src/runtime/agent/orchestrator/agent-runtime/session-builder.mjs +0 -6
- package/src/runtime/agent/orchestrator/agent-trace-format.mjs +20 -3
- package/src/runtime/agent/orchestrator/config-presets.mjs +100 -0
- package/src/runtime/agent/orchestrator/config-storage.mjs +246 -0
- package/src/runtime/agent/orchestrator/config.mjs +58 -552
- package/src/runtime/agent/orchestrator/context/collect.mjs +31 -5
- package/src/runtime/agent/orchestrator/context/skill-state.mjs +43 -0
- package/src/runtime/agent/orchestrator/providers/account-pool.mjs +7 -1
- package/src/runtime/agent/orchestrator/providers/admission-scheduler.mjs +6 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth-credentials.mjs +105 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +20 -5
- package/src/runtime/agent/orchestrator/providers/anthropic-sse.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/cursor-wire.mjs +7 -5
- package/src/runtime/agent/orchestrator/providers/cursor.mjs +8 -3
- package/src/runtime/agent/orchestrator/providers/custom-tool-wire.mjs +47 -1
- package/src/runtime/agent/orchestrator/providers/gemini-stream.mjs +62 -101
- package/src/runtime/agent/orchestrator/providers/model-catalog.mjs +78 -8
- package/src/runtime/agent/orchestrator/providers/openai-compat-chat-stream.mjs +320 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream-common.mjs +136 -0
- package/src/runtime/agent/orchestrator/providers/openai-compat-stream.mjs +21 -542
- package/src/runtime/agent/orchestrator/providers/openai-compat-wire.mjs +7 -7
- package/src/runtime/agent/orchestrator/providers/openai-oauth-http-sse.mjs +2 -7
- package/src/runtime/agent/orchestrator/providers/openai-responses-input.mjs +26 -6
- package/src/runtime/agent/orchestrator/providers/openai-responses-payload.mjs +8 -18
- package/src/runtime/agent/orchestrator/providers/openai-ws-stream.mjs +2 -7
- package/src/runtime/agent/orchestrator/providers/registry.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/statusline-route-meta.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/agent-loop.mjs +41 -116
- package/src/runtime/agent/orchestrator/session/compact/constants.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/compact/messages.mjs +13 -4
- package/src/runtime/agent/orchestrator/session/compact/runner.mjs +132 -32
- package/src/runtime/agent/orchestrator/session/compact/summary.mjs +10 -0
- package/src/runtime/agent/orchestrator/session/explicit-skills.mjs +57 -0
- package/src/runtime/agent/orchestrator/session/loop/compact-policy.mjs +23 -8
- package/src/runtime/agent/orchestrator/session/loop/completion-guards.mjs +0 -4
- package/src/runtime/agent/orchestrator/session/loop/fresh-context.mjs +104 -431
- package/src/runtime/agent/orchestrator/session/loop/termination.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/loop/tool-exec.mjs +18 -15
- package/src/runtime/agent/orchestrator/session/loop/tool-helpers.mjs +2 -2
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +11 -1
- package/src/runtime/agent/orchestrator/session/manager/compaction-runner.mjs +65 -33
- package/src/runtime/agent/orchestrator/session/manager/rules-cache.mjs +5 -2
- package/src/runtime/agent/orchestrator/session/manager/session-lifecycle.mjs +14 -6
- package/src/runtime/agent/orchestrator/session/manager/status-telemetry.mjs +2 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +1 -0
- package/src/runtime/agent/orchestrator/session/store-summary-index.mjs +0 -6
- package/src/runtime/agent/orchestrator/session/store-summary-reader.mjs +0 -1
- package/src/runtime/agent/orchestrator/session/tool-batch.mjs +33 -0
- package/src/runtime/agent/orchestrator/session/tool-result-offload.mjs +142 -20
- package/src/runtime/agent/orchestrator/tools/builtin/arg-guard.mjs +75 -28
- package/src/runtime/agent/orchestrator/tools/builtin/bash-tool.mjs +9 -94
- package/src/runtime/agent/orchestrator/tools/builtin/binary-file.mjs +8 -8
- package/src/runtime/agent/orchestrator/tools/builtin/builtin-tools.mjs +54 -26
- package/src/runtime/agent/orchestrator/tools/builtin/cache-layers.mjs +43 -54
- package/src/runtime/agent/orchestrator/tools/builtin/git-command-tool.mjs +37 -25
- package/src/runtime/agent/orchestrator/tools/builtin/lib/glob-static-prefix.mjs +11 -18
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-context-expander.mjs +24 -36
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-path-fanout.mjs +4 -4
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-pattern-fanout.mjs +32 -4
- package/src/runtime/agent/orchestrator/tools/builtin/lib/list-helpers.mjs +2 -12
- package/src/runtime/agent/orchestrator/tools/builtin/lib/search-input-helpers.mjs +0 -53
- package/src/runtime/agent/orchestrator/tools/builtin/list-tool.mjs +15 -37
- package/src/runtime/agent/orchestrator/tools/builtin/local-search-telemetry.mjs +12 -0
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-client.mjs +3 -4
- package/src/runtime/agent/orchestrator/tools/builtin/native-search-runner.mjs +2 -1
- package/src/runtime/agent/orchestrator/tools/builtin/path-utils.mjs +6 -84
- package/src/runtime/agent/orchestrator/tools/builtin/read-batch.mjs +2 -6
- package/src/runtime/agent/orchestrator/tools/builtin/read-constants.mjs +4 -9
- package/src/runtime/agent/orchestrator/tools/builtin/read-office-files.mjs +453 -16
- package/src/runtime/agent/orchestrator/tools/builtin/read-range-index.mjs +40 -23
- package/src/runtime/agent/orchestrator/tools/builtin/read-single-tool.mjs +25 -49
- package/src/runtime/agent/orchestrator/tools/builtin/read-source-windows.mjs +81 -0
- package/src/runtime/agent/orchestrator/tools/builtin/read-streaming.mjs +83 -67
- package/src/runtime/agent/orchestrator/tools/builtin/read-tool.mjs +3 -6
- package/src/runtime/agent/orchestrator/tools/builtin/runtime-capabilities.mjs +25 -1
- package/src/runtime/agent/orchestrator/tools/builtin/search-builders.mjs +11 -2
- package/src/runtime/agent/orchestrator/tools/builtin/search-glob-tool.mjs +20 -61
- package/src/runtime/agent/orchestrator/tools/builtin/search-grep-tool.mjs +16 -91
- package/src/runtime/agent/orchestrator/tools/builtin/search-path-diagnostics.mjs +3 -70
- package/src/runtime/agent/orchestrator/tools/builtin/search-tool.mjs +0 -1
- package/src/runtime/agent/orchestrator/tools/builtin/shell-analysis.mjs +0 -43
- package/src/runtime/agent/orchestrator/tools/builtin/shell-lossless-compact.mjs +78 -23
- package/src/runtime/agent/orchestrator/tools/code-graph/build.mjs +1 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/dispatch.mjs +16 -1
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-binary.mjs +3 -0
- package/src/runtime/agent/orchestrator/tools/code-graph/graph-model.mjs +2 -0
- package/src/runtime/agent/orchestrator/tools/code-graph-tool-defs.mjs +5 -5
- package/src/runtime/agent/orchestrator/tools/graph-manifest.json +11 -11
- package/src/runtime/agent/orchestrator/tools/patch-tool-defs.mjs +5 -2
- package/src/runtime/agent/orchestrator/tools/tool-batch-trace.mjs +34 -4
- package/src/runtime/attachments/pdf-extract.mjs +32 -2
- package/src/runtime/computer-bridge/action-schema.mjs +1 -3
- package/src/runtime/computer-bridge/client.mjs +20 -12
- package/src/runtime/computer-bridge/core-actions.mjs +7 -1
- package/src/runtime/computer-bridge/limits.d.mts +1 -0
- package/src/runtime/computer-bridge/limits.mjs +3 -0
- package/src/runtime/memory/index.mjs +73 -145
- package/src/runtime/memory/lib/http-router.mjs +14 -7
- package/src/runtime/memory/lib/http-wire.mjs +3 -47
- package/src/runtime/memory/lib/memory-recall-store.mjs +7 -5
- package/src/runtime/memory/lib/memory-service-lifecycle.mjs +105 -0
- package/src/runtime/memory/lib/pg/compact-indexes.mjs +93 -0
- package/src/runtime/memory/lib/recall-format.mjs +17 -5
- package/src/runtime/memory/lib/recall-substring-predicate.mjs +7 -0
- package/src/runtime/memory/lib/session-ingest.mjs +16 -0
- package/src/runtime/memory/lib/trace-store.mjs +4 -3
- package/src/runtime/office/authoring/pptx-author-action.mjs +1 -0
- package/src/runtime/office/authoring/pptx-brief.mjs +21 -5
- package/src/runtime/office/authoring/pptx-receipt.mjs +79 -4
- package/src/runtime/office/authoring/pptx-script-normalize.mjs +70 -1
- package/src/runtime/office/authoring/pptx-script-runner.mjs +1 -0
- package/src/runtime/office/capabilities.mjs +346 -46
- package/src/runtime/office/com/office-com-cleanup.ps1 +15 -2
- package/src/runtime/office/com/office-com-host.ps1 +519 -40
- package/src/runtime/office/com/office-com-session-host.ps1 +5 -0
- package/src/runtime/office/com/office-word-formatting.ps1 +6 -0
- package/src/runtime/office/core/office-actions-batch.mjs +39 -4
- package/src/runtime/office/core/office-actions-inspect.mjs +96 -1
- package/src/runtime/office/core/office-actions-lifecycle.mjs +1 -1
- package/src/runtime/office/core/office-actions-render.mjs +26 -12
- package/src/runtime/office/core/office-core.mjs +25 -5
- package/src/runtime/office/core/office-recalculation.mjs +17 -1
- package/src/runtime/office/core/office-sessions.mjs +185 -15
- package/src/runtime/office/core/office-transactions.mjs +9 -1
- package/src/runtime/office/core/pagination.mjs +27 -21
- package/src/runtime/office/core/pptx-page-cache.mjs +4 -1
- package/src/runtime/office/core/snapshot-contract.mjs +10 -0
- package/src/runtime/office/core/tabular.mjs +32 -9
- package/src/runtime/office/design/composition-system.mjs +5 -2
- package/src/runtime/office/design/content-model.mjs +69 -12
- package/src/runtime/office/design/design-discipline.mjs +18 -3
- package/src/runtime/office/design/design-table-input.mjs +33 -0
- package/src/runtime/office/design/design-tokens.mjs +3 -0
- package/src/runtime/office/design/docx/design-docx-components.mjs +41 -5
- package/src/runtime/office/design/docx/design-docx.mjs +20 -25
- package/src/runtime/office/design/xlsx/design-xlsx-components.mjs +3 -2
- package/src/runtime/office/design/xlsx/design-xlsx.mjs +154 -36
- package/src/runtime/office/index.mjs +62 -7
- package/src/runtime/office/pdf/pdf-adapter.mjs +76 -17
- package/src/runtime/office/pdf/pdf-analysis.mjs +154 -29
- package/src/runtime/office/pdf/pdf-draw.mjs +43 -3
- package/src/runtime/office/pdf/pdf-fonts.mjs +48 -2
- package/src/runtime/office/pdf/pdf-forms.mjs +131 -2
- package/src/runtime/office/pdf/pdf-search.mjs +99 -17
- package/src/runtime/office/pdf/pdf-writer.mjs +282 -20
- package/src/runtime/office/portable/docx-tracked-edits.mjs +14 -9
- package/src/runtime/office/portable/portable-cells.mjs +142 -7
- package/src/runtime/office/portable/portable-chart.mjs +50 -10
- package/src/runtime/office/portable/portable-docx-parts.mjs +115 -8
- package/src/runtime/office/portable/portable-docx-xml.mjs +213 -11
- package/src/runtime/office/portable/portable-docx.mjs +294 -36
- package/src/runtime/office/portable/portable-ooxml.mjs +4 -1
- package/src/runtime/office/portable/portable-opc.mjs +111 -7
- package/src/runtime/office/portable/portable-package.mjs +5 -1
- package/src/runtime/office/portable/portable-pivot.mjs +52 -2
- package/src/runtime/office/portable/portable-pptx-chart.mjs +50 -0
- package/src/runtime/office/portable/portable-pptx-charts.mjs +50 -11
- package/src/runtime/office/portable/portable-pptx-core.mjs +105 -11
- package/src/runtime/office/portable/portable-pptx-deck.mjs +66 -7
- package/src/runtime/office/portable/portable-pptx-shapes.mjs +36 -6
- package/src/runtime/office/portable/portable-pptx.mjs +2 -1
- package/src/runtime/office/portable/portable-sheet-page.mjs +94 -29
- package/src/runtime/office/portable/portable-sheet-styles.mjs +59 -2
- package/src/runtime/office/portable/portable-sheet-xml.mjs +267 -4
- package/src/runtime/office/portable/portable-slide-shapes.mjs +2 -1
- package/src/runtime/office/portable/portable-snapshot.mjs +344 -36
- package/src/runtime/office/portable/portable-validation.mjs +524 -75
- package/src/runtime/office/portable/portable-xlsx.mjs +455 -53
- package/src/runtime/office/portable/portable-xml.mjs +25 -0
- package/src/runtime/office/portable/text-metrics.mjs +140 -10
- package/src/runtime/office/portable/xlsx-assertions.mjs +56 -4
- package/src/runtime/office/portable/xlsx-audit-support.mjs +35 -0
- package/src/runtime/office/portable/xlsx-contract.mjs +118 -2
- package/src/runtime/office/portable/xlsx-formula-audit.mjs +52 -3
- package/src/runtime/office/portable/xlsx-sheet-hygiene.mjs +17 -3
- package/src/runtime/office/quality/assurance-checklist.mjs +16 -1
- package/src/runtime/office/quality/assurance-rendered.mjs +12 -0
- package/src/runtime/office/quality/assurance-structure.mjs +297 -7
- package/src/runtime/office/quality/design-aesthetics.mjs +29 -13
- package/src/runtime/office/quality/design-deck-diversity.mjs +93 -2
- package/src/runtime/office/quality/design-review-authored.mjs +23 -7
- package/src/runtime/office/quality/design-review-critique.mjs +17 -3
- package/src/runtime/office/quality/design-review.mjs +10 -4
- package/src/runtime/office/quality/document-acceptance.mjs +30 -5
- package/src/runtime/office/quality/pptx-deck-rubric.mjs +23 -0
- package/src/runtime/office/quality/quality-pipeline.mjs +22 -3
- package/src/runtime/office/quality/render-air.mjs +49 -4
- package/src/runtime/office/shared/labels.mjs +25 -0
- package/src/runtime/office/tool-defs.mjs +1 -1
- package/src/runtime/shared/abort-race.mjs +4 -1
- package/src/runtime/shared/atomic-file.d.mts +27 -0
- package/src/runtime/shared/atomic-file.mjs +31 -564
- package/src/runtime/shared/bounded-download.mjs +45 -29
- package/src/runtime/shared/debounced-writer.d.mts +13 -0
- package/src/runtime/shared/debounced-writer.mjs +73 -0
- package/src/runtime/shared/edit-tool-dialect.mjs +9 -2
- package/src/runtime/shared/file-lock.mjs +278 -0
- package/src/runtime/shared/file-permissions.d.mts +9 -0
- package/src/runtime/shared/file-permissions.mjs +141 -0
- package/src/runtime/shared/http-request-body.mjs +97 -0
- package/src/runtime/shared/json-metrics.mjs +38 -25
- package/src/runtime/shared/keyed-serial-queue.d.mts +4 -0
- package/src/runtime/shared/keyed-serial-queue.mjs +14 -0
- package/src/runtime/shared/llm/cost.mjs +42 -28
- package/src/runtime/shared/llm/usage-accounting.mjs +59 -0
- package/src/runtime/shared/llm/usage-ledger-import.mjs +159 -0
- package/src/runtime/shared/llm/usage-ledger.mjs +439 -0
- package/src/runtime/shared/llm/usage-measurement.mjs +40 -0
- package/src/runtime/shared/llm/usage-rollup.mjs +473 -0
- package/src/runtime/shared/llm/usage-session-history.mjs +491 -0
- package/src/runtime/shared/loopback-listener.mjs +84 -0
- package/src/runtime/shared/pristine-execution-contract.json +4 -0
- package/src/runtime/shared/pristine-execution.mjs +1 -0
- package/src/runtime/shared/profile-config.cjs +77 -0
- package/src/runtime/shared/skill-selection.d.mts +2 -0
- package/src/runtime/shared/skill-selection.mjs +20 -0
- package/src/runtime/shared/sleep.mjs +7 -0
- package/src/runtime/shared/tool-result-summary.mjs +10 -5
- package/src/session-runtime/builtin-features.d.mts +2 -1
- package/src/session-runtime/builtin-features.mjs +25 -4
- package/src/session-runtime/config-lifecycle.mjs +44 -182
- package/src/session-runtime/goal-deadlines.mjs +165 -0
- package/src/session-runtime/goal-runtime.mjs +170 -464
- package/src/session-runtime/goal-state.mjs +287 -0
- package/src/session-runtime/goal-storage.mjs +91 -11
- package/src/session-runtime/goal-tasks.mjs +9 -2
- package/src/session-runtime/goal-text.mjs +71 -8
- package/src/session-runtime/goal-tool-defs.mjs +12 -8
- package/src/session-runtime/inheritance-fit.mjs +132 -0
- package/src/session-runtime/internal-tool-executor.mjs +1 -1
- package/src/session-runtime/lifecycle-api.mjs +108 -61
- package/src/session-runtime/provider-auth-api.mjs +20 -0
- package/src/session-runtime/provider-catalog-cache.mjs +46 -0
- package/src/session-runtime/provider-models.mjs +40 -81
- package/src/session-runtime/provider-readiness.mjs +3 -4
- package/src/session-runtime/provider-usage.mjs +41 -22
- package/src/session-runtime/resource-api.mjs +2 -2
- package/src/session-runtime/runtime-core.mjs +9 -10
- package/src/session-runtime/session-hooks.mjs +2 -2
- package/src/session-runtime/session-lifecycle.mjs +0 -3
- package/src/session-runtime/session-turn-api.mjs +2 -2
- package/src/session-runtime/skill-tool-loading.mjs +17 -21
- package/src/session-runtime/skills-api.mjs +1 -1
- package/src/session-runtime/tool-catalog.mjs +12 -15
- package/src/session-runtime/tool-defs.mjs +2 -14
- package/src/session-runtime/tool-policy-refresh.mjs +1 -0
- package/src/session-runtime/usage-stats-api.mjs +72 -0
- package/src/session-runtime/warmup-schedulers.mjs +23 -15
- package/src/standalone/agent-dispatch-broker.mjs +6 -6
- package/src/standalone/agent-tool/job-views.mjs +0 -4
- package/src/standalone/agent-tool/render.mjs +7 -14
- package/src/standalone/agent-tool/spawn-flow.mjs +1 -3
- package/src/standalone/channel-binding.mjs +57 -0
- package/src/standalone/channel-transport.mjs +55 -80
- package/src/standalone/daemon-log.mjs +167 -0
- package/src/standalone/daemon.mjs +9 -145
- package/src/standalone/desktop-service-registry.mjs +37 -17
- package/src/standalone/fair-call-scheduler.mjs +14 -2
- package/src/standalone/hook-bus/command-handler.mjs +236 -0
- package/src/standalone/hook-bus/handler-timeout.mjs +17 -0
- package/src/standalone/hook-bus/handlers.mjs +28 -244
- package/src/standalone/hook-bus/rule-file.mjs +63 -0
- package/src/standalone/hook-bus.mjs +90 -98
- package/src/standalone/provider-admin.mjs +17 -3
- package/src/standalone/rpc-call-identity.mjs +23 -0
- package/src/standalone/session-call-cache.mjs +139 -0
- package/src/standalone/session-frame-stream.mjs +215 -0
- package/src/standalone/session-protocol.mjs +2 -0
- package/src/standalone/session-runtime-inline-host.mjs +55 -29
- package/src/standalone/session-service/agent-tree.mjs +0 -2
- package/src/standalone/session-service/projection.mjs +4 -2
- package/src/standalone/session-service/stored-reader.mjs +72 -0
- package/src/standalone/session-service.mjs +123 -148
- package/src/standalone/session-transport.mjs +67 -353
- package/src/standalone/test-fixtures/lifecycle-desktop-service.mjs +3 -0
- package/src/standalone/usage-dashboard-model.mjs +394 -0
- package/src/standalone/usage-dashboard.mjs +40 -389
- package/src/standalone/usage-stats-hours.mjs +50 -0
- package/src/standalone/usage-stats-model.mjs +634 -0
- package/src/standalone/usage-stats-period.mjs +84 -0
- package/src/tui/app/create-app-pickers.mjs +0 -1
- package/src/tui/app/model-picker.mjs +28 -29
- package/src/tui/app/slash-commands.mjs +1 -1
- package/src/tui/dist/index.mjs +582 -431
- package/src/tui/session/goal-continuation.mjs +96 -11
- package/src/tui/session/queue-helpers.mjs +6 -2
- package/src/tui/session/session-api-ext.mjs +84 -12
- package/src/tui/session/session-api.mjs +4 -0
- package/src/tui/session/session-flow.mjs +9 -5
- package/src/tui/session/turn.mjs +2 -1
- package/src/tui/session-local.mjs +5 -1
- package/src/vendor/statusline/src/gateway/route-meta.mjs +6 -2
- package/src/workflows/headless/WORKFLOW.md +3 -2
- package/src/runtime/agent/orchestrator/agent-runtime/agent-loop-policy.mjs +0 -18
- package/src/runtime/agent/orchestrator/tools/builtin/lib/grep-fixed-fallback.mjs +0 -123
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { basename, extname, join, posix } from 'node:path';
|
|
2
2
|
import { resolveImageLayout } from './image-layout.mjs';
|
|
3
|
-
import { shrinkFontSizeToFit } from './text-metrics.mjs';
|
|
3
|
+
import { contrastRatio, shrinkFontSizeToFit } from './text-metrics.mjs';
|
|
4
4
|
import { pictureXml, resolveGeometry, shapeXml, supportedShapeTypes, tableXml, textBodyXml, toEmu } from './portable-slide-shapes.mjs';
|
|
5
5
|
import { readFile } from 'node:fs/promises';
|
|
6
6
|
import { addPackageRelationship, ensureDefaultContentType, imagePixelSize, partRelationshipPath, removePackageRelationship, zipText } from './portable-opc.mjs';
|
|
@@ -176,6 +176,15 @@ export async function handleSetText(context, op) {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
|
|
179
|
+
// The ink a filled shape can carry: white on a dark card, near-black on a light
|
|
180
|
+
// one, and nothing at all when the fill is a theme reference or absent, so the
|
|
181
|
+
// inherited colour keeps deciding.
|
|
182
|
+
function inkOnFill(fill) {
|
|
183
|
+
const field = String(fill || '').replace(/^#/, '').toUpperCase();
|
|
184
|
+
if (!/^[0-9A-F]{6}$/.test(field)) return undefined;
|
|
185
|
+
return (contrastRatio('FFFFFF', field) ?? 0) >= (contrastRatio('1F2429', field) ?? 0) ? 'FFFFFF' : '1F2429';
|
|
186
|
+
}
|
|
187
|
+
|
|
179
188
|
export async function handleAddTextboxOrAddShape(context, op) {
|
|
180
189
|
const { zip } = context;
|
|
181
190
|
const slides = context.slides;
|
|
@@ -209,7 +218,10 @@ export async function handleAddTextboxOrAddShape(context, op) {
|
|
|
209
218
|
defaults: {
|
|
210
219
|
fontName: op.fontName ?? properties.fontName,
|
|
211
220
|
fontSize: op.fontSize ?? properties.fontSize ?? 18,
|
|
212
|
-
|
|
221
|
+
// A caller who fills a shape and says nothing about its text gets ink
|
|
222
|
+
// the fill can carry. The default dark ink on a dark card was text the
|
|
223
|
+
// runtime's own contrast check then reported as unreadable.
|
|
224
|
+
color: op.color ?? properties.color ?? inkOnFill(op.fillColor ?? properties.fillColor),
|
|
213
225
|
bold: properties.bold,
|
|
214
226
|
italic: properties.italic,
|
|
215
227
|
align: properties.align,
|
|
@@ -289,6 +301,7 @@ export async function handleAddImage(context, op) {
|
|
|
289
301
|
width: placement.width,
|
|
290
302
|
height: placement.height,
|
|
291
303
|
crop: placement.crop,
|
|
304
|
+
altText: op.altText,
|
|
292
305
|
});
|
|
293
306
|
zip.file(path, appendSlideShape(current, picture));
|
|
294
307
|
return {
|
|
@@ -351,6 +364,9 @@ export async function handleFitText(context, op) {
|
|
|
351
364
|
slide: Number(op.slide),
|
|
352
365
|
shape: Number(op.shape),
|
|
353
366
|
scale: Number(fitted.scale.toFixed(2)),
|
|
367
|
+
// The size the copy now reads at: shrinking is the last repair, so the
|
|
368
|
+
// author sees what it cost and can rewrite the line instead.
|
|
369
|
+
fontSize: Math.max(...fitted.sizes.map((size) => Number(size) || 0), 0) || undefined,
|
|
354
370
|
};
|
|
355
371
|
}
|
|
356
372
|
|
|
@@ -383,6 +399,16 @@ export async function handleSetTableDataOrReplaceImage(context, op) {
|
|
|
383
399
|
const media = await addSlideImage(zip, path, op.path);
|
|
384
400
|
updated = shape.xml.replace(/(<a:blip\b[^>]*\br:embed=")[^"]*(")/, `$1${media.relationshipId}$2`);
|
|
385
401
|
detail = { image: media.part, replaced: previous };
|
|
402
|
+
// The frame keeps the description of the picture that used to be in it, so
|
|
403
|
+
// a replaced photo is announced as the old one until the caller renames it.
|
|
404
|
+
const altText = String(op.altText ?? '').trim();
|
|
405
|
+
if (altText) {
|
|
406
|
+
const encoded = xmlEncode(altText);
|
|
407
|
+
updated = /<p:cNvPr\b[^>]*\bdescr="/.test(updated)
|
|
408
|
+
? updated.replace(/(<p:cNvPr\b[^>]*\bdescr=")[^"]*(")/, `$1${encoded}$2`)
|
|
409
|
+
: updated.replace(/(<p:cNvPr\b[^>]*?)(\/?>)/, `$1 descr="${encoded}"$2`);
|
|
410
|
+
detail.altText = altText;
|
|
411
|
+
}
|
|
386
412
|
}
|
|
387
413
|
const nextInner = `${tree.inner.slice(0, shape.start)}${updated}${tree.inner.slice(shape.end)}`;
|
|
388
414
|
const nextSlide = `${current.slice(0, tree.start)}${nextInner}${current.slice(tree.end)}`;
|
|
@@ -485,7 +511,7 @@ export async function handleAddMedia(context, op) {
|
|
|
485
511
|
const poster = await addSlideImage(zip, path, op.poster);
|
|
486
512
|
const id = nextShapeId(current);
|
|
487
513
|
const shape = '<p:pic><p:nvPicPr>'
|
|
488
|
-
+ `<p:cNvPr id="${id}" name="${xmlEncode(posix.basename(mediaPart))}">`
|
|
514
|
+
+ `<p:cNvPr id="${id}" name="${xmlEncode(posix.basename(mediaPart))}"${op.altText ? ` descr="${xmlEncode(String(op.altText))}"` : ''}>`
|
|
489
515
|
+ '<a:hlinkClick xmlns:a="' + DRAWING_MAIN_NS + '" r:id="" action="ppaction://media"/></p:cNvPr>'
|
|
490
516
|
+ '<p:cNvPicPr><a:picLocks noChangeAspect="1"/></p:cNvPicPr>'
|
|
491
517
|
+ `<p:nvPr><a:${kind}File xmlns:a="${DRAWING_MAIN_NS}" r:link="${linkId}"/>`
|
|
@@ -543,14 +569,18 @@ export async function handleAddAnimation(context, op) {
|
|
|
543
569
|
fly: { presetID: 2, filter: 'slide(fromBottom)' },
|
|
544
570
|
float: { presetID: 30, filter: 'slide(fromBottom)' },
|
|
545
571
|
};
|
|
546
|
-
|
|
572
|
+
// Every other multiword value in this runtime is written with a separator
|
|
573
|
+
// (outside_end, section_next), so on_click and after_previous name the same
|
|
574
|
+
// triggers as onclick and afterprevious.
|
|
575
|
+
const named = (value) => String(value || '').toLowerCase().replace(/[\s_-]+/g, '');
|
|
576
|
+
const requested = named(op.effect) || 'fade';
|
|
547
577
|
if (!Object.hasOwn(effects, requested)) {
|
|
548
578
|
throw new Error(`add_animation effect must be one of: ${Object.keys(effects).join(', ')}`);
|
|
549
579
|
}
|
|
550
580
|
const triggers = { onclick: 'clickEffect', withprevious: 'withEffect', afterprevious: 'afterEffect' };
|
|
551
|
-
const trigger =
|
|
581
|
+
const trigger = named(op.trigger) || 'onclick';
|
|
552
582
|
if (!Object.hasOwn(triggers, trigger)) {
|
|
553
|
-
throw new Error(
|
|
583
|
+
throw new Error('add_animation trigger must be one of: on_click, with_previous, after_previous');
|
|
554
584
|
}
|
|
555
585
|
const effect = effects[requested];
|
|
556
586
|
const duration = Math.max(1, Math.round((Number(op.duration) > 0 ? Number(op.duration) : 0.5) * 1000));
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { presentationSlides, renumberPresentationSlides } from './portable-pptx-package.mjs';
|
|
2
2
|
import { resolvePptxTargets } from './pptx-targets.mjs';
|
|
3
3
|
import { handleAddChart, handleSetChartAxis, handleSetChartData, handleSetChartDataLabels, handleSetChartSeries, handleSetChartTrendlineOrSetChartErrorBars } from './portable-pptx-charts.mjs';
|
|
4
|
-
import { handleAddCommentOrDeleteComment, handleAddProvenance, handleAddSlide, handleApplyTheme, handleDeleteSlide, handleDuplicateSlide, handleFillTemplate, handleImportSlides, handleKeepSlides, handleMoveSlide, handleReplaceText, handleSetFooterOrSetSlideNumber, handleSetLayout, handleSetNotes, handleSetSlideBackground, handleSetTransition } from './portable-pptx-deck.mjs';
|
|
4
|
+
import { handleAddCommentOrDeleteComment, handleAddProvenance, handleAddSlide, handleApplyTheme, handleDeleteSlide, handleDuplicateSlide, handleFillTemplate, handleImportSlides, handleKeepSlides, handleMoveSlide, handleReplaceText, handleSetFooterOrSetSlideNumber, handleSetLayout, handleSetNotes, handleSetSlideBackground, handleSetSlideVisibility, handleSetTransition } from './portable-pptx-deck.mjs';
|
|
5
5
|
import { handleAddAnimation, handleAddImage, handleAddMedia, handleAddTable, handleAddTextboxOrAddShape, handleAlignShapesOrDistributeShapes, handleCropImage, handleDeleteShape, handleFitText, handleGroupShapesOrUngroupShape, handleSetHyperlink, handleSetShape, handleSetTableDataOrReplaceImage, handleSetText, handleZOrder } from './portable-pptx-shapes.mjs';
|
|
6
6
|
export { inspectPptxTextBoxes } from './portable-pptx-core.mjs';
|
|
7
7
|
|
|
@@ -49,6 +49,7 @@ const PPTX_OPERATIONS = Object.freeze({
|
|
|
49
49
|
crop_image: handleCropImage,
|
|
50
50
|
add_animation: handleAddAnimation,
|
|
51
51
|
set_transition: handleSetTransition,
|
|
52
|
+
set_slide_visibility: handleSetSlideVisibility,
|
|
52
53
|
set_shape: handleSetShape,
|
|
53
54
|
});
|
|
54
55
|
|
|
@@ -26,80 +26,139 @@ export function worksheetGeometry(xml) {
|
|
|
26
26
|
return width === 0 ? 0 : Math.floor(width * 7 + 5) * 0.75;
|
|
27
27
|
};
|
|
28
28
|
const rowPoints = (index) => rows.get(index) ?? defaultRow;
|
|
29
|
-
|
|
29
|
+
// Which cell a point falls in. A drawing that starts exactly on a boundary
|
|
30
|
+
// sits in the cell that begins there (an image placed at D2 is in D2, not in
|
|
31
|
+
// C1), while one that ends on a boundary still ends in the cell before it.
|
|
32
|
+
const edgeCell = (points, size, limit, trailing) => {
|
|
30
33
|
let edge = 0;
|
|
31
34
|
for (let index = 1; index <= limit; index += 1) {
|
|
32
35
|
edge += size(index);
|
|
33
|
-
if (edge >= points) return index;
|
|
36
|
+
if (trailing ? edge >= points : edge > points) return index;
|
|
34
37
|
}
|
|
35
38
|
throw new Error('Worksheet drawing exceeds the Excel page grid');
|
|
36
39
|
};
|
|
37
40
|
return {
|
|
38
41
|
columnPoints,
|
|
39
42
|
rowPoints,
|
|
40
|
-
columnAt: (points) => edgeCell(points, columnPoints, 16_384),
|
|
41
|
-
rowAt: (points) => edgeCell(points, rowPoints, 1_048_576),
|
|
43
|
+
columnAt: (points, { trailing = false } = {}) => edgeCell(points, columnPoints, 16_384, trailing),
|
|
44
|
+
rowAt: (points, { trailing = false } = {}) => edgeCell(points, rowPoints, 1_048_576, trailing),
|
|
42
45
|
};
|
|
43
46
|
}
|
|
44
47
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
lastColumn = Math.max(lastColumn, columnNumber(parsed.col));
|
|
52
|
-
lastRow = Math.max(lastRow, parsed.row);
|
|
53
|
-
}
|
|
54
|
-
for (const reference of mergedRanges(xml)) {
|
|
55
|
-
const area = parseAreaRange(reference);
|
|
56
|
-
lastColumn = Math.max(lastColumn, area.endCol);
|
|
57
|
-
lastRow = Math.max(lastRow, area.endRow);
|
|
58
|
-
}
|
|
48
|
+
/** Every chart and picture anchored to a worksheet, resolved onto the cell grid
|
|
49
|
+
* (1-based, inclusive). Excel writes three anchor kinds and our own writer uses
|
|
50
|
+
* the absolute one, so the print area and the snapshot read the same resolver
|
|
51
|
+
* rather than each parsing anchors again. `body` is the anchor's own markup,
|
|
52
|
+
* which the caller inspects to tell a chart frame from a picture. */
|
|
53
|
+
export async function worksheetDrawings(zip, sheet, xml) {
|
|
59
54
|
const relations = await zipText(zip, partRelationshipPath(sheet.path));
|
|
60
55
|
const geometry = worksheetGeometry(xml);
|
|
61
|
-
|
|
56
|
+
const drawings = [];
|
|
57
|
+
for (const match of (relations || '').matchAll(/<Relationship\b([^>]*?)\/>/g)) {
|
|
62
58
|
const attrs = match[1];
|
|
63
59
|
if (!String(xmlAttribute(attrs, 'Type') || '').endsWith('/drawing') || xmlAttribute(attrs, 'TargetMode') === 'External') continue;
|
|
64
60
|
const target = xmlAttribute(attrs, 'Target');
|
|
65
61
|
const part = target.startsWith('/') ? target.slice(1) : posix.normalize(posix.join(posix.dirname(sheet.path), target));
|
|
66
62
|
const drawing = await zipText(zip, part);
|
|
67
|
-
for (const anchor of drawing.matchAll(/<xdr:(absoluteAnchor|oneCellAnchor|twoCellAnchor)\b[^>]*>([\s\S]*?)<\/xdr:\1>/g)) {
|
|
63
|
+
for (const anchor of (drawing || '').matchAll(/<xdr:(absoluteAnchor|oneCellAnchor|twoCellAnchor)\b[^>]*>([\s\S]*?)<\/xdr:\1>/g)) {
|
|
64
|
+
const kind = anchor[1];
|
|
68
65
|
const body = anchor[2];
|
|
69
66
|
const marker = (tag) => {
|
|
70
67
|
const value = new RegExp(`<xdr:${tag}>([\\s\\S]*?)<\\/xdr:${tag}>`).exec(body)?.[1] || '';
|
|
71
68
|
const number = (name) => Number(new RegExp(`<xdr:${name}>(\\d+)<\\/xdr:${name}>`).exec(value)?.[1]) || 0;
|
|
72
69
|
return { column: number('col'), row: number('row'), x: number('colOff') / 12700, y: number('rowOff') / 12700 };
|
|
73
70
|
};
|
|
74
|
-
|
|
71
|
+
const start = kind === 'absoluteAnchor' ? null : marker('from');
|
|
72
|
+
if (start && (start.column >= 16_384 || start.row >= 1_048_576)) {
|
|
73
|
+
throw new Error('Worksheet drawing anchor exceeds the Excel grid');
|
|
74
|
+
}
|
|
75
|
+
if (kind === 'twoCellAnchor') {
|
|
75
76
|
const end = marker('to');
|
|
76
77
|
if (end.column >= 16_384 || end.row >= 1_048_576) throw new Error('Worksheet drawing anchor exceeds the Excel grid');
|
|
77
|
-
|
|
78
|
-
|
|
78
|
+
drawings.push({
|
|
79
|
+
part,
|
|
80
|
+
kind,
|
|
81
|
+
body,
|
|
82
|
+
startColumn: start.column + 1,
|
|
83
|
+
startRow: start.row + 1,
|
|
84
|
+
endColumn: end.column + (end.x > 0 ? 1 : 0),
|
|
85
|
+
endRow: end.row + (end.y > 0 ? 1 : 0),
|
|
86
|
+
});
|
|
79
87
|
continue;
|
|
80
88
|
}
|
|
81
89
|
const extent = /<xdr:ext\b([^>]*?)\/>/.exec(body)?.[1] || '';
|
|
82
90
|
let x = 0;
|
|
83
91
|
let y = 0;
|
|
84
|
-
if (
|
|
92
|
+
if (kind === 'absoluteAnchor') {
|
|
85
93
|
const position = /<xdr:pos\b([^>]*?)\/>/.exec(body)?.[1] || '';
|
|
86
94
|
x = Number(xmlAttribute(position, 'x')) / 12700 || 0;
|
|
87
95
|
y = Number(xmlAttribute(position, 'y')) / 12700 || 0;
|
|
88
96
|
} else {
|
|
89
|
-
const start = marker('from');
|
|
90
|
-
if (start.column >= 16_384 || start.row >= 1_048_576) throw new Error('Worksheet drawing anchor exceeds the Excel grid');
|
|
91
97
|
x = start.x;
|
|
92
98
|
y = start.y;
|
|
93
99
|
for (let index = 1; index <= start.column; index += 1) x += geometry.columnPoints(index);
|
|
94
100
|
for (let index = 1; index <= start.row; index += 1) y += geometry.rowPoints(index);
|
|
95
101
|
}
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
const width = (Number(xmlAttribute(extent, 'cx')) || 0) / 12700;
|
|
103
|
+
const height = (Number(xmlAttribute(extent, 'cy')) || 0) / 12700;
|
|
104
|
+
drawings.push({
|
|
105
|
+
part,
|
|
106
|
+
kind,
|
|
107
|
+
body,
|
|
108
|
+
startColumn: geometry.columnAt(x),
|
|
109
|
+
startRow: geometry.rowAt(y),
|
|
110
|
+
endColumn: geometry.columnAt(x + width, { trailing: true }),
|
|
111
|
+
endRow: geometry.rowAt(y + height, { trailing: true }),
|
|
112
|
+
left: x,
|
|
113
|
+
top: y,
|
|
114
|
+
width,
|
|
115
|
+
height,
|
|
116
|
+
});
|
|
98
117
|
}
|
|
99
118
|
}
|
|
119
|
+
return drawings;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export async function contentPrintArea(zip, sheet, xml) {
|
|
123
|
+
let lastColumn = 1;
|
|
124
|
+
let lastRow = 1;
|
|
125
|
+
for (const cell of cellRecords(xml, await sharedStrings(zip))) {
|
|
126
|
+
if (!cell.formula && (cell.value == null || cell.value === '')) continue;
|
|
127
|
+
const parsed = parseCellRef(cell.ref);
|
|
128
|
+
lastColumn = Math.max(lastColumn, columnNumber(parsed.col));
|
|
129
|
+
lastRow = Math.max(lastRow, parsed.row);
|
|
130
|
+
}
|
|
131
|
+
for (const reference of mergedRanges(xml)) {
|
|
132
|
+
const area = parseAreaRange(reference);
|
|
133
|
+
lastColumn = Math.max(lastColumn, area.endCol);
|
|
134
|
+
lastRow = Math.max(lastRow, area.endRow);
|
|
135
|
+
}
|
|
136
|
+
for (const drawing of await worksheetDrawings(zip, sheet, xml)) {
|
|
137
|
+
lastColumn = Math.max(lastColumn, drawing.endColumn);
|
|
138
|
+
lastRow = Math.max(lastRow, drawing.endRow);
|
|
139
|
+
}
|
|
100
140
|
return `A1:${columnLabel(lastColumn)}${lastRow}`;
|
|
101
141
|
}
|
|
102
142
|
|
|
143
|
+
// A chart or picture beside a table is wider than a portrait page, and a sheet
|
|
144
|
+
// with no page setup exports by column blocks — the page break runs through the
|
|
145
|
+
// chart. A sheet that declares nothing takes one page wide and keeps paging down
|
|
146
|
+
// (a fit only scales down, so a small sheet prints as before); a declared fit,
|
|
147
|
+
// print scale, or print area is the author's and stays.
|
|
148
|
+
export function fitDrawingSheetOnePageWide(xml) {
|
|
149
|
+
if (/<pageSetUpPr\b[^>]*\bfitToPage="1"/.test(xml)) return { xml, applied: false };
|
|
150
|
+
const setup = worksheetSection(xml, 'pageSetup');
|
|
151
|
+
if (setup && /\bscale="/.test(setup[0])) return { xml, applied: false };
|
|
152
|
+
const sheetPr = worksheetSection(xml, 'sheetPr');
|
|
153
|
+
const attrs = sheetPr ? /^<sheetPr\b([^>]*?)(?:\/>|>)/.exec(sheetPr[0])?.[1] || '' : '';
|
|
154
|
+
const body = sheetPr && !sheetPr[0].endsWith('/>')
|
|
155
|
+
? sheetPr[0].slice(sheetPr[0].indexOf('>') + 1, sheetPr[0].lastIndexOf('</sheetPr>')) : '';
|
|
156
|
+
let next = upsertWorksheetSection(xml, 'sheetPr', `<sheetPr${attrs}>${body.replace(/<pageSetUpPr\b[^>]*?\/>/, '')}<pageSetUpPr fitToPage="1"/></sheetPr>`);
|
|
157
|
+
const existing = setup ? setup[0].replace(/\s+fitTo(?:Width|Height)="[^"]*"/g, '') : '<pageSetup/>';
|
|
158
|
+
next = upsertWorksheetSection(next, 'pageSetup', existing.replace(/\/?>$/, ' fitToWidth="1" fitToHeight="0"/>'));
|
|
159
|
+
return { xml: next, applied: true };
|
|
160
|
+
}
|
|
161
|
+
|
|
103
162
|
export async function applyWorksheetPageSetup(zip, sheets, sheet, xml, op) {
|
|
104
163
|
const orientation = String(op.orientation || '').toLowerCase();
|
|
105
164
|
if (orientation && !['portrait', 'landscape'].includes(orientation)) {
|
|
@@ -107,6 +166,12 @@ export async function applyWorksheetPageSetup(zip, sheets, sheet, xml, op) {
|
|
|
107
166
|
}
|
|
108
167
|
const fitWide = Number(op.fitToPagesWide) || 0;
|
|
109
168
|
const fitTall = op.fitToPagesTall == null ? null : Number(op.fitToPagesTall) || 0;
|
|
169
|
+
// A call that sets only the print area or margins keeps the fit the sheet
|
|
170
|
+
// already declares: with fitToPage on and the counts dropped, Excel would
|
|
171
|
+
// read the default 1 × 1 and shrink a long sheet onto one page.
|
|
172
|
+
const carriedFit = fitWide || fitTall != null
|
|
173
|
+
? ''
|
|
174
|
+
: (worksheetSection(xml, 'pageSetup')?.[0].match(/\s+fitTo(?:Width|Height)="[^"]*"/g) || []).join('');
|
|
110
175
|
if (fitWide || fitTall != null) {
|
|
111
176
|
const existing = worksheetSection(xml, 'sheetPr');
|
|
112
177
|
const attrs = existing ? /^<sheetPr\b([^>]*?)(?:\/>|>)/.exec(existing[0])?.[1] || '' : '';
|
|
@@ -118,7 +183,7 @@ export async function applyWorksheetPageSetup(zip, sheets, sheet, xml, op) {
|
|
|
118
183
|
xml = upsertWorksheetSection(xml, 'printOptions', centered ? `<printOptions${centered}/>` : '');
|
|
119
184
|
const margin = (value, fallback) => Number.isFinite(Number(value)) && Number(value) >= 0 ? Number(value) : fallback;
|
|
120
185
|
xml = upsertWorksheetSection(xml, 'pageMargins', `<pageMargins left="${margin(op.leftMargin, 0.7)}" right="${margin(op.rightMargin, 0.7)}" top="${margin(op.topMargin, 0.75)}" bottom="${margin(op.bottomMargin, 0.75)}" header="0.3" footer="0.3"/>`);
|
|
121
|
-
xml = upsertWorksheetSection(xml, 'pageSetup', `<pageSetup paperSize="9"${orientation ? ` orientation="${orientation}"` : ''}${fitWide ? ` fitToWidth="${fitWide}"` : ''}${fitTall == null ? '' : ` fitToHeight="${fitTall}"`}/>`);
|
|
186
|
+
xml = upsertWorksheetSection(xml, 'pageSetup', `<pageSetup paperSize="9"${orientation ? ` orientation="${orientation}"` : ''}${fitWide ? ` fitToWidth="${fitWide}"` : ''}${fitTall == null ? '' : ` fitToHeight="${fitTall}"`}${carriedFit}/>`);
|
|
122
187
|
const printArea = op.fitToContent === true ? await contentPrintArea(zip, sheet, xml) : op.printArea;
|
|
123
188
|
if (printArea) {
|
|
124
189
|
const area = parseAreaRange(printArea);
|
|
@@ -109,9 +109,52 @@ function buildFill(color) {
|
|
|
109
109
|
: '<fill><patternFill patternType="none"/></fill>';
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
+
// A cell's four edges: the rule under a header, the rule over a total, the frame of an input block.
|
|
113
|
+
// Each side is { style, color }; a side the caller does not name keeps what the cell had.
|
|
114
|
+
const BORDER_SIDES = Object.freeze(['left', 'right', 'top', 'bottom']);
|
|
115
|
+
const BORDER_STYLES = Object.freeze(['none', 'hair', 'thin', 'medium', 'thick', 'dashed', 'dotted', 'double']);
|
|
116
|
+
|
|
117
|
+
function parseBorder(xml) {
|
|
118
|
+
const border = {};
|
|
119
|
+
for (const side of BORDER_SIDES) {
|
|
120
|
+
const element = new RegExp(`<${side}\\b([^>]*?)(?:\\/>|>([\\s\\S]*?)<\\/${side}>)`).exec(xml || '');
|
|
121
|
+
const style = element ? attribute(element[1], 'style') : '';
|
|
122
|
+
border[side] = style && style !== 'none'
|
|
123
|
+
? { style, color: normalizeColor(attribute(/<color\b([^>]*?)\/>/.exec(element[2] || '')?.[1], 'rgb')) }
|
|
124
|
+
: null;
|
|
125
|
+
}
|
|
126
|
+
return border;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function buildBorder(border) {
|
|
130
|
+
const side = (name) => {
|
|
131
|
+
const edge = border[name];
|
|
132
|
+
if (!edge) return `<${name}/>`;
|
|
133
|
+
return `<${name} style="${edge.style}">${edge.color ? `<color rgb="${edge.color}"/>` : '<color auto="1"/>'}</${name}>`;
|
|
134
|
+
};
|
|
135
|
+
return `<border>${side('left')}${side('right')}${side('top')}${side('bottom')}<diagonal/></border>`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function mergeBorders(base, requested) {
|
|
139
|
+
const next = { ...base };
|
|
140
|
+
const sides = requested && typeof requested === 'object' && !Array.isArray(requested) ? requested : {};
|
|
141
|
+
// One spec for every side (`borders: { style: 'thin', color }`) or a spec per side.
|
|
142
|
+
const uniform = BORDER_SIDES.every((name) => sides[name] === undefined) && (sides.style || sides.color);
|
|
143
|
+
for (const name of BORDER_SIDES) {
|
|
144
|
+
const spec = uniform ? sides : sides[name];
|
|
145
|
+
if (spec === undefined) continue;
|
|
146
|
+
if (spec === null || spec === false || spec === 'none' || spec?.style === 'none' || spec?.enabled === false) { next[name] = null; continue; }
|
|
147
|
+
const style = String(spec?.style || 'thin').toLowerCase();
|
|
148
|
+
if (!BORDER_STYLES.includes(style)) throw new Error(`set_style borders.${name}.style "${style}" is not one of ${BORDER_STYLES.join(', ')}`);
|
|
149
|
+
next[name] = { style, color: normalizeColor(spec?.color) };
|
|
150
|
+
}
|
|
151
|
+
return next;
|
|
152
|
+
}
|
|
153
|
+
|
|
112
154
|
function parseXf(xml) {
|
|
113
155
|
const attrs = /^<xf\b([^>]*?)(?:\/>|>)/.exec(xml)?.[1] || '';
|
|
114
156
|
const alignment = /<alignment\b([^>]*?)\/?>/.exec(xml)?.[1] || '';
|
|
157
|
+
const protection = /<protection\b([^>]*?)\/?>/.exec(xml)?.[1] || '';
|
|
115
158
|
return {
|
|
116
159
|
numFmtId: Number(attribute(attrs, 'numFmtId')) || 0,
|
|
117
160
|
fontId: Number(attribute(attrs, 'fontId')) || 0,
|
|
@@ -121,6 +164,9 @@ function parseXf(xml) {
|
|
|
121
164
|
horizontal: attribute(alignment, 'horizontal'),
|
|
122
165
|
vertical: attribute(alignment, 'vertical'),
|
|
123
166
|
wrapText: attribute(alignment, 'wrapText') === '1',
|
|
167
|
+
// Every cell is locked until told otherwise; the flag only takes effect
|
|
168
|
+
// once the sheet itself is protected.
|
|
169
|
+
locked: attribute(protection, 'locked') !== '0',
|
|
124
170
|
};
|
|
125
171
|
}
|
|
126
172
|
|
|
@@ -131,14 +177,18 @@ function buildXf(xf) {
|
|
|
131
177
|
+ `${xf.vertical ? ` vertical="${xf.vertical}"` : ''}`
|
|
132
178
|
+ `${xf.wrapText ? ' wrapText="1"' : ''}/>`
|
|
133
179
|
: '';
|
|
180
|
+
// An unlocked cell is how a protected sheet keeps its entry fields typable.
|
|
181
|
+
const protection = xf.locked === false ? '<protection locked="0"/>' : '';
|
|
134
182
|
const head = `<xf numFmtId="${xf.numFmtId}" fontId="${xf.fontId}" fillId="${xf.fillId}"`
|
|
135
183
|
+ ` borderId="${xf.borderId}" xfId="${xf.xfId}"`
|
|
136
184
|
+ `${xf.numFmtId ? ' applyNumberFormat="1"' : ''}`
|
|
137
185
|
+ `${xf.fontId ? ' applyFont="1"' : ''}`
|
|
138
186
|
+ `${xf.fillId ? ' applyFill="1"' : ''}`
|
|
139
187
|
+ `${xf.borderId ? ' applyBorder="1"' : ''}`
|
|
140
|
-
+ `${aligned ? ' applyAlignment="1"' : ''}
|
|
141
|
-
|
|
188
|
+
+ `${aligned ? ' applyAlignment="1"' : ''}`
|
|
189
|
+
+ `${protection ? ' applyProtection="1"' : ''}`;
|
|
190
|
+
const body = `${alignment}${protection}`;
|
|
191
|
+
return body ? `${head}>${body}</xf>` : `${head}/>`;
|
|
142
192
|
}
|
|
143
193
|
|
|
144
194
|
function register(items, candidate) {
|
|
@@ -218,6 +268,11 @@ export function applyCellStyle(stylesXml, baseIndex, properties = {}) {
|
|
|
218
268
|
next.fillId = register(sections.fills, buildFill(fill));
|
|
219
269
|
}
|
|
220
270
|
|
|
271
|
+
if (Object.hasOwn(properties, 'borders')) {
|
|
272
|
+
const merged = mergeBorders(parseBorder(sections.borders[base.borderId] || ''), properties.borders);
|
|
273
|
+
next.borderId = BORDER_SIDES.some((name) => merged[name]) ? register(sections.borders, buildBorder(merged)) : 0;
|
|
274
|
+
}
|
|
275
|
+
|
|
221
276
|
if (Object.hasOwn(properties, 'numberFormat')) {
|
|
222
277
|
next.numFmtId = registerNumberFormat(sections.numFmts, properties.numberFormat);
|
|
223
278
|
}
|
|
@@ -228,6 +283,7 @@ export function applyCellStyle(stylesXml, baseIndex, properties = {}) {
|
|
|
228
283
|
next.vertical = VERTICAL[String(properties.verticalAlignment).toLowerCase()] || '';
|
|
229
284
|
}
|
|
230
285
|
if (Object.hasOwn(properties, 'wrapText')) next.wrapText = properties.wrapText === true;
|
|
286
|
+
if (Object.hasOwn(properties, 'locked')) next.locked = properties.locked !== false;
|
|
231
287
|
|
|
232
288
|
const index = register(sections.cellXfs, buildXf(next));
|
|
233
289
|
return { xml: serialize(stylesXml, sections), index };
|
|
@@ -299,6 +355,7 @@ export function resolveCellStyles(stylesXml) {
|
|
|
299
355
|
...(font.color ? { color: font.color.slice(-6) } : {}),
|
|
300
356
|
...(fill ? { fillColor: fill.slice(-6) } : {}),
|
|
301
357
|
...(xf.horizontal ? { horizontalAlignment: xf.horizontal } : {}),
|
|
358
|
+
...(xf.locked === false ? { locked: false } : {}),
|
|
302
359
|
};
|
|
303
360
|
});
|
|
304
361
|
}
|