pmx-canvas 0.8.0 → 0.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/extensions/pmx-canvas/runtime.json +1 -1
- package/.github/extensions/pmx-canvas/tools.json +601 -22
- package/CHANGELOG.md +66 -0
- package/Readme.md +131 -532
- package/dist/canvas/global.css +2759 -510
- package/dist/canvas/index.js +168 -107
- package/dist/canvas/mcp-app.html +2936 -632
- package/dist/json-render/index.css +81 -1
- package/dist/json-render/index.js +20 -20
- package/dist/types/client/canvas/BarHint.d.ts +12 -2
- package/dist/types/client/canvas/BoardLinkDialog.d.ts +3 -1
- package/dist/types/client/canvas/BoardPinButton.d.ts +10 -0
- package/dist/types/client/canvas/BoardSwitcher.d.ts +1 -1
- package/dist/types/client/canvas/CanvasViewport.d.ts +1 -1
- package/dist/types/client/canvas/CommandBar.d.ts +3 -4
- package/dist/types/client/canvas/ConnectionBanner.d.ts +2 -1
- package/dist/types/client/canvas/ContextChip.d.ts +3 -0
- package/dist/types/client/canvas/CrossBoardReferences.d.ts +5 -0
- package/dist/types/client/canvas/EdgeLayer.d.ts +14 -3
- package/dist/types/client/canvas/EmptyState.d.ts +4 -4
- package/dist/types/client/canvas/HomeGraphView.d.ts +2 -0
- package/dist/types/client/canvas/HomeMapView.d.ts +1 -0
- package/dist/types/client/canvas/HomeReviewView.d.ts +6 -0
- package/dist/types/client/canvas/HomeView.d.ts +9 -7
- package/dist/types/client/canvas/KnowledgeDialog.d.ts +4 -0
- package/dist/types/client/canvas/LifecycleChrome.d.ts +5 -0
- package/dist/types/client/canvas/LifecycleFields.d.ts +7 -0
- package/dist/types/client/canvas/LifecyclePanel.d.ts +1 -0
- package/dist/types/client/canvas/MemoryInspector.d.ts +7 -0
- package/dist/types/client/canvas/NearPinLayer.d.ts +14 -0
- package/dist/types/client/canvas/NodeContextMark.d.ts +40 -7
- package/dist/types/client/canvas/RelationPresetsDialog.d.ts +4 -0
- package/dist/types/client/canvas/RelationsPanel.d.ts +4 -0
- package/dist/types/client/canvas/SessionPanel.d.ts +2 -0
- package/dist/types/client/canvas/canvas-area.d.ts +1 -1
- package/dist/types/client/canvas/graph-geometry.d.ts +27 -0
- package/dist/types/client/canvas/graph-model.d.ts +37 -0
- package/dist/types/client/canvas/memory-layout.d.ts +23 -0
- package/dist/types/client/canvas/use-chrome-height.d.ts +2 -0
- package/dist/types/client/canvas/use-focus-trap.d.ts +1 -0
- package/dist/types/client/canvas/use-memory-graph.d.ts +8 -0
- package/dist/types/client/canvas/use-pan-zoom.d.ts +4 -1
- package/dist/types/client/icons.d.ts +0 -10
- package/dist/types/client/nodes/AxStepControls.d.ts +1 -1
- package/dist/types/client/nodes/HtmlNode.d.ts +1 -0
- package/dist/types/client/nodes/InlineMarkdownEditor.d.ts +4 -4
- package/dist/types/client/nodes/MarkdownNode.d.ts +5 -1
- package/dist/types/client/nodes/McpAppNode.d.ts +4 -4
- package/dist/types/client/nodes/viewer-runtime-state.d.ts +1 -1
- package/dist/types/client/state/boards-store.d.ts +81 -9
- package/dist/types/client/state/canvas-store.d.ts +14 -1
- package/dist/types/client/state/context-chip-store.d.ts +22 -0
- package/dist/types/client/state/context-status-store.d.ts +35 -0
- package/dist/types/client/state/intent-bridge.d.ts +11 -8
- package/dist/types/client/state/knowledge-store.d.ts +22 -0
- package/dist/types/client/state/near-pin-store.d.ts +44 -1
- package/dist/types/client/state/presence-store.d.ts +4 -4
- package/dist/types/client/state/presentation.d.ts +28 -0
- package/dist/types/client/state/relation-store.d.ts +10 -0
- package/dist/types/client/state/session-store.d.ts +41 -2
- package/dist/types/client/state/workspace-settings.d.ts +7 -0
- package/dist/types/client/types.d.ts +13 -1
- package/dist/types/client/utils/keyboard.d.ts +2 -0
- package/dist/types/client/utils/time-ago.d.ts +4 -0
- package/dist/types/mcp/context-uri-template.d.ts +3 -1
- package/dist/types/server/agent-presence.d.ts +10 -3
- package/dist/types/server/ax-context.d.ts +1 -1
- package/dist/types/server/canvas-db.d.ts +30 -7
- package/dist/types/server/canvas-operations.d.ts +24 -2
- package/dist/types/server/canvas-provenance.d.ts +22 -2
- package/dist/types/server/canvas-serialization.d.ts +2 -0
- package/dist/types/server/canvas-state.d.ts +93 -15
- package/dist/types/server/canvas-validation.d.ts +1 -1
- package/dist/types/server/context-brief.d.ts +94 -6
- package/dist/types/server/context-reads.d.ts +33 -7
- package/dist/types/server/edit-summary.d.ts +20 -0
- package/dist/types/server/index.d.ts +71 -10
- package/dist/types/server/knowledge-lifecycle.d.ts +221 -0
- package/dist/types/server/lifecycle-status.d.ts +10 -0
- package/dist/types/server/memory-graph.d.ts +9 -0
- package/dist/types/server/mutation-history.d.ts +2 -0
- package/dist/types/server/operations/ops/ax-shared.d.ts +1 -1
- package/dist/types/server/operations/ops/boards.d.ts +2 -1
- package/dist/types/server/operations/ops/lifecycle.d.ts +2 -0
- package/dist/types/server/operations/ops/memory-graph.d.ts +2 -0
- package/dist/types/server/operations/ops/nodes.d.ts +5 -0
- package/dist/types/server/operations/ops/query.d.ts +23 -0
- package/dist/types/server/operations/registry.d.ts +1 -0
- package/dist/types/server/placement.d.ts +7 -0
- package/dist/types/server/scope-fence.d.ts +2 -1
- package/dist/types/server/spatial-analysis.d.ts +6 -3
- package/dist/types/server/tour-control.d.ts +32 -0
- package/dist/types/server/wiki-links.d.ts +14 -0
- package/dist/types/server/workbench-auth.d.ts +2 -0
- package/dist/types/shared/agent-context.d.ts +2 -0
- package/dist/types/shared/agent-presence.d.ts +15 -12
- package/dist/types/shared/auto-arrange.d.ts +1 -0
- package/dist/types/shared/boards.d.ts +4 -4
- package/dist/types/shared/knowledge-lifecycle.d.ts +41 -0
- package/dist/types/shared/knowledge-presets.d.ts +6 -0
- package/dist/types/shared/lifecycle-status.d.ts +24 -0
- package/dist/types/shared/memory-graph.d.ts +53 -0
- package/dist/types/shared/relations.d.ts +68 -0
- package/dist/types/shared/semantic-attention.d.ts +2 -0
- package/dist/types/shared/tour.d.ts +38 -0
- package/dist/types/shared/workspace-settings.d.ts +11 -0
- package/docs/RELEASE.md +21 -3
- package/docs/adr-001-bun-only-runtime.md +7 -1
- package/docs/adr-002-shared-product-separate-runtimes.md +93 -0
- package/docs/api-reference.md +109 -0
- package/docs/ax-host-adapter-contract.md +1 -1
- package/docs/ax-state-contract.md +5 -4
- package/docs/cli.md +153 -15
- package/docs/demos/pmx-canvas-demo.mp4 +0 -0
- package/docs/design/AgentContext.dc.html +3 -3
- package/docs/design/BoardPins.dc.html +37 -22
- package/docs/design/Categories.dc.html +208 -0
- package/docs/design/CategoryEditor.dc.html +33 -0
- package/docs/design/Chrome.dc.html +55 -9
- package/docs/design/ContextChip.dc.html +224 -0
- package/docs/design/ContextChip600.dc.html +136 -0
- package/docs/design/EmptyState.dc.html +45 -0
- package/docs/design/Flows.dc.html +23 -1
- package/docs/design/HeaderMarks.dc.html +183 -0
- package/docs/design/Home.dc.html +19 -1
- package/docs/design/HomeGraph.dc.html +86 -202
- package/docs/design/HomeMap.dc.html +23 -1
- package/docs/design/HomeTeam.dc.html +32 -14
- package/docs/design/Lifecycle.dc.html +22 -0
- package/docs/design/Lifecycle600.dc.html +18 -0
- package/docs/design/LifecycleVariants.dc.html +157 -0
- package/docs/design/LinkAuthorship.dc.html +139 -0
- package/docs/design/LinksChanged.dc.html +203 -0
- package/docs/design/LinksOptions.dc.html +137 -0
- package/docs/design/LinksOptions600.dc.html +134 -0
- package/docs/design/NearPin.dc.html +9 -10
- package/docs/design/Pane600.dc.html +4 -3
- package/docs/design/Relations.dc.html +3 -2
- package/docs/design/Restore.dc.html +152 -0
- package/docs/design/SeeChange.dc.html +267 -0
- package/docs/design/Uses.dc.html +12 -6
- package/docs/design/canvas.json +146 -19
- package/docs/design.md +454 -18
- package/docs/environment.md +0 -1
- package/docs/evals/curation-effect.md +128 -0
- package/docs/http-api.md +465 -38
- package/docs/mcp.md +150 -19
- package/docs/node-types.md +58 -3
- package/docs/plans/design-gaps-2026-10-08.md +53 -0
- package/docs/plans/plan-009-tech-debt-backlog.md +379 -0
- package/docs/plans/plan-016-context-made-visible-wave-1.md +150 -5
- package/docs/plans/plan-017-knowledge-categories-and-lifecycle.md +175 -0
- package/docs/plans/plan-018-connected-memory-wave-2.md +594 -0
- package/docs/plans/plan-019-post-0.9-roadmap.md +198 -0
- package/docs/product-context-vision-2026-09.md +17 -1
- package/docs/product-vision-2026-09.md +1082 -43
- package/docs/product-vision-review-2026-09.md +13 -1
- package/docs/screenshots/workbench-live-dark.png +0 -0
- package/docs/screenshots/workbench-live-light.png +0 -0
- package/docs/sdk.md +62 -2
- package/package.json +1 -1
- package/plugins/pmx-canvas/com.github.copilot/extensions/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/plugin.json +1 -1
- package/plugins/pmx-canvas/runtime.json +1 -1
- package/plugins/pmx-canvas/tools.json +601 -22
- package/skills/pmx-canvas/SKILL.md +234 -43
- package/skills/pmx-canvas/evals/evals.json +62 -0
- package/skills/pmx-canvas/references/ax-html-control-surface.md +6 -0
- package/skills/pmx-canvas/references/full-reference.md +120 -21
- package/skills/pmx-canvas/references/github-copilot-app-adapter.md +2 -2
- package/skills/pmx-canvas/references/importing-context.md +132 -0
- package/skills/pmx-canvas/references/installing-pmx-canvas.md +6 -6
- package/skills/pmx-canvas/references/tours-and-recording.md +16 -1
- package/skills/pmx-canvas-testing/SKILL.md +20 -0
- package/src/cli/agent.ts +2 -0
- package/src/cli/commands/ax.ts +12 -0
- package/src/cli/commands/boards.ts +87 -11
- package/src/cli/commands/edges.ts +28 -0
- package/src/cli/commands/groups.ts +7 -0
- package/src/cli/commands/nodes.ts +4 -2
- package/src/cli/commands/query.ts +34 -2
- package/src/cli/commands/record.ts +199 -48
- package/src/cli/index.ts +7 -1
- package/src/client/App.tsx +177 -70
- package/src/client/canvas/AttentionHistory.tsx +0 -2
- package/src/client/canvas/BarHint.tsx +40 -2
- package/src/client/canvas/BoardCopyDialog.tsx +1 -1
- package/src/client/canvas/BoardFolderDialog.tsx +7 -7
- package/src/client/canvas/BoardLinkDialog.tsx +138 -33
- package/src/client/canvas/BoardPinButton.tsx +33 -0
- package/src/client/canvas/BoardSwitcher.tsx +26 -40
- package/src/client/canvas/CanvasNode.tsx +360 -237
- package/src/client/canvas/CanvasViewport.tsx +45 -7
- package/src/client/canvas/CommandBar.tsx +29 -23
- package/src/client/canvas/CommandPalette.tsx +5 -12
- package/src/client/canvas/ConnectionBanner.tsx +3 -3
- package/src/client/canvas/ContextChip.tsx +296 -0
- package/src/client/canvas/ContextMenu.tsx +63 -5
- package/src/client/canvas/CrossBoardReferences.tsx +60 -0
- package/src/client/canvas/EdgeLayer.tsx +339 -76
- package/src/client/canvas/EmptyState.tsx +33 -18
- package/src/client/canvas/ExpandedNodeOverlay.tsx +72 -40
- package/src/client/canvas/ExternalWriters.tsx +57 -42
- package/src/client/canvas/HomeGraphView.tsx +942 -0
- package/src/client/canvas/HomeMapView.tsx +641 -0
- package/src/client/canvas/HomeReviewView.tsx +167 -0
- package/src/client/canvas/HomeView.tsx +687 -160
- package/src/client/canvas/IntentLayer.tsx +19 -12
- package/src/client/canvas/KnowledgeDialog.tsx +488 -0
- package/src/client/canvas/LifecycleChrome.tsx +133 -0
- package/src/client/canvas/LifecycleFields.tsx +91 -0
- package/src/client/canvas/LifecyclePanel.tsx +533 -0
- package/src/client/canvas/MemoryInspector.tsx +104 -0
- package/src/client/canvas/Minimap.tsx +3 -3
- package/src/client/canvas/NearPinLayer.tsx +187 -0
- package/src/client/canvas/NodeContextMark.tsx +175 -97
- package/src/client/canvas/Presentation.tsx +23 -57
- package/src/client/canvas/RelationPresetsDialog.tsx +413 -0
- package/src/client/canvas/RelationsPanel.tsx +82 -0
- package/src/client/canvas/SelectionBar.tsx +3 -6
- package/src/client/canvas/SessionPanel.tsx +90 -50
- package/src/client/canvas/SessionReceipt.tsx +311 -39
- package/src/client/canvas/ShortcutOverlay.tsx +1 -1
- package/src/client/canvas/SnapshotPanel.tsx +27 -36
- package/src/client/canvas/ToolRail.tsx +367 -192
- package/src/client/canvas/TopBar.tsx +354 -124
- package/src/client/canvas/canvas-area.ts +10 -3
- package/src/client/canvas/graph-geometry.ts +195 -0
- package/src/client/canvas/graph-model.ts +254 -0
- package/src/client/canvas/memory-layout.ts +106 -0
- package/src/client/canvas/use-chrome-height.ts +20 -0
- package/src/client/canvas/use-focus-trap.ts +31 -5
- package/src/client/canvas/use-memory-graph.ts +58 -0
- package/src/client/canvas/use-node-drag.ts +2 -1
- package/src/client/canvas/use-pan-zoom.ts +64 -7
- package/src/client/icons.tsx +0 -63
- package/src/client/mcp-app/style.css +0 -6
- package/src/client/nodes/AxStepControls.tsx +74 -50
- package/src/client/nodes/BoardNode.tsx +5 -1
- package/src/client/nodes/ContextNode.tsx +18 -11
- package/src/client/nodes/ExtAppFrame.tsx +0 -1
- package/src/client/nodes/FileNode.tsx +46 -42
- package/src/client/nodes/HtmlNode.tsx +44 -2
- package/src/client/nodes/ImageNode.tsx +1 -1
- package/src/client/nodes/InlineFormatBar.tsx +6 -9
- package/src/client/nodes/InlineMarkdownEditor.tsx +56 -17
- package/src/client/nodes/MarkdownNode.tsx +377 -109
- package/src/client/nodes/McpAppNode.tsx +50 -20
- package/src/client/nodes/MdFormatBar.tsx +21 -18
- package/src/client/nodes/PromptNode.tsx +1 -1
- package/src/client/nodes/ResponseNode.tsx +1 -1
- package/src/client/nodes/viewer-runtime-state.ts +54 -22
- package/src/client/state/attention-bridge.ts +64 -15
- package/src/client/state/boards-store.ts +193 -39
- package/src/client/state/canvas-store.ts +99 -12
- package/src/client/state/context-chip-store.ts +70 -0
- package/src/client/state/context-status-store.ts +53 -0
- package/src/client/state/intent-bridge.ts +51 -30
- package/src/client/state/knowledge-store.ts +43 -0
- package/src/client/state/near-pin-store.ts +115 -6
- package/src/client/state/presence-store.ts +7 -10
- package/src/client/state/presentation.ts +91 -0
- package/src/client/state/relation-store.ts +81 -0
- package/src/client/state/session-store.ts +144 -18
- package/src/client/state/sse-bridge.ts +206 -29
- package/src/client/state/workspace-settings.ts +11 -0
- package/src/client/theme/global.css +2759 -510
- package/src/client/types.ts +12 -0
- package/src/client/utils/keyboard.ts +10 -0
- package/src/client/utils/time-ago.ts +14 -0
- package/src/json-render/renderer/index.css +6 -1
- package/src/json-render/renderer/index.tsx +31 -1
- package/src/mcp/canvas-app.ts +12 -2
- package/src/mcp/context-uri-template.ts +7 -4
- package/src/mcp/server.ts +63 -41
- package/src/mcp/workbench-app.ts +39 -6
- package/src/server/agent-presence.ts +128 -28
- package/src/server/ax-context.ts +27 -13
- package/src/server/board-export.ts +12 -1
- package/src/server/canvas-db.ts +331 -42
- package/src/server/canvas-operations.ts +196 -84
- package/src/server/canvas-provenance.ts +53 -22
- package/src/server/canvas-serialization.ts +2 -0
- package/src/server/canvas-state.ts +950 -122
- package/src/server/canvas-validation.ts +9 -2
- package/src/server/context-brief.ts +345 -38
- package/src/server/context-reads.ts +179 -30
- package/src/server/demo-state.json +355 -24
- package/src/server/edit-summary.ts +74 -0
- package/src/server/html-surface.ts +8 -1
- package/src/server/index.ts +203 -13
- package/src/server/knowledge-lifecycle.ts +304 -0
- package/src/server/lifecycle-status.ts +232 -0
- package/src/server/memory-graph.ts +166 -0
- package/src/server/mutation-history.ts +18 -6
- package/src/server/operations/composites.ts +32 -11
- package/src/server/operations/http.ts +7 -2
- package/src/server/operations/index.ts +4 -0
- package/src/server/operations/mcp.ts +32 -1
- package/src/server/operations/ops/ax-presence.ts +1 -1
- package/src/server/operations/ops/ax-read.ts +57 -6
- package/src/server/operations/ops/ax-timeline.ts +28 -2
- package/src/server/operations/ops/backup.ts +5 -3
- package/src/server/operations/ops/batch.ts +35 -4
- package/src/server/operations/ops/boards.ts +188 -47
- package/src/server/operations/ops/canvas-wire.ts +31 -1
- package/src/server/operations/ops/edges.ts +36 -2
- package/src/server/operations/ops/groups.ts +6 -1
- package/src/server/operations/ops/json-render.ts +5 -1
- package/src/server/operations/ops/lifecycle.ts +262 -0
- package/src/server/operations/ops/memory-graph.ts +45 -0
- package/src/server/operations/ops/nodes.ts +107 -24
- package/src/server/operations/ops/query.ts +281 -68
- package/src/server/operations/ops/snapshots.ts +73 -1
- package/src/server/operations/ops/validate.ts +5 -1
- package/src/server/operations/ops/viewport.ts +97 -3
- package/src/server/operations/registry.ts +77 -26
- package/src/server/placement.ts +29 -0
- package/src/server/scope-fence.ts +78 -6
- package/src/server/server.ts +115 -16
- package/src/server/spatial-analysis.ts +48 -23
- package/src/server/tour-control.ts +82 -0
- package/src/server/wiki-links.ts +137 -0
- package/src/server/workbench-auth.ts +11 -0
- package/src/shared/agent-context.ts +3 -0
- package/src/shared/agent-presence.ts +15 -15
- package/src/shared/auto-arrange.ts +2 -0
- package/src/shared/boards.ts +4 -4
- package/src/shared/knowledge-lifecycle.ts +22 -0
- package/src/shared/knowledge-presets.ts +80 -0
- package/src/shared/lifecycle-status.ts +26 -0
- package/src/shared/memory-graph.ts +74 -0
- package/src/shared/relations.ts +98 -0
- package/src/shared/semantic-attention.ts +15 -1
- package/src/shared/tour.ts +23 -5
- package/src/shared/workspace-settings.ts +13 -0
- package/dist/types/client/canvas/ContextPinBar.d.ts +0 -1
- package/dist/types/client/canvas/FocusFieldLayer.d.ts +0 -1
- package/dist/types/client/canvas/kind-colors.d.ts +0 -3
- package/dist/types/server/agent-context.d.ts +0 -22
- package/src/client/canvas/ContextPinBar.tsx +0 -25
package/src/client/App.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { computed } from '@preact/signals';
|
|
2
|
+
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'preact/hooks';
|
|
2
3
|
import { Presentation } from './canvas/Presentation';
|
|
3
4
|
import { presenting } from './state/presentation';
|
|
4
5
|
import { AttentionHistory } from './canvas/AttentionHistory';
|
|
@@ -6,26 +7,36 @@ import { AttentionToast } from './canvas/AttentionToast';
|
|
|
6
7
|
import { registerCanvasArea, canvasArea } from './canvas/canvas-area';
|
|
7
8
|
import { CanvasViewport } from './canvas/CanvasViewport';
|
|
8
9
|
import { CommandBar } from './canvas/CommandBar';
|
|
10
|
+
import { ContextPinBar } from './canvas/ContextChip';
|
|
9
11
|
import { ConnectionBanner } from './canvas/ConnectionBanner';
|
|
10
12
|
import { CommandPalette } from './canvas/CommandPalette';
|
|
11
13
|
import { BoardLinkDialog } from './canvas/BoardLinkDialog';
|
|
14
|
+
import { LifecyclePanel } from './canvas/LifecyclePanel';
|
|
15
|
+
import { KnowledgeSync, LifecycleFilters } from './canvas/LifecycleChrome';
|
|
12
16
|
import { ContextMenu, useContextMenu } from './canvas/ContextMenu';
|
|
13
|
-
import { ContextPinBar } from './canvas/ContextPinBar';
|
|
14
17
|
import { EmptyState } from './canvas/EmptyState';
|
|
15
18
|
import { HomeView } from './canvas/HomeView';
|
|
16
19
|
import { ExportDialog } from './canvas/ExportDialog';
|
|
17
|
-
import { activeBoardId, boardsLoaded } from './state/boards-store';
|
|
20
|
+
import { activeBoardId, boardsLoaded, browsingMemory, boardIncarnation } from './state/boards-store';
|
|
18
21
|
import { createNodeInView } from './canvas/create-in-view';
|
|
19
|
-
import {
|
|
22
|
+
import {
|
|
23
|
+
activityLens,
|
|
24
|
+
heldGates,
|
|
25
|
+
pendingGates,
|
|
26
|
+
scopeFence,
|
|
27
|
+
sessionPanelOpened,
|
|
28
|
+
undoFromKeyboard,
|
|
29
|
+
} from './state/session-store';
|
|
20
30
|
import { ActivityFeed, WritersSheet } from './canvas/ExternalWriters';
|
|
21
31
|
import { ExpandedNodeOverlay } from './canvas/ExpandedNodeOverlay';
|
|
22
32
|
import { Minimap } from './canvas/Minimap';
|
|
23
33
|
import { SelectionBar } from './canvas/SelectionBar';
|
|
24
|
-
import { SessionPanel } from './canvas/SessionPanel';
|
|
34
|
+
import { AgentControls, SessionPanel } from './canvas/SessionPanel';
|
|
25
35
|
import { SessionReceipt } from './canvas/SessionReceipt';
|
|
26
36
|
import { ShortcutOverlay } from './canvas/ShortcutOverlay';
|
|
27
37
|
import { SnapshotPanel } from './canvas/SnapshotPanel';
|
|
28
38
|
import { promptedCreate, ToolRail } from './canvas/ToolRail';
|
|
39
|
+
import { EdgeHint, RelationLegend } from './canvas/EdgeLayer';
|
|
29
40
|
import { TextPrompt } from './canvas/TextPrompt';
|
|
30
41
|
import { TopBar } from './canvas/TopBar';
|
|
31
42
|
import {
|
|
@@ -39,6 +50,7 @@ import {
|
|
|
39
50
|
edges,
|
|
40
51
|
expandedNodeId,
|
|
41
52
|
fitAll,
|
|
53
|
+
focusNode,
|
|
42
54
|
hasInitialServerLayout,
|
|
43
55
|
nodes,
|
|
44
56
|
pendingExpandedNodeCloseId,
|
|
@@ -53,7 +65,8 @@ import {
|
|
|
53
65
|
} from './state/canvas-store';
|
|
54
66
|
import { connectSSE } from './state/sse-bridge';
|
|
55
67
|
import { intents } from './state/intent-store';
|
|
56
|
-
import { sessionActive } from './state/presence-store';
|
|
68
|
+
import { sessionActive, sessionChromeActive } from './state/presence-store';
|
|
69
|
+
import { agentSessionsEnabled, knowledgeLifecycleEnabled } from './state/workspace-settings';
|
|
57
70
|
import { isHostedWorkbench } from './state/workbench-transport';
|
|
58
71
|
import {
|
|
59
72
|
createGroupFromClient,
|
|
@@ -63,31 +76,73 @@ import {
|
|
|
63
76
|
ungroupFromClient,
|
|
64
77
|
} from './state/intent-bridge';
|
|
65
78
|
import type { AnnotationTool } from './types';
|
|
79
|
+
import { lifecycleFilter, lifecycleTarget } from './state/knowledge-store';
|
|
80
|
+
import { relationFilter } from './state/relation-store';
|
|
81
|
+
import { isSelectionEscape } from './utils/keyboard';
|
|
66
82
|
|
|
67
83
|
function logAppError(action: string, error: unknown): void {
|
|
68
84
|
console.error(`[app] ${action} failed`, error);
|
|
69
85
|
}
|
|
70
86
|
|
|
87
|
+
/**
|
|
88
|
+
* App needs only whether the board is empty. Reading the nodes map itself
|
|
89
|
+
* would re-render the whole shell (and re-measure the canvas) on every drag frame.
|
|
90
|
+
*/
|
|
91
|
+
const boardIsEmpty = computed(() => nodes.value.size === 0);
|
|
92
|
+
const validReviewTarget = computed(() => {
|
|
93
|
+
const target = lifecycleTarget.value;
|
|
94
|
+
return (
|
|
95
|
+
!!target &&
|
|
96
|
+
!browsingMemory.value &&
|
|
97
|
+
activeBoardId.value === target.boardId &&
|
|
98
|
+
boardIncarnation === target.incarnation &&
|
|
99
|
+
nodes.value.has(target.nodeId)
|
|
100
|
+
);
|
|
101
|
+
});
|
|
102
|
+
const reviewIsOpen = computed(() => knowledgeLifecycleEnabled.value && validReviewTarget.value);
|
|
103
|
+
|
|
71
104
|
export function App() {
|
|
72
|
-
|
|
105
|
+
// Off by default in a narrow pane (Pane600.dc.html); the Settings menu turns it on.
|
|
106
|
+
const [minimapVisible, setMinimapVisible] = useState(() => window.innerWidth >= 700);
|
|
73
107
|
const [snapshotOpen, setSnapshotOpen] = useState(false);
|
|
74
108
|
const [paletteOpen, setPaletteOpen] = useState(false);
|
|
75
109
|
const [boardLinkOpen, setBoardLinkOpen] = useState(false);
|
|
110
|
+
const [boardLinkSource, setBoardLinkSource] = useState<{ sourceNodeId?: string; wikiRef?: string }>({});
|
|
76
111
|
const [shortcutsOpen, setShortcutsOpen] = useState(false);
|
|
77
112
|
const [annotationTool, setAnnotationTool] = useState<AnnotationTool>(null);
|
|
78
113
|
const snapshotBtnRef = useRef<HTMLButtonElement>(null);
|
|
114
|
+
const canvasRegionRef = useRef<HTMLDivElement>(null);
|
|
115
|
+
const [area, setArea] = useState(canvasArea);
|
|
79
116
|
const { menu, openNodeMenu, openCanvasMenu, openEdgeMenu, closeMenu } = useContextMenu();
|
|
80
117
|
const hasInitialLayout = hasInitialServerLayout.value;
|
|
81
|
-
const onHome = boardsLoaded.value && activeBoardId.value === null;
|
|
118
|
+
const onHome = boardsLoaded.value && (activeBoardId.value === null || browsingMemory.value);
|
|
82
119
|
// rail-chrome-v2: the one selector every agent surface mounts on. Exposed
|
|
83
120
|
// as a data attribute so styling and tests can key on it; the quiet board
|
|
84
121
|
// (no attached session) must stay byte-clean of agent chrome.
|
|
85
|
-
const sessionIsActive =
|
|
122
|
+
const sessionIsActive = sessionChromeActive.value;
|
|
123
|
+
const sessionsEnabled = agentSessionsEnabled.value;
|
|
124
|
+
const hasAgentControls =
|
|
125
|
+
sessionActive.value || pendingGates.value.length > 0 || heldGates.value.length > 0 || !!scopeFence.value;
|
|
126
|
+
const reviewOpen = reviewIsOpen.value;
|
|
127
|
+
|
|
128
|
+
useLayoutEffect(() => {
|
|
129
|
+
if (!sessionsEnabled) {
|
|
130
|
+
activityLens.value = null;
|
|
131
|
+
sessionPanelOpened.value = false;
|
|
132
|
+
if (document.activeElement?.closest('.command-bar, .session-panel, .agent-popover')) {
|
|
133
|
+
snapshotBtnRef.current?.focus({ preventScroll: true });
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}, [sessionsEnabled]);
|
|
86
137
|
|
|
87
138
|
const handleToggleMinimap = useCallback(() => setMinimapVisible((v) => !v), []);
|
|
88
139
|
const handleToggleSnapshot = useCallback(() => setSnapshotOpen((v) => !v), []);
|
|
89
140
|
const handleCloseSnapshot = useCallback(() => setSnapshotOpen(false), []);
|
|
90
141
|
const handleSetAnnotationTool = useCallback((tool: AnnotationTool) => setAnnotationTool(tool), []);
|
|
142
|
+
const handleCanvasRegion = useCallback((el: HTMLDivElement | null) => {
|
|
143
|
+
registerCanvasArea(el);
|
|
144
|
+
canvasRegionRef.current = el;
|
|
145
|
+
}, []);
|
|
91
146
|
|
|
92
147
|
const handleMinimapNavigate = useCallback((x: number, y: number) => {
|
|
93
148
|
animateViewport({ x, y, scale: viewport.value.scale }, 200);
|
|
@@ -98,7 +153,11 @@ export function App() {
|
|
|
98
153
|
}, []);
|
|
99
154
|
|
|
100
155
|
useEffect(() => {
|
|
101
|
-
const open = () =>
|
|
156
|
+
const open = (event: Event) => {
|
|
157
|
+
const detail = (event as CustomEvent<{ sourceNodeId?: string; wikiRef?: string }>).detail;
|
|
158
|
+
setBoardLinkSource(detail ?? {});
|
|
159
|
+
setBoardLinkOpen(true);
|
|
160
|
+
};
|
|
102
161
|
window.addEventListener('pmx-open-board-link', open);
|
|
103
162
|
return () => window.removeEventListener('pmx-open-board-link', open);
|
|
104
163
|
}, []);
|
|
@@ -123,12 +182,24 @@ export function App() {
|
|
|
123
182
|
};
|
|
124
183
|
}, []);
|
|
125
184
|
|
|
126
|
-
|
|
185
|
+
// Keyboard ownership must match the committed UI before another key can arrive.
|
|
186
|
+
useLayoutEffect(() => {
|
|
127
187
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
128
188
|
// AltGr on European Windows layouts reports Ctrl+Alt — never treat it
|
|
129
189
|
// as the shortcut modifier, or AltGr+Z (ż, ź…) fires undo.
|
|
130
190
|
const mod = (e.metaKey || e.ctrlKey) && !e.altKey;
|
|
131
191
|
|
|
192
|
+
// The shortcut reference is available on Home too.
|
|
193
|
+
if (shortcutsOpen && (e.key === 'Escape' || e.key === '?' || (e.key === '/' && e.shiftKey))) {
|
|
194
|
+
e.preventDefault();
|
|
195
|
+
setShortcutsOpen(false);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// The library owns its keyboard. Never edit the agent's hidden active board.
|
|
200
|
+
if (browsingMemory.value || activeBoardId.value === null) return;
|
|
201
|
+
if (e.defaultPrevented) return;
|
|
202
|
+
|
|
132
203
|
// Cmd/Ctrl+K toggles command palette (works from anywhere, including inputs)
|
|
133
204
|
if (mod && e.key === 'k') {
|
|
134
205
|
e.preventDefault();
|
|
@@ -143,7 +214,15 @@ export function App() {
|
|
|
143
214
|
return;
|
|
144
215
|
}
|
|
145
216
|
|
|
146
|
-
//
|
|
217
|
+
// A companion review closes before expansion or the underlying selection.
|
|
218
|
+
if (e.key === 'Escape' && reviewIsOpen.value) {
|
|
219
|
+
if ((e.target as HTMLElement | null)?.closest('[role="menu"], [role="alertdialog"]')) return;
|
|
220
|
+
e.preventDefault();
|
|
221
|
+
lifecycleTarget.value = null;
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Esc collapses expansion after its companion review.
|
|
147
226
|
if (e.key === 'Escape' && expandedNodeId.value && !pendingExpandedNodeCloseId.value) {
|
|
148
227
|
e.preventDefault();
|
|
149
228
|
collapseExpandedNode();
|
|
@@ -157,13 +236,6 @@ export function App() {
|
|
|
157
236
|
return;
|
|
158
237
|
}
|
|
159
238
|
|
|
160
|
-
// Esc closes shortcut overlay
|
|
161
|
-
if (e.key === 'Escape' && shortcutsOpen) {
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
setShortcutsOpen(false);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
239
|
// Ignore other shortcuts when inside inputs or editable content — the
|
|
168
240
|
// single-key rail shortcuts below would otherwise eat characters typed
|
|
169
241
|
// into the inline markdown editor (contenteditable), which INPUT/TEXTAREA
|
|
@@ -171,9 +243,16 @@ export function App() {
|
|
|
171
243
|
const target = e.target as HTMLElement | null;
|
|
172
244
|
const tag = target?.tagName;
|
|
173
245
|
if (tag === 'INPUT' || tag === 'TEXTAREA' || target?.isContentEditable) return;
|
|
246
|
+
// Menus own every shortcut; dialogs retain global undo, zoom and help.
|
|
247
|
+
if (target?.closest('[role="menu"]')) return;
|
|
248
|
+
const inDialog = target?.closest('[role="dialog"]');
|
|
249
|
+
// Chrome owns its arrow keys and Tab order, rather than board navigation.
|
|
250
|
+
if ((e.key === 'Tab' || e.key.startsWith('Arrow')) && target?.closest('.tool-rail, .top-bar')) {
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
174
253
|
|
|
175
254
|
// Held Space = temporary pan tool (released on keyup below).
|
|
176
|
-
if (e.key === ' ' && !e.repeat && !mod) {
|
|
255
|
+
if (e.key === ' ' && !e.repeat && !mod && !inDialog) {
|
|
177
256
|
e.preventDefault();
|
|
178
257
|
spacePanHeld.value = true;
|
|
179
258
|
return;
|
|
@@ -207,11 +286,15 @@ export function App() {
|
|
|
207
286
|
} else if (mod && e.key === '-') {
|
|
208
287
|
e.preventDefault();
|
|
209
288
|
zoomByFactor(1 / 1.25);
|
|
289
|
+
} else if (inDialog) {
|
|
290
|
+
// Dialog controls keep navigation and board-editing keys to themselves.
|
|
291
|
+
return;
|
|
210
292
|
} else if (e.key === 'Escape') {
|
|
211
|
-
if (
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
293
|
+
if (!isSelectionEscape(e)) return;
|
|
294
|
+
e.preventDefault();
|
|
295
|
+
if (relationFilter.value?.boardId === activeBoardId.value) relationFilter.value = null;
|
|
296
|
+
lifecycleFilter.value = 'All';
|
|
297
|
+
clearSelection();
|
|
215
298
|
activeNodeId.value = null;
|
|
216
299
|
closeMenu();
|
|
217
300
|
} else if (e.key === 'Tab') {
|
|
@@ -327,22 +410,32 @@ export function App() {
|
|
|
327
410
|
return () => window.clearTimeout(timer);
|
|
328
411
|
}, [sessionIsActive]);
|
|
329
412
|
|
|
413
|
+
useLayoutEffect(() => {
|
|
414
|
+
const region = canvasRegionRef.current;
|
|
415
|
+
if (!region) return;
|
|
416
|
+
const observer = new ResizeObserver(() => {
|
|
417
|
+
setArea(canvasArea());
|
|
418
|
+
void reportClientViewportSize();
|
|
419
|
+
const review = lifecycleTarget.value;
|
|
420
|
+
if (review && reviewIsOpen.value) focusNode(review.nodeId, { recordHistory: false });
|
|
421
|
+
});
|
|
422
|
+
observer.observe(region);
|
|
423
|
+
return () => observer.disconnect();
|
|
424
|
+
}, []);
|
|
425
|
+
|
|
330
426
|
useEffect(() => {
|
|
331
427
|
if (!hasInitialLayout) return;
|
|
332
428
|
const ready = (window as Window & { __pmxCanvasBootstrapReady?: () => void }).__pmxCanvasBootstrapReady;
|
|
333
429
|
if (typeof ready === 'function') ready();
|
|
334
430
|
}, [hasInitialLayout]);
|
|
335
431
|
|
|
336
|
-
const allNodes = Array.from(nodes.value.values());
|
|
337
|
-
|
|
338
|
-
const area = canvasArea();
|
|
339
|
-
|
|
340
432
|
return (
|
|
341
433
|
<div
|
|
342
434
|
class={`app-shell${presenting.value ? ' is-presenting' : ''}`}
|
|
343
435
|
data-session-active={sessionIsActive ? 'true' : 'false'}
|
|
344
436
|
>
|
|
345
437
|
<Presentation />
|
|
438
|
+
<EdgeHint />
|
|
346
439
|
<ToolRail
|
|
347
440
|
minimapVisible={minimapVisible}
|
|
348
441
|
onToggleMinimap={handleToggleMinimap}
|
|
@@ -357,52 +450,66 @@ export function App() {
|
|
|
357
450
|
<div class="app-main">
|
|
358
451
|
<TopBar />
|
|
359
452
|
<ConnectionBanner />
|
|
360
|
-
<div class=
|
|
361
|
-
<
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
<
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
<
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
containerWidth={area.width}
|
|
397
|
-
containerHeight={area.height}
|
|
453
|
+
<div class={`workspace${reviewOpen ? ' is-reviewing' : ''}`}>
|
|
454
|
+
<div class="canvas-region" ref={handleCanvasRegion}>
|
|
455
|
+
{!browsingMemory.value && (
|
|
456
|
+
<CanvasViewport
|
|
457
|
+
onNodeContextMenu={openNodeMenu}
|
|
458
|
+
onCanvasContextMenu={openCanvasMenu}
|
|
459
|
+
onEdgeContextMenu={openEdgeMenu}
|
|
460
|
+
annotationMode={annotationTool !== null}
|
|
461
|
+
annotationTool={annotationTool}
|
|
462
|
+
/>
|
|
463
|
+
)}
|
|
464
|
+
<AttentionToast />
|
|
465
|
+
<KnowledgeSync />
|
|
466
|
+
{!onHome && <LifecycleFilters />}
|
|
467
|
+
{!onHome && <RelationLegend />}
|
|
468
|
+
<AttentionHistory />
|
|
469
|
+
<ActivityFeed />
|
|
470
|
+
<WritersSheet />
|
|
471
|
+
{onHome ? (
|
|
472
|
+
<HomeView />
|
|
473
|
+
) : (
|
|
474
|
+
hasInitialLayout &&
|
|
475
|
+
boardIsEmpty.value &&
|
|
476
|
+
intents.value.size === 0 && <EmptyState onOpenPalette={() => setPaletteOpen(true)} />
|
|
477
|
+
)}
|
|
478
|
+
{!onHome && selectedNodeIds.value.size > 0 && <SelectionBar />}
|
|
479
|
+
{!onHome && !sessionsEnabled && <ContextPinBar />}
|
|
480
|
+
{!onHome && sessionActive.value && !isHostedWorkbench() && <CommandBar />}
|
|
481
|
+
{!onHome && <SessionReceipt onOpenSnapshots={() => setSnapshotOpen(true)} />}
|
|
482
|
+
<TextPrompt />
|
|
483
|
+
{!browsingMemory.value && <ExportDialog />}
|
|
484
|
+
{!onHome && expandedNodeId.value && <ExpandedNodeOverlay />}
|
|
485
|
+
<SnapshotPanel
|
|
486
|
+
open={snapshotOpen && !browsingMemory.value}
|
|
487
|
+
onClose={handleCloseSnapshot}
|
|
488
|
+
anchorRef={snapshotBtnRef}
|
|
398
489
|
/>
|
|
399
|
-
|
|
490
|
+
{minimapVisible && !onHome && (
|
|
491
|
+
<Minimap
|
|
492
|
+
viewport={viewport}
|
|
493
|
+
nodes={nodes}
|
|
494
|
+
edges={edges}
|
|
495
|
+
onNavigate={handleMinimapNavigate}
|
|
496
|
+
containerWidth={area.width}
|
|
497
|
+
containerHeight={area.height}
|
|
498
|
+
/>
|
|
499
|
+
)}
|
|
500
|
+
</div>
|
|
501
|
+
{validReviewTarget.value && <LifecyclePanel />}
|
|
400
502
|
</div>
|
|
401
503
|
</div>
|
|
402
504
|
{sessionIsActive && <SessionPanel />}
|
|
403
|
-
{
|
|
404
|
-
{
|
|
405
|
-
{
|
|
505
|
+
{!sessionIsActive && hasAgentControls && <AgentControls />}
|
|
506
|
+
{!browsingMemory.value && menu && <ContextMenu menu={menu} onClose={closeMenu} />}
|
|
507
|
+
{!browsingMemory.value && paletteOpen && (
|
|
508
|
+
<CommandPalette onClose={() => setPaletteOpen(false)} onToggleMinimap={handleToggleMinimap} />
|
|
509
|
+
)}
|
|
510
|
+
{!browsingMemory.value && boardLinkOpen && (
|
|
511
|
+
<BoardLinkDialog {...boardLinkSource} onClose={() => setBoardLinkOpen(false)} />
|
|
512
|
+
)}
|
|
406
513
|
{shortcutsOpen && <ShortcutOverlay onClose={() => setShortcutsOpen(false)} />}
|
|
407
514
|
</div>
|
|
408
515
|
);
|
|
@@ -31,7 +31,6 @@ export function AttentionHistory() {
|
|
|
31
31
|
class="attention-history-tab"
|
|
32
32
|
onClick={handleOpenUpdates}
|
|
33
33
|
aria-label={unread > 0 ? `Recent updates — ${unread} new` : 'Recent updates'}
|
|
34
|
-
title={unread > 0 ? `${unread} new updates since last viewed` : 'Recent updates'}
|
|
35
34
|
>
|
|
36
35
|
<svg
|
|
37
36
|
width="14"
|
|
@@ -72,7 +71,6 @@ export function AttentionHistory() {
|
|
|
72
71
|
class="attention-history-close"
|
|
73
72
|
onClick={closeAttentionHistory}
|
|
74
73
|
aria-label="Collapse changes panel"
|
|
75
|
-
title="Collapse"
|
|
76
74
|
>
|
|
77
75
|
<IconClose />
|
|
78
76
|
</button>
|
|
@@ -1,4 +1,19 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { render, type ComponentChildren, type VNode } from 'preact';
|
|
2
|
+
import { useLayoutEffect, useMemo, useState } from 'preact/hooks';
|
|
3
|
+
|
|
4
|
+
/** Escape a bar's filter/clip without installing React-compat event hooks. */
|
|
5
|
+
export function BarPortal({ children }: { children: VNode }) {
|
|
6
|
+
const host = useMemo(() => document.createElement('div'), []);
|
|
7
|
+
useLayoutEffect(() => {
|
|
8
|
+
document.body.appendChild(host);
|
|
9
|
+
return () => {
|
|
10
|
+
render(null, host);
|
|
11
|
+
host.remove();
|
|
12
|
+
};
|
|
13
|
+
}, [host]);
|
|
14
|
+
useLayoutEffect(() => render(children, host), [children, host]);
|
|
15
|
+
return null;
|
|
16
|
+
}
|
|
2
17
|
|
|
3
18
|
/**
|
|
4
19
|
* Styled hover/focus tooltip for bar controls — the replacement for native
|
|
@@ -11,6 +26,7 @@ export function BarHint({
|
|
|
11
26
|
align = 'center',
|
|
12
27
|
side = 'down',
|
|
13
28
|
tapToOpen = false,
|
|
29
|
+
fitWithin,
|
|
14
30
|
children,
|
|
15
31
|
}: {
|
|
16
32
|
label: string;
|
|
@@ -27,12 +43,34 @@ export function BarHint({
|
|
|
27
43
|
* after the click, not linger on focus.
|
|
28
44
|
*/
|
|
29
45
|
tapToOpen?: boolean;
|
|
46
|
+
/**
|
|
47
|
+
* The clipping box to stay inside (a CSS selector for an ancestor). As the
|
|
48
|
+
* tooltip opens it takes whichever of end/start alignment fits — a node
|
|
49
|
+
* header chip sits anywhere along a card that clips its contents.
|
|
50
|
+
*/
|
|
51
|
+
fitWithin?: string;
|
|
30
52
|
children: ComponentChildren;
|
|
31
53
|
}) {
|
|
54
|
+
const [fitted, setFitted] = useState<'start' | 'end' | null>(null);
|
|
55
|
+
const fit = (event: Event) => {
|
|
56
|
+
if (!fitWithin) return;
|
|
57
|
+
const anchor = event.currentTarget as HTMLElement;
|
|
58
|
+
const box = anchor.closest(fitWithin)?.getBoundingClientRect();
|
|
59
|
+
const tip = anchor.querySelector('.toolbar-tooltip')?.getBoundingClientRect();
|
|
60
|
+
if (!box || !tip) return;
|
|
61
|
+
const self = anchor.getBoundingClientRect();
|
|
62
|
+
// Opening from the anchor's right edge leftwards (end) or its left edge rightwards (start).
|
|
63
|
+
const endFits = self.right - tip.width >= box.left;
|
|
64
|
+
const startFits = self.left + tip.width <= box.right;
|
|
65
|
+
setFitted(endFits || !startFits ? 'end' : 'start');
|
|
66
|
+
};
|
|
67
|
+
const placed = fitted ?? align;
|
|
32
68
|
return (
|
|
33
69
|
<span
|
|
34
|
-
class={`toolbar-tooltip-anchor toolbar-tooltip-anchor-${
|
|
70
|
+
class={`toolbar-tooltip-anchor toolbar-tooltip-anchor-${placed}${side === 'up' ? ' toolbar-tooltip-anchor-up' : ''}${tapToOpen ? ' toolbar-tooltip-anchor-tap' : ''}`}
|
|
35
71
|
tabIndex={tapToOpen ? -1 : undefined}
|
|
72
|
+
onPointerEnter={fitWithin ? fit : undefined}
|
|
73
|
+
onFocusIn={fitWithin ? fit : undefined}
|
|
36
74
|
>
|
|
37
75
|
{children}
|
|
38
76
|
<span class="toolbar-tooltip" role="tooltip">
|
|
@@ -42,7 +42,7 @@ export function BoardCopyDialog({ board, onClose }: { board: BoardSummary; onClo
|
|
|
42
42
|
const created = await createBoardFrom({
|
|
43
43
|
sourceBoardId: board.id,
|
|
44
44
|
name: name.trim(),
|
|
45
|
-
...(board.
|
|
45
|
+
...(board.folder ? { folder: board.folder } : {}),
|
|
46
46
|
nodeIds: [...selected],
|
|
47
47
|
includeReadme,
|
|
48
48
|
includeStructure,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
2
|
-
import {
|
|
2
|
+
import { normalizeBoardFolder } from '../../shared/boards.js';
|
|
3
3
|
import { boardList, updateBoard, type BoardSummary } from '../state/boards-store';
|
|
4
4
|
import { useFocusTrap } from './use-focus-trap';
|
|
5
5
|
|
|
@@ -14,7 +14,7 @@ export function BoardFolderDialog({
|
|
|
14
14
|
onClose: () => void;
|
|
15
15
|
}) {
|
|
16
16
|
const ref = useRef<HTMLDivElement>(null);
|
|
17
|
-
const [parent, setParent] = useState(board.
|
|
17
|
+
const [parent, setParent] = useState(board.folder ?? '');
|
|
18
18
|
const [name, setName] = useState('');
|
|
19
19
|
const [busy, setBusy] = useState(false);
|
|
20
20
|
const [error, setError] = useState('');
|
|
@@ -32,18 +32,18 @@ export function BoardFolderDialog({
|
|
|
32
32
|
|
|
33
33
|
const destination = name.trim() ? [parent, name.trim()].filter(Boolean).join('/') : parent;
|
|
34
34
|
const move = async () => {
|
|
35
|
-
let
|
|
35
|
+
let folder: string | null;
|
|
36
36
|
try {
|
|
37
37
|
if (name.includes('/')) throw new Error('Use one folder name; choose its parent above.');
|
|
38
|
-
|
|
38
|
+
folder = normalizeBoardFolder(destination);
|
|
39
39
|
} catch (cause) {
|
|
40
40
|
setError(cause instanceof Error ? cause.message : String(cause));
|
|
41
41
|
return;
|
|
42
42
|
}
|
|
43
43
|
setBusy(true);
|
|
44
44
|
setError('');
|
|
45
|
-
await updateBoard(board.id, {
|
|
46
|
-
if (boardList.value.find((entry) => entry.id === board.id)?.
|
|
45
|
+
await updateBoard(board.id, { folder });
|
|
46
|
+
if (boardList.value.find((entry) => entry.id === board.id)?.folder === folder) onClose();
|
|
47
47
|
else setError('Could not move this board. Try again.');
|
|
48
48
|
setBusy(false);
|
|
49
49
|
};
|
|
@@ -108,7 +108,7 @@ export function BoardFolderDialog({
|
|
|
108
108
|
<button
|
|
109
109
|
type="button"
|
|
110
110
|
class="text-prompt-confirm"
|
|
111
|
-
disabled={busy || destination === (board.
|
|
111
|
+
disabled={busy || destination === (board.folder ?? '')}
|
|
112
112
|
onClick={() => void move()}
|
|
113
113
|
>
|
|
114
114
|
{busy ? 'Moving…' : 'Move board'}
|