@plures/design-dojo 0.5.2 → 0.7.1
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/dist/app/CanvasBreadcrumb.svelte +146 -0
- package/dist/app/CanvasBreadcrumb.svelte.d.ts +19 -0
- package/dist/app/CanvasBreadcrumb.types.js +1 -0
- package/dist/app/ChatInput.svelte +296 -0
- package/dist/app/ChatInput.svelte.d.ts +15 -0
- package/dist/app/ChatView.svelte +542 -0
- package/dist/app/ChatView.svelte.d.ts +19 -0
- package/dist/app/ChatView.types.js +1 -0
- package/dist/app/ConversationGraph.svelte +471 -0
- package/dist/app/ConversationGraph.svelte.d.ts +20 -0
- package/dist/app/FirstRunWizard.svelte +542 -0
- package/dist/app/FirstRunWizard.svelte.d.ts +10 -0
- package/dist/app/FirstRunWizard.types.js +1 -0
- package/dist/app/MemorySidebar.svelte +258 -0
- package/dist/app/MemorySidebar.svelte.d.ts +9 -0
- package/dist/app/MemorySidebar.types.js +1 -0
- package/dist/app/PeerStatusPanel.svelte +464 -0
- package/dist/app/PeerStatusPanel.svelte.d.ts +16 -0
- package/dist/app/ProcedureCanvas.svelte +994 -0
- package/dist/app/ProcedureCanvas.svelte.d.ts +12 -0
- package/dist/app/ProcedureCanvas.types.js +1 -0
- package/dist/app/ProcedureEditor.svelte +494 -0
- package/dist/app/ProcedureEditor.svelte.d.ts +11 -0
- package/dist/app/ProcedureEditor.types.js +1 -0
- package/dist/app/ProcedureInspector.svelte +520 -0
- package/dist/app/ProcedureInspector.svelte.d.ts +15 -0
- package/dist/app/ProcedureNode.svelte +283 -0
- package/dist/app/ProcedureNode.svelte.d.ts +26 -0
- package/dist/app/Realm.types.js +1 -0
- package/dist/app/RealmIndicator.svelte +81 -0
- package/dist/app/RealmIndicator.svelte.d.ts +10 -0
- package/dist/app/RealmSwitcher.svelte +354 -0
- package/dist/app/RealmSwitcher.svelte.d.ts +16 -0
- package/dist/app/SemanticConversation.types.js +1 -0
- package/dist/app/SemanticSearchInput.svelte +630 -0
- package/dist/app/SemanticSearchInput.svelte.d.ts +20 -0
- package/dist/app/SemanticSearchInput.types.js +1 -0
- package/dist/app/SemanticTimeline.svelte +426 -0
- package/dist/app/SemanticTimeline.svelte.d.ts +13 -0
- package/dist/app/SettingsPanel.svelte +330 -0
- package/dist/app/SettingsPanel.svelte.d.ts +12 -0
- package/dist/app/SettingsPanel.types.js +1 -0
- package/dist/app/SubCanvas.svelte +457 -0
- package/dist/app/SubCanvas.svelte.d.ts +48 -0
- package/dist/app/SubCanvas.types.js +1 -0
- package/dist/app/Sync.types.js +1 -0
- package/dist/app/SyncIndicator.svelte +219 -0
- package/dist/app/SyncIndicator.svelte.d.ts +15 -0
- package/dist/app/SyncTimeline.svelte +299 -0
- package/dist/app/SyncTimeline.svelte.d.ts +12 -0
- package/dist/app/TagCloud.svelte +287 -0
- package/dist/app/TagCloud.svelte.d.ts +12 -0
- package/dist/app/WorkModeToggle.svelte +188 -0
- package/dist/app/WorkModeToggle.svelte.d.ts +17 -0
- package/dist/data/List.svelte +104 -0
- package/dist/data/List.svelte.d.ts +18 -0
- package/dist/data/ListItem.svelte +130 -0
- package/dist/data/ListItem.svelte.d.ts +12 -0
- package/dist/data/Table.svelte +241 -0
- package/dist/data/Table.svelte.d.ts +17 -0
- package/dist/data/index.d.ts +3 -0
- package/dist/data/index.js +3 -0
- package/dist/disclosure/Accordion.svelte +48 -0
- package/dist/disclosure/Accordion.svelte.d.ts +15 -0
- package/dist/feedback/Badge.svelte +60 -0
- package/dist/feedback/Badge.svelte.d.ts +14 -0
- package/dist/feedback/Callout.svelte +52 -0
- package/dist/feedback/Callout.svelte.d.ts +12 -0
- package/dist/feedback/EmptyState.svelte +47 -0
- package/dist/feedback/EmptyState.svelte.d.ts +12 -0
- package/dist/feedback/ProgressBar.svelte +95 -0
- package/dist/feedback/ProgressBar.svelte.d.ts +16 -0
- package/dist/forms/FileUpload.svelte +99 -0
- package/dist/forms/FileUpload.svelte.d.ts +18 -0
- package/dist/forms/RadioGroup.svelte +84 -0
- package/dist/forms/RadioGroup.svelte.d.ts +19 -0
- package/dist/icons/NerdFont.svelte +44 -0
- package/dist/icons/NerdFont.svelte.d.ts +13 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.js +70 -6212
- package/dist/layout/Box.svelte +207 -0
- package/dist/layout/Box.svelte.d.ts +22 -0
- package/dist/layout/Sidebar.svelte +210 -0
- package/dist/layout/Sidebar.svelte.d.ts +22 -0
- package/dist/layout/SplitPane.svelte +64 -0
- package/dist/layout/SplitPane.svelte.d.ts +12 -0
- package/dist/layout/StatusBar.svelte +83 -0
- package/dist/layout/StatusBar.svelte.d.ts +12 -0
- package/dist/layout/StatusBarItem.svelte +146 -0
- package/dist/layout/StatusBarItem.svelte.d.ts +15 -0
- package/dist/layout/StatusBarSpacer.svelte +38 -0
- package/dist/layout/StatusBarSpacer.svelte.d.ts +3 -0
- package/dist/layout/Tabs.svelte +254 -0
- package/dist/layout/Tabs.svelte.d.ts +21 -0
- package/dist/layout/Tabs.types.js +1 -0
- package/dist/layout/TitleBar.svelte +422 -0
- package/dist/layout/TitleBar.svelte.d.ts +22 -0
- package/dist/layout/index.d.ts +9 -0
- package/dist/layout/index.js +8 -0
- package/dist/motion/index.d.ts +1 -0
- package/dist/motion/index.js +1 -0
- package/dist/motion/spring.js +116 -0
- package/dist/overlays/ContextMenu.svelte +268 -0
- package/dist/overlays/ContextMenu.svelte.d.ts +17 -0
- package/dist/overlays/Dialog.svelte +264 -0
- package/dist/overlays/Dialog.svelte.d.ts +20 -0
- package/dist/overlays/Menu.svelte +274 -0
- package/dist/overlays/Menu.svelte.d.ts +26 -0
- package/dist/overlays/Menu.types.js +1 -0
- package/dist/overlays/Popover.svelte +158 -0
- package/dist/overlays/Popover.svelte.d.ts +21 -0
- package/dist/overlays/Toast.svelte +179 -0
- package/dist/overlays/Toast.svelte.d.ts +19 -0
- package/dist/overlays/Tooltip.svelte +114 -0
- package/dist/overlays/Tooltip.svelte.d.ts +17 -0
- package/dist/overlays/index.d.ts +7 -0
- package/dist/overlays/index.js +6 -0
- package/dist/primitives/Button.svelte +217 -0
- package/dist/primitives/Button.svelte.d.ts +13 -0
- package/dist/primitives/ContextMenu.svelte +242 -0
- package/dist/primitives/ContextMenu.svelte.d.ts +18 -0
- package/dist/primitives/ContextMenu.types.js +1 -0
- package/dist/primitives/Input.svelte +468 -0
- package/dist/primitives/Input.svelte.d.ts +21 -0
- package/dist/primitives/MarkdownEditor.svelte +781 -0
- package/dist/primitives/MarkdownEditor.svelte.d.ts +21 -0
- package/dist/primitives/MarkdownEditor.types.js +1 -0
- package/dist/primitives/SearchInput.svelte +623 -0
- package/dist/primitives/SearchInput.svelte.d.ts +24 -0
- package/dist/primitives/Select.svelte +336 -0
- package/dist/primitives/Select.svelte.d.ts +18 -0
- package/dist/primitives/Text.svelte +177 -0
- package/dist/primitives/Text.svelte.d.ts +26 -0
- package/dist/primitives/Toggle.svelte +138 -0
- package/dist/primitives/Toggle.svelte.d.ts +9 -0
- package/dist/primitives/index.d.ts +9 -0
- package/dist/primitives/index.js +7 -0
- package/dist/primitives/search-input-types.js +1 -0
- package/dist/surfaces/ChatPane.svelte +520 -0
- package/dist/surfaces/ChatPane.svelte.d.ts +15 -0
- package/dist/surfaces/ChatPane.types.js +1 -0
- package/dist/surfaces/GlassPanel.svelte +118 -0
- package/dist/surfaces/GlassPanel.svelte.d.ts +19 -0
- package/dist/surfaces/Pane.svelte +172 -0
- package/dist/surfaces/Pane.svelte.d.ts +25 -0
- package/dist/surfaces/index.d.ts +4 -0
- package/dist/surfaces/index.js +3 -0
- package/dist/telemetry/correlation.js +26 -0
- package/dist/telemetry/index.d.ts +4 -4
- package/dist/telemetry/index.js +20 -101
- package/dist/telemetry/sampling.js +58 -0
- package/dist/telemetry/tracer.d.ts +16 -1
- package/dist/telemetry/tracer.js +112 -0
- package/dist/tokens.css +123 -0
- package/dist/tui-tokens.css +36 -0
- package/dist/useTui.js +31 -0
- package/package.json +32 -22
- package/dist/design-dojo.css +0 -1
- package/dist/enforce/index.d.ts +0 -75
- package/dist/enforce/known-components.d.ts +0 -7
- package/dist/enforce/rules/no-local-components.d.ts +0 -29
- package/dist/enforce/rules/prefer-design-dojo-imports.d.ts +0 -27
- package/dist/enforce.js +0 -132
- package/dist/lib/app/CanvasBreadcrumb.svelte.d.ts +0 -1
- package/dist/lib/app/ChatInput.svelte.d.ts +0 -1
- package/dist/lib/app/ChatView.svelte.d.ts +0 -1
- package/dist/lib/app/ConversationGraph.svelte.d.ts +0 -1
- package/dist/lib/app/FirstRunWizard.svelte.d.ts +0 -1
- package/dist/lib/app/MemorySidebar.svelte.d.ts +0 -1
- package/dist/lib/app/PeerStatusPanel.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureCanvas.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureEditor.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureInspector.svelte.d.ts +0 -1
- package/dist/lib/app/ProcedureNode.svelte.d.ts +0 -1
- package/dist/lib/app/RealmIndicator.svelte.d.ts +0 -1
- package/dist/lib/app/RealmSwitcher.svelte.d.ts +0 -1
- package/dist/lib/app/SemanticSearchInput.svelte.d.ts +0 -1
- package/dist/lib/app/SemanticTimeline.svelte.d.ts +0 -1
- package/dist/lib/app/SettingsPanel.svelte.d.ts +0 -1
- package/dist/lib/app/SubCanvas.svelte.d.ts +0 -1
- package/dist/lib/app/SyncIndicator.svelte.d.ts +0 -1
- package/dist/lib/app/SyncTimeline.svelte.d.ts +0 -1
- package/dist/lib/app/TagCloud.svelte.d.ts +0 -1
- package/dist/lib/app/WorkModeToggle.svelte.d.ts +0 -1
- package/dist/lib/data/List.svelte.d.ts +0 -1
- package/dist/lib/data/ListItem.svelte.d.ts +0 -1
- package/dist/lib/data/Table.svelte.d.ts +0 -1
- package/dist/lib/data/index.d.ts +0 -3
- package/dist/lib/disclosure/Accordion.svelte.d.ts +0 -1
- package/dist/lib/feedback/Badge.svelte.d.ts +0 -1
- package/dist/lib/feedback/Callout.svelte.d.ts +0 -1
- package/dist/lib/feedback/EmptyState.svelte.d.ts +0 -1
- package/dist/lib/feedback/ProgressBar.svelte.d.ts +0 -1
- package/dist/lib/forms/FileUpload.svelte.d.ts +0 -1
- package/dist/lib/forms/RadioGroup.svelte.d.ts +0 -1
- package/dist/lib/icons/NerdFont.svelte.d.ts +0 -1
- package/dist/lib/icons/index.d.ts +0 -1
- package/dist/lib/index.d.ts +0 -76
- package/dist/lib/layout/Box.svelte.d.ts +0 -1
- package/dist/lib/layout/Sidebar.svelte.d.ts +0 -1
- package/dist/lib/layout/SplitPane.svelte.d.ts +0 -1
- package/dist/lib/layout/StatusBar.svelte.d.ts +0 -1
- package/dist/lib/layout/StatusBarItem.svelte.d.ts +0 -1
- package/dist/lib/layout/StatusBarSpacer.svelte.d.ts +0 -1
- package/dist/lib/layout/Tabs.svelte.d.ts +0 -1
- package/dist/lib/layout/TitleBar.svelte.d.ts +0 -1
- package/dist/lib/layout/index.d.ts +0 -9
- package/dist/lib/motion/index.d.ts +0 -1
- package/dist/lib/overlays/ContextMenu.svelte.d.ts +0 -1
- package/dist/lib/overlays/Dialog.svelte.d.ts +0 -1
- package/dist/lib/overlays/Menu.svelte.d.ts +0 -1
- package/dist/lib/overlays/Popover.svelte.d.ts +0 -1
- package/dist/lib/overlays/Toast.svelte.d.ts +0 -1
- package/dist/lib/overlays/Tooltip.svelte.d.ts +0 -1
- package/dist/lib/overlays/index.d.ts +0 -7
- package/dist/lib/primitives/Button.svelte.d.ts +0 -1
- package/dist/lib/primitives/ContextMenu.svelte.d.ts +0 -1
- package/dist/lib/primitives/Input.svelte.d.ts +0 -1
- package/dist/lib/primitives/MarkdownEditor.svelte.d.ts +0 -1
- package/dist/lib/primitives/SearchInput.svelte.d.ts +0 -1
- package/dist/lib/primitives/Select.svelte.d.ts +0 -1
- package/dist/lib/primitives/Text.svelte.d.ts +0 -1
- package/dist/lib/primitives/Toggle.svelte.d.ts +0 -1
- package/dist/lib/primitives/index.d.ts +0 -9
- package/dist/lib/surfaces/ChatPane.svelte.d.ts +0 -1
- package/dist/lib/surfaces/GlassPanel.svelte.d.ts +0 -1
- package/dist/lib/surfaces/Pane.svelte.d.ts +0 -1
- package/dist/lib/surfaces/index.d.ts +0 -4
- /package/dist/{lib/app → app}/CanvasBreadcrumb.types.d.ts +0 -0
- /package/dist/{lib/app → app}/ChatView.types.d.ts +0 -0
- /package/dist/{lib/app → app}/FirstRunWizard.types.d.ts +0 -0
- /package/dist/{lib/app → app}/MemorySidebar.types.d.ts +0 -0
- /package/dist/{lib/app → app}/ProcedureCanvas.types.d.ts +0 -0
- /package/dist/{lib/app → app}/ProcedureEditor.types.d.ts +0 -0
- /package/dist/{lib/app → app}/Realm.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SemanticConversation.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SemanticSearchInput.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SettingsPanel.types.d.ts +0 -0
- /package/dist/{lib/app → app}/SubCanvas.types.d.ts +0 -0
- /package/dist/{lib/app → app}/Sync.types.d.ts +0 -0
- /package/dist/{lib/layout → layout}/Tabs.types.d.ts +0 -0
- /package/dist/{lib/motion → motion}/spring.d.ts +0 -0
- /package/dist/{lib/overlays → overlays}/Menu.types.d.ts +0 -0
- /package/dist/{lib/primitives → primitives}/ContextMenu.types.d.ts +0 -0
- /package/dist/{lib/primitives → primitives}/MarkdownEditor.types.d.ts +0 -0
- /package/dist/{lib/primitives → primitives}/search-input-types.d.ts +0 -0
- /package/dist/{lib/surfaces → surfaces}/ChatPane.types.d.ts +0 -0
- /package/dist/{lib/useTui.d.ts → useTui.d.ts} +0 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
SubCanvas — Resizable, draggable labeled container for grouped controls.
|
|
3
|
+
|
|
4
|
+
Designed to be placed inside a canvas surface (e.g. ProcedureCanvas) as a
|
|
5
|
+
sub-canvas region. Supports:
|
|
6
|
+
- Labeled header with drag handle
|
|
7
|
+
- 8-direction resize handles
|
|
8
|
+
- Arbitrary children via Svelte snippet
|
|
9
|
+
- Depth-aware accent colours (Obsidian-like palette)
|
|
10
|
+
- Linked mode: double-clicking the header fires `onopen` (navigate to sub-canvas)
|
|
11
|
+
- Optional `zoom` prop so resize/drag math is correct when inside a scaled canvas
|
|
12
|
+
- Keyboard accessible (focus ring, arrow-key nudge on header)
|
|
13
|
+
-->
|
|
14
|
+
<script lang="ts">
|
|
15
|
+
import type { Snippet } from "svelte";
|
|
16
|
+
import { untrack } from "svelte";
|
|
17
|
+
import type { SubCanvasBounds } from "./SubCanvas.types.js";
|
|
18
|
+
|
|
19
|
+
// ── Depth accent palette (calm, Obsidian-inspired) ───────────────────────
|
|
20
|
+
const DEPTH_ACCENTS = [
|
|
21
|
+
"#6366f1", // indigo — root / department
|
|
22
|
+
"#0ea5e9", // sky — team
|
|
23
|
+
"#22c55e", // green — primitive group
|
|
24
|
+
"#f59e0b", // amber — leaf / detail
|
|
25
|
+
"#888888", // muted — depth 4+
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
type ResizeDir = "n" | "ne" | "e" | "se" | "s" | "sw" | "w" | "nw";
|
|
29
|
+
|
|
30
|
+
interface ResizeHandle {
|
|
31
|
+
dir: ResizeDir;
|
|
32
|
+
cursor: string;
|
|
33
|
+
/** Inline style fragment controlling position/size of the hit target. */
|
|
34
|
+
pos: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const HANDLES: ResizeHandle[] = [
|
|
38
|
+
{ dir: "n", cursor: "n-resize", pos: "top:-5px;left:50%;transform:translateX(-50%);width:32px;height:10px;" },
|
|
39
|
+
{ dir: "ne", cursor: "ne-resize", pos: "top:-5px;right:-5px;width:10px;height:10px;" },
|
|
40
|
+
{ dir: "e", cursor: "e-resize", pos: "top:50%;right:-5px;transform:translateY(-50%);width:10px;height:32px;" },
|
|
41
|
+
{ dir: "se", cursor: "se-resize", pos: "bottom:-5px;right:-5px;width:10px;height:10px;" },
|
|
42
|
+
{ dir: "s", cursor: "s-resize", pos: "bottom:-5px;left:50%;transform:translateX(-50%);width:32px;height:10px;" },
|
|
43
|
+
{ dir: "sw", cursor: "sw-resize", pos: "bottom:-5px;left:-5px;width:10px;height:10px;" },
|
|
44
|
+
{ dir: "w", cursor: "w-resize", pos: "top:50%;left:-5px;transform:translateY(-50%);width:10px;height:32px;" },
|
|
45
|
+
{ dir: "nw", cursor: "nw-resize", pos: "top:-5px;left:-5px;width:10px;height:10px;" },
|
|
46
|
+
];
|
|
47
|
+
|
|
48
|
+
// ── Props ─────────────────────────────────────────────────────────────────
|
|
49
|
+
interface Props {
|
|
50
|
+
/** Initial X position in canvas coordinates. */
|
|
51
|
+
x?: number;
|
|
52
|
+
/** Initial Y position in canvas coordinates. */
|
|
53
|
+
y?: number;
|
|
54
|
+
/** Initial width in canvas units. */
|
|
55
|
+
width?: number;
|
|
56
|
+
/** Initial height in canvas units. */
|
|
57
|
+
height?: number;
|
|
58
|
+
/** Display label shown in the header. */
|
|
59
|
+
label?: string;
|
|
60
|
+
/**
|
|
61
|
+
* Nesting depth. Controls accent colour:
|
|
62
|
+
* 0 = indigo (department), 1 = sky (team), 2 = green (group), …
|
|
63
|
+
*/
|
|
64
|
+
depth?: number;
|
|
65
|
+
/**
|
|
66
|
+
* When true, a link icon is shown and double-clicking the header fires `onopen`.
|
|
67
|
+
* Use this for sub-canvases that reference another canvas by ID.
|
|
68
|
+
*/
|
|
69
|
+
linked?: boolean;
|
|
70
|
+
/** Whether this sub-canvas is currently selected. */
|
|
71
|
+
selected?: boolean;
|
|
72
|
+
/** Minimum width during resize (canvas units). */
|
|
73
|
+
minWidth?: number;
|
|
74
|
+
/** Minimum height during resize (canvas units). */
|
|
75
|
+
minHeight?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Current canvas zoom level. Used to scale mouse deltas correctly when
|
|
78
|
+
* the sub-canvas is embedded inside a transformed canvas surface.
|
|
79
|
+
*/
|
|
80
|
+
zoom?: number;
|
|
81
|
+
/** Slot content rendered inside the body area. */
|
|
82
|
+
children?: Snippet;
|
|
83
|
+
/** Custom CSS class applied to the root element. */
|
|
84
|
+
class?: string;
|
|
85
|
+
/** Fired when position or size changes after a drag/resize gesture ends. */
|
|
86
|
+
onchange?: (bounds: SubCanvasBounds) => void;
|
|
87
|
+
/** Fired on click (selection). */
|
|
88
|
+
onclick?: (e: MouseEvent) => void;
|
|
89
|
+
/** Fired on header double-click when `linked` is true. */
|
|
90
|
+
onopen?: () => void;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
let {
|
|
94
|
+
x: initX = 40,
|
|
95
|
+
y: initY = 40,
|
|
96
|
+
width: initW = 320,
|
|
97
|
+
height: initH = 240,
|
|
98
|
+
label = "Sub-canvas",
|
|
99
|
+
depth = 0,
|
|
100
|
+
linked = false,
|
|
101
|
+
selected = false,
|
|
102
|
+
minWidth = 120,
|
|
103
|
+
minHeight = 80,
|
|
104
|
+
zoom = 1,
|
|
105
|
+
children,
|
|
106
|
+
class: className = "",
|
|
107
|
+
onchange,
|
|
108
|
+
onclick,
|
|
109
|
+
onopen,
|
|
110
|
+
}: Props = $props();
|
|
111
|
+
|
|
112
|
+
// ── Internal state (owned) ────────────────────────────────────────────────
|
|
113
|
+
// untrack(): one-time capture of initial prop values (owned state pattern)
|
|
114
|
+
let x = $state(untrack(() => initX));
|
|
115
|
+
let y = $state(untrack(() => initY));
|
|
116
|
+
let w = $state(untrack(() => initW));
|
|
117
|
+
let h = $state(untrack(() => initH));
|
|
118
|
+
|
|
119
|
+
const accent = $derived(
|
|
120
|
+
DEPTH_ACCENTS[Math.max(0, Math.min(depth, DEPTH_ACCENTS.length - 1))]
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
// ── Drag state ────────────────────────────────────────────────────────────
|
|
124
|
+
let drag: { startCX: number; startCY: number; startX: number; startY: number } | null = $state(null);
|
|
125
|
+
|
|
126
|
+
// ── Resize state ──────────────────────────────────────────────────────────
|
|
127
|
+
let resize: {
|
|
128
|
+
dir: ResizeDir;
|
|
129
|
+
startCX: number;
|
|
130
|
+
startCY: number;
|
|
131
|
+
startX: number;
|
|
132
|
+
startY: number;
|
|
133
|
+
startW: number;
|
|
134
|
+
startH: number;
|
|
135
|
+
} | null = $state(null);
|
|
136
|
+
|
|
137
|
+
// ── Drag handlers (pointer capture on header) ─────────────────────────────
|
|
138
|
+
function onHeaderPointerDown(e: PointerEvent) {
|
|
139
|
+
if (e.button !== 0) return;
|
|
140
|
+
e.stopPropagation();
|
|
141
|
+
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
|
|
142
|
+
drag = { startCX: e.clientX, startCY: e.clientY, startX: x, startY: y };
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function onHeaderPointerMove(e: PointerEvent) {
|
|
146
|
+
if (!drag) return;
|
|
147
|
+
const scale = zoom > 0 ? zoom : 1;
|
|
148
|
+
x = drag.startX + (e.clientX - drag.startCX) / scale;
|
|
149
|
+
y = drag.startY + (e.clientY - drag.startCY) / scale;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function onHeaderPointerUp() {
|
|
153
|
+
if (!drag) return;
|
|
154
|
+
drag = null;
|
|
155
|
+
onchange?.({ x, y, width: w, height: h });
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function onHeaderPointerCancel() {
|
|
159
|
+
if (!drag) return;
|
|
160
|
+
drag = null;
|
|
161
|
+
onchange?.({ x, y, width: w, height: h });
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function onHeaderKeyDown(e: KeyboardEvent) {
|
|
165
|
+
if (e.key === "Enter") {
|
|
166
|
+
e.preventDefault();
|
|
167
|
+
if (linked) { onopen?.(); } else { (e.currentTarget as HTMLElement).click(); }
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
if (e.key === " ") {
|
|
171
|
+
e.preventDefault();
|
|
172
|
+
(e.currentTarget as HTMLElement).click();
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const step = e.shiftKey ? 20 : 4;
|
|
176
|
+
if (e.key === "ArrowLeft") { e.preventDefault(); x -= step; }
|
|
177
|
+
else if (e.key === "ArrowRight") { e.preventDefault(); x += step; }
|
|
178
|
+
else if (e.key === "ArrowUp") { e.preventDefault(); y -= step; }
|
|
179
|
+
else if (e.key === "ArrowDown") { e.preventDefault(); y += step; }
|
|
180
|
+
else return;
|
|
181
|
+
onchange?.({ x, y, width: w, height: h });
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// ── Resize handlers (pointer capture on each handle) ─────────────────────
|
|
185
|
+
function onResizePointerDown(dir: ResizeDir, e: PointerEvent) {
|
|
186
|
+
if (e.button !== 0) return;
|
|
187
|
+
e.stopPropagation();
|
|
188
|
+
(e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
|
|
189
|
+
resize = { dir, startCX: e.clientX, startCY: e.clientY, startX: x, startY: y, startW: w, startH: h };
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function onResizePointerMove(e: PointerEvent) {
|
|
193
|
+
if (!resize) return;
|
|
194
|
+
const scale = zoom > 0 ? zoom : 1;
|
|
195
|
+
const dx = (e.clientX - resize.startCX) / scale;
|
|
196
|
+
const dy = (e.clientY - resize.startCY) / scale;
|
|
197
|
+
const dir = resize.dir;
|
|
198
|
+
|
|
199
|
+
let nx = resize.startX;
|
|
200
|
+
let ny = resize.startY;
|
|
201
|
+
let nw = resize.startW;
|
|
202
|
+
let nh = resize.startH;
|
|
203
|
+
|
|
204
|
+
if (dir.includes("e")) {
|
|
205
|
+
nw = Math.max(minWidth, resize.startW + dx);
|
|
206
|
+
}
|
|
207
|
+
if (dir.includes("s")) {
|
|
208
|
+
nh = Math.max(minHeight, resize.startH + dy);
|
|
209
|
+
}
|
|
210
|
+
if (dir.includes("w")) {
|
|
211
|
+
const dw = Math.min(dx, resize.startW - minWidth);
|
|
212
|
+
nx = resize.startX + dw;
|
|
213
|
+
nw = resize.startW - dw;
|
|
214
|
+
}
|
|
215
|
+
if (dir.includes("n")) {
|
|
216
|
+
const dh = Math.min(dy, resize.startH - minHeight);
|
|
217
|
+
ny = resize.startY + dh;
|
|
218
|
+
nh = resize.startH - dh;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
x = nx; y = ny; w = nw; h = nh;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function onResizePointerUp() {
|
|
225
|
+
if (!resize) return;
|
|
226
|
+
resize = null;
|
|
227
|
+
onchange?.({ x, y, width: w, height: h });
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function onResizePointerCancel() {
|
|
231
|
+
if (!resize) return;
|
|
232
|
+
resize = null;
|
|
233
|
+
onchange?.({ x, y, width: w, height: h });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function onResizeHandleKeyDown(dir: ResizeDir, e: KeyboardEvent) {
|
|
237
|
+
const step = e.shiftKey ? 20 : 8;
|
|
238
|
+
let changed = false;
|
|
239
|
+
if (dir.includes("e") && e.key === "ArrowRight") { w = Math.max(minWidth, w + step); changed = true; }
|
|
240
|
+
if (dir.includes("e") && e.key === "ArrowLeft") { w = Math.max(minWidth, w - step); changed = true; }
|
|
241
|
+
if (dir.includes("w") && e.key === "ArrowLeft") { const d = Math.min(step, w - minWidth); x -= d; w += d; changed = true; }
|
|
242
|
+
if (dir.includes("w") && e.key === "ArrowRight") { const d = Math.min(step, w - minWidth); x += d; w -= d; changed = true; }
|
|
243
|
+
if (dir.includes("s") && e.key === "ArrowDown") { h = Math.max(minHeight, h + step); changed = true; }
|
|
244
|
+
if (dir.includes("s") && e.key === "ArrowUp") { h = Math.max(minHeight, h - step); changed = true; }
|
|
245
|
+
if (dir.includes("n") && e.key === "ArrowUp") { const d = Math.min(step, h - minHeight); y -= d; h += d; changed = true; }
|
|
246
|
+
if (dir.includes("n") && e.key === "ArrowDown") { const d = Math.min(step, h - minHeight); y += d; h -= d; changed = true; }
|
|
247
|
+
if (changed) { e.preventDefault(); onchange?.({ x, y, width: w, height: h }); }
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ── Root keyboard handler (Enter/Space → selection when body is focused) ──
|
|
251
|
+
function onRootKeyDown(e: KeyboardEvent) {
|
|
252
|
+
if (e.target !== e.currentTarget) return;
|
|
253
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
254
|
+
e.preventDefault();
|
|
255
|
+
(e.currentTarget as HTMLElement).click();
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// ── Header double-click (open linked canvas) ──────────────────────────────
|
|
260
|
+
function onHeaderDblClick(e: MouseEvent) {
|
|
261
|
+
if (!linked) return;
|
|
262
|
+
e.stopPropagation();
|
|
263
|
+
onopen?.();
|
|
264
|
+
}
|
|
265
|
+
</script>
|
|
266
|
+
|
|
267
|
+
<!-- svelte-ignore a11y_no_noninteractive_element_interactions -->
|
|
268
|
+
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
269
|
+
<div
|
|
270
|
+
class="sub-canvas {className}"
|
|
271
|
+
class:sub-canvas--selected={selected}
|
|
272
|
+
style:left="{x}px"
|
|
273
|
+
style:top="{y}px"
|
|
274
|
+
style:width="{w}px"
|
|
275
|
+
style:height="{h}px"
|
|
276
|
+
style:--sc-accent={accent}
|
|
277
|
+
role="region"
|
|
278
|
+
aria-label={label}
|
|
279
|
+
tabindex="0"
|
|
280
|
+
onclick={onclick}
|
|
281
|
+
onkeydown={onRootKeyDown}
|
|
282
|
+
>
|
|
283
|
+
<!-- ── Header ── -->
|
|
284
|
+
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
|
285
|
+
<div
|
|
286
|
+
class="sub-canvas__header"
|
|
287
|
+
class:sub-canvas__header--dragging={!!drag}
|
|
288
|
+
onpointerdown={onHeaderPointerDown}
|
|
289
|
+
onpointermove={onHeaderPointerMove}
|
|
290
|
+
onpointerup={onHeaderPointerUp}
|
|
291
|
+
onpointercancel={onHeaderPointerCancel}
|
|
292
|
+
onlostpointercapture={onHeaderPointerCancel}
|
|
293
|
+
ondblclick={onHeaderDblClick}
|
|
294
|
+
onkeydown={onHeaderKeyDown}
|
|
295
|
+
role="button"
|
|
296
|
+
tabindex="0"
|
|
297
|
+
aria-label="{linked ? `Drag or double-click to open ${label}` : `Drag ${label}`}"
|
|
298
|
+
>
|
|
299
|
+
<span class="sub-canvas__drag-grip" aria-hidden="true">⠿</span>
|
|
300
|
+
<span class="sub-canvas__label">{label}</span>
|
|
301
|
+
{#if linked}
|
|
302
|
+
<span class="sub-canvas__linked-icon" title="Double-click to open linked canvas">↗</span>
|
|
303
|
+
{/if}
|
|
304
|
+
<span class="sub-canvas__depth-badge" aria-label="Depth {depth}">L{depth}</span>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
<!-- ── Body (children) ── -->
|
|
308
|
+
<div class="sub-canvas__body">
|
|
309
|
+
{#if children}
|
|
310
|
+
{@render children()}
|
|
311
|
+
{/if}
|
|
312
|
+
</div>
|
|
313
|
+
|
|
314
|
+
<!-- ── Resize handles ── -->
|
|
315
|
+
{#each HANDLES as handle}
|
|
316
|
+
<button
|
|
317
|
+
type="button"
|
|
318
|
+
class="sub-canvas__resize-handle"
|
|
319
|
+
style="{handle.pos}cursor:{handle.cursor};"
|
|
320
|
+
aria-label="Resize {handle.dir}"
|
|
321
|
+
onpointerdown={(e) => onResizePointerDown(handle.dir, e)}
|
|
322
|
+
onpointermove={onResizePointerMove}
|
|
323
|
+
onpointerup={onResizePointerUp}
|
|
324
|
+
onpointercancel={onResizePointerCancel}
|
|
325
|
+
onlostpointercapture={onResizePointerCancel}
|
|
326
|
+
onkeydown={(e) => onResizeHandleKeyDown(handle.dir, e)}
|
|
327
|
+
></button>
|
|
328
|
+
{/each}
|
|
329
|
+
</div>
|
|
330
|
+
|
|
331
|
+
<style>
|
|
332
|
+
/* ── Root ── */
|
|
333
|
+
.sub-canvas {
|
|
334
|
+
position: absolute;
|
|
335
|
+
box-sizing: border-box;
|
|
336
|
+
background: color-mix(in srgb, var(--surface-1, #141414) 80%, transparent);
|
|
337
|
+
border: 1px solid color-mix(in srgb, var(--sc-accent, #6366f1) 55%, transparent);
|
|
338
|
+
border-radius: var(--radius-md, 10px);
|
|
339
|
+
outline: none;
|
|
340
|
+
display: flex;
|
|
341
|
+
flex-direction: column;
|
|
342
|
+
overflow: visible;
|
|
343
|
+
/* Subtle glow to distinguish from the background canvas */
|
|
344
|
+
box-shadow:
|
|
345
|
+
0 0 0 1px color-mix(in srgb, var(--sc-accent, #6366f1) 12%, transparent),
|
|
346
|
+
0 4px 20px rgba(0, 0, 0, 0.35);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
.sub-canvas:focus-within {
|
|
350
|
+
outline: none;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
.sub-canvas--selected {
|
|
354
|
+
border-color: var(--sc-accent, #6366f1);
|
|
355
|
+
box-shadow:
|
|
356
|
+
0 0 0 2px color-mix(in srgb, var(--sc-accent, #6366f1) 40%, transparent),
|
|
357
|
+
0 4px 20px rgba(0, 0, 0, 0.35);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/* ── Header ── */
|
|
361
|
+
.sub-canvas__header {
|
|
362
|
+
display: flex;
|
|
363
|
+
align-items: center;
|
|
364
|
+
gap: 6px;
|
|
365
|
+
padding: 0 var(--space-2, 8px);
|
|
366
|
+
height: 30px;
|
|
367
|
+
flex-shrink: 0;
|
|
368
|
+
user-select: none;
|
|
369
|
+
background: color-mix(in srgb, var(--sc-accent, #6366f1) 15%, var(--surface-2, #1e1e1e));
|
|
370
|
+
border-bottom: 1px solid color-mix(in srgb, var(--sc-accent, #6366f1) 25%, transparent);
|
|
371
|
+
border-radius: calc(var(--radius-md, 10px) - 1px) calc(var(--radius-md, 10px) - 1px) 0 0;
|
|
372
|
+
cursor: grab;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.sub-canvas__header:focus {
|
|
376
|
+
outline: 2px solid var(--sc-accent, #6366f1);
|
|
377
|
+
outline-offset: -2px;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
.sub-canvas__header--dragging,
|
|
381
|
+
.sub-canvas__header--dragging:active {
|
|
382
|
+
cursor: grabbing;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
.sub-canvas__drag-grip {
|
|
386
|
+
font-size: 12px;
|
|
387
|
+
color: color-mix(in srgb, var(--sc-accent, #6366f1) 60%, transparent);
|
|
388
|
+
flex-shrink: 0;
|
|
389
|
+
line-height: 1;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
.sub-canvas__label {
|
|
393
|
+
flex: 1;
|
|
394
|
+
font-size: var(--text-xs, 12px);
|
|
395
|
+
font-weight: 700;
|
|
396
|
+
font-family: var(--font-mono, monospace);
|
|
397
|
+
letter-spacing: 0.04em;
|
|
398
|
+
color: color-mix(in srgb, var(--sc-accent, #6366f1) 80%, var(--color-text, #e8e8e8));
|
|
399
|
+
white-space: nowrap;
|
|
400
|
+
overflow: hidden;
|
|
401
|
+
text-overflow: ellipsis;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
.sub-canvas__linked-icon {
|
|
405
|
+
font-size: 11px;
|
|
406
|
+
color: var(--sc-accent, #6366f1);
|
|
407
|
+
flex-shrink: 0;
|
|
408
|
+
opacity: 0.8;
|
|
409
|
+
cursor: pointer;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
.sub-canvas__linked-icon:hover {
|
|
413
|
+
opacity: 1;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
.sub-canvas__depth-badge {
|
|
417
|
+
font-size: 10px;
|
|
418
|
+
font-weight: 700;
|
|
419
|
+
font-family: var(--font-mono, monospace);
|
|
420
|
+
color: color-mix(in srgb, var(--sc-accent, #6366f1) 55%, transparent);
|
|
421
|
+
flex-shrink: 0;
|
|
422
|
+
letter-spacing: 0.02em;
|
|
423
|
+
padding: 1px 4px;
|
|
424
|
+
border-radius: var(--radius-sm, 6px);
|
|
425
|
+
background: color-mix(in srgb, var(--sc-accent, #6366f1) 10%, transparent);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* ── Body ── */
|
|
429
|
+
.sub-canvas__body {
|
|
430
|
+
flex: 1;
|
|
431
|
+
position: relative;
|
|
432
|
+
overflow: hidden;
|
|
433
|
+
user-select: text;
|
|
434
|
+
/* Faint dot-grid identical to ProcedureCanvas surface */
|
|
435
|
+
background:
|
|
436
|
+
radial-gradient(circle, rgba(255, 255, 255, 0.02) 1px, transparent 1px),
|
|
437
|
+
transparent;
|
|
438
|
+
background-size: 20px 20px;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/* ── Resize handles ── */
|
|
442
|
+
.sub-canvas__resize-handle {
|
|
443
|
+
position: absolute;
|
|
444
|
+
background: transparent;
|
|
445
|
+
border: none;
|
|
446
|
+
padding: 0;
|
|
447
|
+
z-index: 10;
|
|
448
|
+
user-select: none;
|
|
449
|
+
/* Reveal faint accent dot on hover */
|
|
450
|
+
border-radius: 2px;
|
|
451
|
+
transition: background 0.1s;
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
.sub-canvas__resize-handle:hover {
|
|
455
|
+
background: color-mix(in srgb, var(--sc-accent, #6366f1) 45%, transparent);
|
|
456
|
+
}
|
|
457
|
+
</style>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { Snippet } from "svelte";
|
|
2
|
+
import type { SubCanvasBounds } from "./SubCanvas.types.js";
|
|
3
|
+
interface Props {
|
|
4
|
+
/** Initial X position in canvas coordinates. */
|
|
5
|
+
x?: number;
|
|
6
|
+
/** Initial Y position in canvas coordinates. */
|
|
7
|
+
y?: number;
|
|
8
|
+
/** Initial width in canvas units. */
|
|
9
|
+
width?: number;
|
|
10
|
+
/** Initial height in canvas units. */
|
|
11
|
+
height?: number;
|
|
12
|
+
/** Display label shown in the header. */
|
|
13
|
+
label?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Nesting depth. Controls accent colour:
|
|
16
|
+
* 0 = indigo (department), 1 = sky (team), 2 = green (group), …
|
|
17
|
+
*/
|
|
18
|
+
depth?: number;
|
|
19
|
+
/**
|
|
20
|
+
* When true, a link icon is shown and double-clicking the header fires `onopen`.
|
|
21
|
+
* Use this for sub-canvases that reference another canvas by ID.
|
|
22
|
+
*/
|
|
23
|
+
linked?: boolean;
|
|
24
|
+
/** Whether this sub-canvas is currently selected. */
|
|
25
|
+
selected?: boolean;
|
|
26
|
+
/** Minimum width during resize (canvas units). */
|
|
27
|
+
minWidth?: number;
|
|
28
|
+
/** Minimum height during resize (canvas units). */
|
|
29
|
+
minHeight?: number;
|
|
30
|
+
/**
|
|
31
|
+
* Current canvas zoom level. Used to scale mouse deltas correctly when
|
|
32
|
+
* the sub-canvas is embedded inside a transformed canvas surface.
|
|
33
|
+
*/
|
|
34
|
+
zoom?: number;
|
|
35
|
+
/** Slot content rendered inside the body area. */
|
|
36
|
+
children?: Snippet;
|
|
37
|
+
/** Custom CSS class applied to the root element. */
|
|
38
|
+
class?: string;
|
|
39
|
+
/** Fired when position or size changes after a drag/resize gesture ends. */
|
|
40
|
+
onchange?: (bounds: SubCanvasBounds) => void;
|
|
41
|
+
/** Fired on click (selection). */
|
|
42
|
+
onclick?: (e: MouseEvent) => void;
|
|
43
|
+
/** Fired on header double-click when `linked` is true. */
|
|
44
|
+
onopen?: () => void;
|
|
45
|
+
}
|
|
46
|
+
declare const SubCanvas: import("svelte").Component<Props, {}, "">;
|
|
47
|
+
type SubCanvas = ReturnType<typeof SubCanvas>;
|
|
48
|
+
export default SubCanvas;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|