@kestack/web-platform 0.0.1 → 0.0.2
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/README.md +6 -0
- package/dist/components/viewers/diff-viewer/diff-viewer.js +1 -1
- package/dist/components/viewers/source-explorer/source-explorer.js +1 -1
- package/dist/recipes/devtools-dock/devtools-dock.d.ts +23 -0
- package/dist/recipes/devtools-dock/devtools-dock.d.ts.map +1 -0
- package/dist/recipes/devtools-dock/devtools-dock.js +343 -0
- package/dist/recipes/devtools-dock/devtools-dock.js.map +1 -0
- package/dist/recipes/devtools-dock/index.d.ts +2 -0
- package/dist/recipes/devtools-dock/index.js +2 -0
- package/dist/recipes/kabuki-devtools/kabuki-devtools.d.ts.map +1 -1
- package/dist/recipes/kabuki-devtools/kabuki-devtools.js +4 -1
- package/dist/recipes/kabuki-devtools/kabuki-devtools.js.map +1 -1
- package/dist/recipes/kabuki-devtools/map/canvas/canvas.d.ts.map +1 -1
- package/dist/recipes/kabuki-devtools/map/canvas/canvas.js +13 -0
- package/dist/recipes/kabuki-devtools/map/canvas/canvas.js.map +1 -1
- package/dist/recipes/kabuki-devtools/scene/tree.js +1 -1
- package/dist/recipes/kabuki-devtools/scene/tree.js.map +1 -1
- package/dist/recipes/kabuki-devtools/sheet/sheet.d.ts.map +1 -1
- package/dist/recipes/kabuki-devtools/sheet/sheet.js +84 -78
- package/dist/recipes/kabuki-devtools/sheet/sheet.js.map +1 -1
- package/dist/recipes/kabuki-devtools/timeline/timeline.d.ts +6 -1
- package/dist/recipes/kabuki-devtools/timeline/timeline.d.ts.map +1 -1
- package/dist/recipes/kabuki-devtools/timeline/timeline.js +202 -16
- package/dist/recipes/kabuki-devtools/timeline/timeline.js.map +1 -1
- package/package.json +13 -13
- package/src/recipes/devtools-dock/devtools-dock.tsx +505 -0
- package/src/recipes/devtools-dock/index.ts +2 -0
- package/src/recipes/kabuki-devtools/kabuki-devtools.tsx +4 -0
- package/src/recipes/kabuki-devtools/map/canvas/canvas.tsx +17 -1
- package/src/recipes/kabuki-devtools/scene/tree.ts +1 -1
- package/src/recipes/kabuki-devtools/sheet/sheet.tsx +95 -107
- package/src/recipes/kabuki-devtools/timeline/timeline.tsx +238 -19
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"canvas.js","names":[],"sources":["../../../../../src/recipes/kabuki-devtools/map/canvas/canvas.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { animate, motion, useMotionValue } from 'motion/react';\nimport { Maximize, Minus, Plus } from 'lucide-react';\nimport { ink } from '../../ink/index.ts';\n\n/*\n * A surface to pan and zoom a drawing on, sized by whatever holds it. Knows\n * nothing of what it draws.\n *\n * 1. Place at 100%, never zoomed in: centred if the whole drawing fits,\n * else centred across with its top a little below the surface's,\n * running down. From then on it stays where the reader leaves\n * it: the surface never moves by itself.\n * 2. Pan drag with a finger or the pointer, from anywhere, over what is\n * drawn too: a press becomes a drag once it moves, and the click\n * it would have made is swallowed. A press that stays put clicks\n * what it is on.\n * 3. Zoom scroll, pinch, or the buttons; always about a point, so what\n * is under it stays there.\n * 4. Fit the whole drawing in view, on demand.\n * 5. Tap a tap on the surface itself, not on anything drawn (marked\n * `ITEM`), says so.\n * A right click never opens the browser's menu here.\n *\n * What is drawn marked `OWN` handles its own scrolls: the surface leaves\n * them alone.\n */\n\nexport interface Bounds {\n readonly left: number;\n readonly right: number;\n readonly top: number;\n readonly bottom: number;\n}\n\nconst MIN = 0.25;\nconst MAX = 2.5;\nconst PAD = 56;\n/** Room above the root when the Map first opens too tall to fit, in pixels. */\nconst TOP = 100;\nconst SPRING = { type: 'spring', duration: 0.45, bounce: 0 } as const;\n\nconst clamp = (scale: number) => Math.min(MAX, Math.max(MIN, scale));\n\n/** Spread on what handles its own presses and scrolls, such as a list that scrolls. */\nexport const OWN = { 'data-canvas-own': '' } as const;\nconst isOwn = (target: EventTarget | null) =>\n target instanceof Element && target.closest('[data-canvas-own]') !== null;\n\n/** Spread on what is drawn, so a tap on it is not a tap on the surface. */\nexport const ITEM = { 'data-canvas-item': '' } as const;\nconst isItem = (target: EventTarget | null) =>\n target instanceof Element && target.closest('[data-canvas-item]') !== null;\n\n/** How far a pointer may move and still tap. */\nconst SLOP = 4;\n\nexport function Canvas({\n bounds,\n label,\n onTap,\n dotted = true,\n children,\n}: {\n /** What the drawing covers, for Fit. */\n readonly bounds: Bounds;\n readonly label: string;\n /** Dots under the drawing; without them, whatever is behind shows through. */\n readonly dotted?: boolean;\n /** The surface was tapped, away from anything drawn. */\n readonly onTap: () => void;\n /** The drawing, its origin at 0, 0. */\n readonly children: ReactNode;\n}) {\n const surface = useRef<HTMLDivElement>(null);\n const x = useMotionValue(0);\n const y = useMotionValue(TOP);\n const scale = useMotionValue(1);\n\n /** Scale to `next` about a point on the surface. */\n const zoomAt = (next: number, px: number, py: number, smooth = false) => {\n const to = clamp(next);\n const ratio = to / scale.get();\n const nx = px - (px - x.get()) * ratio;\n const ny = py - (py - y.get()) * ratio;\n if (smooth) {\n animate(scale, to, SPRING);\n animate(x, nx, SPRING);\n animate(y, ny, SPRING);\n } else {\n scale.set(to);\n x.set(nx);\n y.set(ny);\n }\n };\n\n // 1. Place, once, from the drawing as it first is.\n useEffect(() => {\n const element = surface.current!;\n const width = element.clientWidth;\n const height = element.clientHeight;\n const fits = bounds.bottom - bounds.top <= height - PAD * 2;\n scale.set(1);\n x.set(width / 2 - (bounds.left + bounds.right) / 2);\n y.set(\n fits ? height / 2 - (bounds.top + bounds.bottom) / 2 : TOP - bounds.top,\n );\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- placed once, where the drawing first is; later it stays where the reader leaves it.\n }, [x, y, scale]);\n\n // 3. Zoom, from the wheel: a passive listener could not stop the page scrolling.\n useEffect(() => {\n const element = surface.current!;\n const onWheel = (event: WheelEvent) => {\n if (isOwn(event.target)) return;\n event.preventDefault();\n const box = element.getBoundingClientRect();\n // A trackpad's pinch comes as a wheel with Ctrl, in much smaller steps.\n const rate = event.ctrlKey ? 0.01 : 0.002;\n zoomAt(\n scale.get() * Math.exp(-event.deltaY * rate),\n event.clientX - box.left,\n event.clientY - box.top,\n );\n };\n element.addEventListener('wheel', onWheel, { passive: false });\n return () => element.removeEventListener('wheel', onWheel);\n });\n\n // 2. Pan and 3. Zoom, from fingers and the pointer.\n const pointers = useRef(new Map<number, { x: number; y: number }>());\n /** The press so far: where it began, whether it became a drag, and whether it began on the bare surface. */\n const press = useRef<\n { x: number; y: number; dragging: boolean; bare: boolean } | undefined\n >(undefined);\n /** A drag just ended: the click it makes is not one. */\n const dragged = useRef(false);\n const pinch = useRef<{ distance: number; scale: number } | undefined>(\n undefined,\n );\n const spread = () => {\n const [a, b] = [...pointers.current.values()];\n return {\n distance: Math.hypot(a!.x - b!.x, a!.y - b!.y),\n mid: { x: (a!.x + b!.x) / 2, y: (a!.y + b!.y) / 2 },\n };\n };\n\n const onPointerDown = (event: React.PointerEvent) => {\n // Only the main button pans: a right click is the card's.\n if (event.button !== 0) return;\n dragged.current = false;\n pointers.current.set(event.pointerId, {\n x: event.clientX,\n y: event.clientY,\n });\n if (pointers.current.size === 1)\n press.current = {\n x: event.clientX,\n y: event.clientY,\n dragging: false,\n bare: !isItem(event.target),\n };\n else {\n // A second finger: a pinch, never a click.\n if (press.current) press.current.dragging = true;\n for (const id of pointers.current.keys())\n event.currentTarget.setPointerCapture(id);\n if (pointers.current.size === 2)\n pinch.current = { distance: spread().distance, scale: scale.get() };\n }\n };\n const onPointerMove = (event: React.PointerEvent) => {\n const was = pointers.current.get(event.pointerId);\n if (!was) return;\n const now = { x: event.clientX, y: event.clientY };\n const held = press.current;\n if (held && !held.dragging) {\n // Still a click until it moves far enough.\n if (Math.hypot(now.x - held.x, now.y - held.y) < SLOP) return;\n held.dragging = true;\n event.currentTarget.setPointerCapture(event.pointerId);\n }\n if (pointers.current.size === 1) {\n x.set(x.get() + now.x - was.x);\n y.set(y.get() + now.y - was.y);\n }\n pointers.current.set(event.pointerId, now);\n if (pointers.current.size === 2 && pinch.current) {\n const { distance, mid } = spread();\n const box = surface.current!.getBoundingClientRect();\n zoomAt(\n (pinch.current.scale * distance) / pinch.current.distance,\n mid.x - box.left,\n mid.y - box.top,\n );\n }\n };\n const onPointerUp = (event: React.PointerEvent) => {\n if (!pointers.current.delete(event.pointerId)) return;\n if (pointers.current.size < 2) pinch.current = undefined;\n if (pointers.current.size > 0) return;\n const held = press.current;\n press.current = undefined;\n if (!held) return;\n if (held.dragging) dragged.current = true;\n // 5. Tap\n else if (held.bare && event.type === 'pointerup') onTap();\n };\n const onClickCapture = (event: React.MouseEvent) => {\n if (!dragged.current) return;\n dragged.current = false;\n event.stopPropagation();\n event.preventDefault();\n };\n\n const zoomBy = (factor: number) => {\n const element = surface.current!;\n zoomAt(\n scale.get() * factor,\n element.clientWidth / 2,\n element.clientHeight / 2,\n true,\n );\n };\n\n // 4. Fit\n const fit = () => {\n const element = surface.current!;\n const width = bounds.right - bounds.left;\n const height = bounds.bottom - bounds.top;\n const to = clamp(\n Math.min(\n 1.25,\n (element.clientWidth - PAD * 2) / Math.max(1, width),\n (element.clientHeight - PAD * 2) / Math.max(1, height),\n ),\n );\n animate(scale, to, SPRING);\n animate(\n x,\n element.clientWidth / 2 - ((bounds.left + bounds.right) / 2) * to,\n SPRING,\n );\n animate(\n y,\n element.clientHeight / 2 - ((bounds.top + bounds.bottom) / 2) * to,\n SPRING,\n );\n };\n\n return (\n <div\n ref={surface}\n role=\"application\"\n aria-label={label}\n // Clipped, never hidden: a hidden overflow still scrolls to whatever takes focus, and the drawing would jump.\n className=\"relative h-full w-full cursor-grab touch-none overflow-clip select-none active:cursor-grabbing\"\n style={\n dotted\n ? {\n backgroundImage: `radial-gradient(${ink('grain')} 1px, transparent 1px)`,\n backgroundSize: '20px 20px',\n }\n : undefined\n }\n // No browser menu anywhere on the Map: a right click is the Map's own.\n onContextMenu={(event) => event.preventDefault()}\n onPointerDown={onPointerDown}\n onClickCapture={onClickCapture}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <motion.div\n className=\"absolute top-0 left-0 origin-top-left\"\n style={{ x, y, scale }}\n >\n {children}\n </motion.div>\n <div\n {...ITEM}\n className=\"bg-background/85 absolute top-3 right-3 flex flex-col overflow-hidden rounded-lg border shadow-xs backdrop-blur-sm\"\n >\n <Control label=\"Zoom in\" onClick={() => zoomBy(1.25)}>\n <Plus />\n </Control>\n <Control label=\"Zoom out\" onClick={() => zoomBy(0.8)}>\n <Minus />\n </Control>\n <Control label=\"Fit everything\" onClick={fit}>\n <Maximize />\n </Control>\n </div>\n </div>\n );\n}\n\nfunction Control({\n label,\n onClick,\n children,\n}: {\n readonly label: string;\n readonly onClick: () => void;\n readonly children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n onClick={onClick}\n className=\"text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring grid size-9 place-items-center transition-colors duration-150 outline-none focus-visible:ring-2 focus-visible:ring-inset [&_svg]:size-4\"\n >\n {children}\n </button>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"canvas.js","names":[],"sources":["../../../../../src/recipes/kabuki-devtools/map/canvas/canvas.tsx"],"sourcesContent":["import { useEffect, useRef } from 'react';\nimport type { ReactNode } from 'react';\nimport { animate, motion, useMotionValue } from 'motion/react';\nimport { Maximize, Minus, Plus } from 'lucide-react';\nimport { ink } from '../../ink/index.ts';\n\n/*\n * A surface to pan and zoom a drawing on, sized by whatever holds it. Knows\n * nothing of what it draws.\n *\n * 1. Place at 100%, never zoomed in: centred if the whole drawing fits,\n * else centred across with its top a little below the surface's,\n * running down. From then on it stays where the reader leaves\n * it: the surface never moves by itself, but when it is resized\n * what was at its centre stays at its centre.\n * 2. Pan drag with a finger or the pointer, from anywhere, over what is\n * drawn too: a press becomes a drag once it moves, and the click\n * it would have made is swallowed. A press that stays put clicks\n * what it is on.\n * 3. Zoom scroll, pinch, or the buttons; always about a point, so what\n * is under it stays there.\n * 4. Fit the whole drawing in view, on demand.\n * 5. Tap a tap on the surface itself, not on anything drawn (marked\n * `ITEM`), says so.\n * A right click never opens the browser's menu here.\n *\n * What is drawn marked `OWN` handles its own scrolls: the surface leaves\n * them alone.\n */\n\nexport interface Bounds {\n readonly left: number;\n readonly right: number;\n readonly top: number;\n readonly bottom: number;\n}\n\nconst MIN = 0.25;\nconst MAX = 2.5;\nconst PAD = 56;\n/** Room above the root when the Map first opens too tall to fit, in pixels. */\nconst TOP = 100;\nconst SPRING = { type: 'spring', duration: 0.45, bounce: 0 } as const;\n\nconst clamp = (scale: number) => Math.min(MAX, Math.max(MIN, scale));\n\n/** Spread on what handles its own presses and scrolls, such as a list that scrolls. */\nexport const OWN = { 'data-canvas-own': '' } as const;\nconst isOwn = (target: EventTarget | null) =>\n target instanceof Element && target.closest('[data-canvas-own]') !== null;\n\n/** Spread on what is drawn, so a tap on it is not a tap on the surface. */\nexport const ITEM = { 'data-canvas-item': '' } as const;\nconst isItem = (target: EventTarget | null) =>\n target instanceof Element && target.closest('[data-canvas-item]') !== null;\n\n/** How far a pointer may move and still tap. */\nconst SLOP = 4;\n\nexport function Canvas({\n bounds,\n label,\n onTap,\n dotted = true,\n children,\n}: {\n /** What the drawing covers, for Fit. */\n readonly bounds: Bounds;\n readonly label: string;\n /** Dots under the drawing; without them, whatever is behind shows through. */\n readonly dotted?: boolean;\n /** The surface was tapped, away from anything drawn. */\n readonly onTap: () => void;\n /** The drawing, its origin at 0, 0. */\n readonly children: ReactNode;\n}) {\n const surface = useRef<HTMLDivElement>(null);\n const x = useMotionValue(0);\n const y = useMotionValue(TOP);\n const scale = useMotionValue(1);\n\n /** Scale to `next` about a point on the surface. */\n const zoomAt = (next: number, px: number, py: number, smooth = false) => {\n const to = clamp(next);\n const ratio = to / scale.get();\n const nx = px - (px - x.get()) * ratio;\n const ny = py - (py - y.get()) * ratio;\n if (smooth) {\n animate(scale, to, SPRING);\n animate(x, nx, SPRING);\n animate(y, ny, SPRING);\n } else {\n scale.set(to);\n x.set(nx);\n y.set(ny);\n }\n };\n\n // 1. Place, once, from the drawing as it first is.\n useEffect(() => {\n const element = surface.current!;\n const width = element.clientWidth;\n const height = element.clientHeight;\n const fits = bounds.bottom - bounds.top <= height - PAD * 2;\n scale.set(1);\n x.set(width / 2 - (bounds.left + bounds.right) / 2);\n y.set(\n fits ? height / 2 - (bounds.top + bounds.bottom) / 2 : TOP - bounds.top,\n );\n // oxlint-disable-next-line react-hooks/exhaustive-deps -- placed once, where the drawing first is; later it stays where the reader leaves it.\n }, [x, y, scale]);\n\n // 1. Place: a resize keeps the centre where it was.\n useEffect(() => {\n const element = surface.current!;\n let width = element.clientWidth;\n let height = element.clientHeight;\n const observer = new ResizeObserver(() => {\n x.set(x.get() + (element.clientWidth - width) / 2);\n y.set(y.get() + (element.clientHeight - height) / 2);\n width = element.clientWidth;\n height = element.clientHeight;\n });\n observer.observe(element);\n return () => observer.disconnect();\n }, [x, y]);\n\n // 3. Zoom, from the wheel: a passive listener could not stop the page scrolling.\n useEffect(() => {\n const element = surface.current!;\n const onWheel = (event: WheelEvent) => {\n if (isOwn(event.target)) return;\n event.preventDefault();\n const box = element.getBoundingClientRect();\n // A trackpad's pinch comes as a wheel with Ctrl, in much smaller steps.\n const rate = event.ctrlKey ? 0.01 : 0.002;\n zoomAt(\n scale.get() * Math.exp(-event.deltaY * rate),\n event.clientX - box.left,\n event.clientY - box.top,\n );\n };\n element.addEventListener('wheel', onWheel, { passive: false });\n return () => element.removeEventListener('wheel', onWheel);\n });\n\n // 2. Pan and 3. Zoom, from fingers and the pointer.\n const pointers = useRef(new Map<number, { x: number; y: number }>());\n /** The press so far: where it began, whether it became a drag, and whether it began on the bare surface. */\n const press = useRef<\n { x: number; y: number; dragging: boolean; bare: boolean } | undefined\n >(undefined);\n /** A drag just ended: the click it makes is not one. */\n const dragged = useRef(false);\n const pinch = useRef<{ distance: number; scale: number } | undefined>(\n undefined,\n );\n const spread = () => {\n const [a, b] = [...pointers.current.values()];\n return {\n distance: Math.hypot(a!.x - b!.x, a!.y - b!.y),\n mid: { x: (a!.x + b!.x) / 2, y: (a!.y + b!.y) / 2 },\n };\n };\n\n const onPointerDown = (event: React.PointerEvent) => {\n // Only the main button pans: a right click is the card's.\n if (event.button !== 0) return;\n dragged.current = false;\n pointers.current.set(event.pointerId, {\n x: event.clientX,\n y: event.clientY,\n });\n if (pointers.current.size === 1)\n press.current = {\n x: event.clientX,\n y: event.clientY,\n dragging: false,\n bare: !isItem(event.target),\n };\n else {\n // A second finger: a pinch, never a click.\n if (press.current) press.current.dragging = true;\n for (const id of pointers.current.keys())\n event.currentTarget.setPointerCapture(id);\n if (pointers.current.size === 2)\n pinch.current = { distance: spread().distance, scale: scale.get() };\n }\n };\n const onPointerMove = (event: React.PointerEvent) => {\n const was = pointers.current.get(event.pointerId);\n if (!was) return;\n const now = { x: event.clientX, y: event.clientY };\n const held = press.current;\n if (held && !held.dragging) {\n // Still a click until it moves far enough.\n if (Math.hypot(now.x - held.x, now.y - held.y) < SLOP) return;\n held.dragging = true;\n event.currentTarget.setPointerCapture(event.pointerId);\n }\n if (pointers.current.size === 1) {\n x.set(x.get() + now.x - was.x);\n y.set(y.get() + now.y - was.y);\n }\n pointers.current.set(event.pointerId, now);\n if (pointers.current.size === 2 && pinch.current) {\n const { distance, mid } = spread();\n const box = surface.current!.getBoundingClientRect();\n zoomAt(\n (pinch.current.scale * distance) / pinch.current.distance,\n mid.x - box.left,\n mid.y - box.top,\n );\n }\n };\n const onPointerUp = (event: React.PointerEvent) => {\n if (!pointers.current.delete(event.pointerId)) return;\n if (pointers.current.size < 2) pinch.current = undefined;\n if (pointers.current.size > 0) return;\n const held = press.current;\n press.current = undefined;\n if (!held) return;\n if (held.dragging) dragged.current = true;\n // 5. Tap\n else if (held.bare && event.type === 'pointerup') onTap();\n };\n const onClickCapture = (event: React.MouseEvent) => {\n if (!dragged.current) return;\n dragged.current = false;\n event.stopPropagation();\n event.preventDefault();\n };\n\n const zoomBy = (factor: number) => {\n const element = surface.current!;\n zoomAt(\n scale.get() * factor,\n element.clientWidth / 2,\n element.clientHeight / 2,\n true,\n );\n };\n\n // 4. Fit\n const fit = () => {\n const element = surface.current!;\n const width = bounds.right - bounds.left;\n const height = bounds.bottom - bounds.top;\n const to = clamp(\n Math.min(\n 1.25,\n (element.clientWidth - PAD * 2) / Math.max(1, width),\n (element.clientHeight - PAD * 2) / Math.max(1, height),\n ),\n );\n animate(scale, to, SPRING);\n animate(\n x,\n element.clientWidth / 2 - ((bounds.left + bounds.right) / 2) * to,\n SPRING,\n );\n animate(\n y,\n element.clientHeight / 2 - ((bounds.top + bounds.bottom) / 2) * to,\n SPRING,\n );\n };\n\n return (\n <div\n ref={surface}\n role=\"application\"\n aria-label={label}\n // Clipped, never hidden: a hidden overflow still scrolls to whatever takes focus, and the drawing would jump.\n className=\"relative h-full w-full cursor-grab touch-none overflow-clip select-none active:cursor-grabbing\"\n style={\n dotted\n ? {\n backgroundImage: `radial-gradient(${ink('grain')} 1px, transparent 1px)`,\n backgroundSize: '20px 20px',\n }\n : undefined\n }\n // No browser menu anywhere on the Map: a right click is the Map's own.\n onContextMenu={(event) => event.preventDefault()}\n onPointerDown={onPointerDown}\n onClickCapture={onClickCapture}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerUp}\n onPointerCancel={onPointerUp}\n >\n <motion.div\n className=\"absolute top-0 left-0 origin-top-left\"\n style={{ x, y, scale }}\n >\n {children}\n </motion.div>\n <div\n {...ITEM}\n className=\"bg-background/85 absolute top-3 right-3 flex flex-col overflow-hidden rounded-lg border shadow-xs backdrop-blur-sm\"\n >\n <Control label=\"Zoom in\" onClick={() => zoomBy(1.25)}>\n <Plus />\n </Control>\n <Control label=\"Zoom out\" onClick={() => zoomBy(0.8)}>\n <Minus />\n </Control>\n <Control label=\"Fit everything\" onClick={fit}>\n <Maximize />\n </Control>\n </div>\n </div>\n );\n}\n\nfunction Control({\n label,\n onClick,\n children,\n}: {\n readonly label: string;\n readonly onClick: () => void;\n readonly children: ReactNode;\n}) {\n return (\n <button\n type=\"button\"\n aria-label={label}\n title={label}\n onClick={onClick}\n className=\"text-muted-foreground hover:text-foreground hover:bg-muted focus-visible:ring-ring grid size-9 place-items-center transition-colors duration-150 outline-none focus-visible:ring-2 focus-visible:ring-inset [&_svg]:size-4\"\n >\n {children}\n </button>\n );\n}\n"],"mappings":"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"}
|
|
@@ -62,7 +62,7 @@ const treeOf = (root, snapshot, statuses, chosen = /* @__PURE__ */ new Map(), to
|
|
|
62
62
|
const slots = (state, lasting, holders) => Object.entries(definition.children[state] ?? {}).filter(([name]) => definition.lasting.includes(name) === lasting).map(([name, held]) => {
|
|
63
63
|
const slot = `${id}>${name}`;
|
|
64
64
|
for (const holder of instances) drawnAt.set(`${holder.id}/${name}`, slot);
|
|
65
|
-
return actor(held.definition, holders.flatMap((holder) => asList(holder.children[name])), slot, held.
|
|
65
|
+
return actor(held.definition, holders.flatMap((holder) => asList(holder.children[name])), slot, held.kind === "each", below);
|
|
66
66
|
});
|
|
67
67
|
const byState = stateless ? slots(SINGLE, false, shown).map((item) => ({ item })) : definition.states.flatMap((tag) => slots(tag, false, shown.filter((each) => each.state._tag === tag)).map((item) => ({
|
|
68
68
|
via: tag,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tree.js","names":[],"sources":["../../../../src/recipes/kabuki-devtools/scene/tree.ts"],"sourcesContent":["import { isMany } from '@kestack/kabuki';\nimport type { ActorDefinition, Instance, Snapshot } from '@kestack/kabuki';\nimport { measure } from './nodes.ts';\nimport type { Item, WorkerKey, WorkerRow, WorkerStatus } from './nodes.ts';\n\n/*\n * The Map's tree: its shape from the Actors alone, what runs in it from one\n * Snapshot.\n *\n * One Node per place an Actor can run. Below it: the Children each of its\n * States Invokes, marked with that State, then the Children written at its\n * top. A dim Actor is drawn with everything below it, except an Actor\n * already above it, so a tree of itself ends.\n *\n * Every Node holds every Instance at its place now: one for a fixed Child,\n * any number for a slot of keyed Children. A slot of keyed Children shows\n * one of them, the chosen one: its States count every Instance but mark the\n * chosen one's, its Workers are the chosen one's, and only the chosen one's\n * Children hang below it, so every place below holds at most what one\n * Instance has. Unless another is chosen, it is the one on the way to the\n * Step's Message, or the first.\n *\n * Every Instance and Worker key is found at the Node it is drawn on, and an\n * Instance the Step stopped at its slot's Node, so a Message it Sent still\n * has somewhere to start.\n */\n\n/** What the live app says of an Instance's Workers; nothing away from it. */\nexport type Statuses = (instance: string) =>\n | ReadonlyArray<{\n readonly name: string;\n readonly key: string;\n readonly status: Exclude<WorkerStatus, 'on'>;\n }>\n | undefined;\n\nconst SINGLE = 'Single';\n\nconst isStateless = (definition: ActorDefinition) =>\n definition.states.length === 1 && definition.states[0] === SINGLE;\n\n/** An Instance ID without its key and how many times it was Invoked: its parent's ID and its slot. */\nconst slotOf = (id: string) => id.replace(/(\\[[^\\]]*\\])?#\\d+$/, '');\n\nconst asList = (\n held: Instance | ReadonlyArray<Instance> | undefined,\n): ReadonlyArray<Instance> =>\n held === undefined ? [] : isMany(held) ? held : [held];\n\nexport const treeOf = (\n root: ActorDefinition,\n snapshot: Snapshot,\n statuses: Statuses,\n /** The Instance chosen at a place of many, by the place's ID. */\n chosen: ReadonlyMap<string, string> = new Map(),\n /** Instances to choose first, unless one is chosen: the Message's ends. */\n toward: ReadonlyArray<string> = [],\n): {\n readonly tree: Item;\n /** The Node an Instance, or one of its Worker keys, is drawn on. */\n readonly find: (id: string) => string | undefined;\n} => {\n const drawnAt = new Map<string, string>();\n\n /** One row per Worker the Actor has, each with the keys its Instances run now. */\n const workers = (\n definition: ActorDefinition,\n instances: ReadonlyArray<Instance>,\n owner: string,\n ): ReadonlyArray<WorkerRow> =>\n Object.entries(definition.workers).map(([name, worker]) => ({\n name,\n ...(worker.state === undefined ? {} : { state: worker.state }),\n keys: instances.flatMap((instance) => {\n if (worker.state !== undefined && worker.state !== instance.state._tag)\n return [];\n const live = statuses(instance.id);\n const data = {\n model: instance.model,\n state: instance.state,\n props: instance.props,\n };\n return worker.keys(data).map((key): WorkerKey => {\n const id = `${instance.id}~${name}#${key}`;\n drawnAt.set(id, owner);\n return {\n id,\n key,\n instance,\n status: live\n ? (live.find((each) => each.name === name && each.key === key)\n ?.status ?? 'running')\n : 'on',\n };\n });\n }),\n }));\n\n const actor = (\n definition: ActorDefinition,\n instances: ReadonlyArray<Instance>,\n id: string,\n many: boolean,\n above: ReadonlySet<ActorDefinition>,\n ): Item => {\n for (const instance of instances) drawnAt.set(instance.id, id);\n const stateless = isStateless(definition);\n const pick = many\n ? (instances.find((each) => each.id === chosen.get(id)) ??\n instances.find((each) =>\n toward.some((to) => to === each.id || to.startsWith(`${each.id}/`)),\n ) ??\n instances[0])\n : undefined;\n // What the card shows and what hangs below it: the chosen one's, at a place of many.\n const shown = many ? (pick ? [pick] : []) : instances;\n const size = measure(\n definition.name,\n many,\n stateless ? undefined : definition.states,\n Object.keys(definition.workers),\n );\n const count = (tag: string) =>\n instances.filter((each) => each.state._tag === tag).length;\n const node = {\n id,\n name: definition.name,\n many,\n instances,\n ...(pick ? { chosen: pick } : {}),\n ...(stateless\n ? {}\n : {\n states: size.lines.map((line) =>\n line.map((tag) => ({ tag, count: count(tag) })),\n ),\n }),\n workers: workers(definition, shown, id),\n width: size.width,\n height: size.height,\n };\n // A dim Actor already above it would draw itself forever.\n if (instances.length === 0 && above.has(definition))\n return { node, children: [] };\n const below = new Set(above).add(definition);\n\n /** The slots written in one State, or at the top, held by `holders`. */\n const slots = (\n state: string,\n lasting: boolean,\n holders: ReadonlyArray<Instance>,\n ) =>\n Object.entries(definition.children[state] ?? {})\n .filter(([name]) => definition.lasting.includes(name) === lasting)\n .map(([name, held]) => {\n const slot = `${id}>${name}`;\n // An Instance this Step stopped is found at its slot.\n for (const holder of instances)\n drawnAt.set(`${holder.id}/${name}`, slot);\n return actor(\n held.definition,\n holders.flatMap((holder) => asList(holder.children[name])),\n slot,\n held.
|
|
1
|
+
{"version":3,"file":"tree.js","names":[],"sources":["../../../../src/recipes/kabuki-devtools/scene/tree.ts"],"sourcesContent":["import { isMany } from '@kestack/kabuki';\nimport type { ActorDefinition, Instance, Snapshot } from '@kestack/kabuki';\nimport { measure } from './nodes.ts';\nimport type { Item, WorkerKey, WorkerRow, WorkerStatus } from './nodes.ts';\n\n/*\n * The Map's tree: its shape from the Actors alone, what runs in it from one\n * Snapshot.\n *\n * One Node per place an Actor can run. Below it: the Children each of its\n * States Invokes, marked with that State, then the Children written at its\n * top. A dim Actor is drawn with everything below it, except an Actor\n * already above it, so a tree of itself ends.\n *\n * Every Node holds every Instance at its place now: one for a fixed Child,\n * any number for a slot of keyed Children. A slot of keyed Children shows\n * one of them, the chosen one: its States count every Instance but mark the\n * chosen one's, its Workers are the chosen one's, and only the chosen one's\n * Children hang below it, so every place below holds at most what one\n * Instance has. Unless another is chosen, it is the one on the way to the\n * Step's Message, or the first.\n *\n * Every Instance and Worker key is found at the Node it is drawn on, and an\n * Instance the Step stopped at its slot's Node, so a Message it Sent still\n * has somewhere to start.\n */\n\n/** What the live app says of an Instance's Workers; nothing away from it. */\nexport type Statuses = (instance: string) =>\n | ReadonlyArray<{\n readonly name: string;\n readonly key: string;\n readonly status: Exclude<WorkerStatus, 'on'>;\n }>\n | undefined;\n\nconst SINGLE = 'Single';\n\nconst isStateless = (definition: ActorDefinition) =>\n definition.states.length === 1 && definition.states[0] === SINGLE;\n\n/** An Instance ID without its key and how many times it was Invoked: its parent's ID and its slot. */\nconst slotOf = (id: string) => id.replace(/(\\[[^\\]]*\\])?#\\d+$/, '');\n\nconst asList = (\n held: Instance | ReadonlyArray<Instance> | undefined,\n): ReadonlyArray<Instance> =>\n held === undefined ? [] : isMany(held) ? held : [held];\n\nexport const treeOf = (\n root: ActorDefinition,\n snapshot: Snapshot,\n statuses: Statuses,\n /** The Instance chosen at a place of many, by the place's ID. */\n chosen: ReadonlyMap<string, string> = new Map(),\n /** Instances to choose first, unless one is chosen: the Message's ends. */\n toward: ReadonlyArray<string> = [],\n): {\n readonly tree: Item;\n /** The Node an Instance, or one of its Worker keys, is drawn on. */\n readonly find: (id: string) => string | undefined;\n} => {\n const drawnAt = new Map<string, string>();\n\n /** One row per Worker the Actor has, each with the keys its Instances run now. */\n const workers = (\n definition: ActorDefinition,\n instances: ReadonlyArray<Instance>,\n owner: string,\n ): ReadonlyArray<WorkerRow> =>\n Object.entries(definition.workers).map(([name, worker]) => ({\n name,\n ...(worker.state === undefined ? {} : { state: worker.state }),\n keys: instances.flatMap((instance) => {\n if (worker.state !== undefined && worker.state !== instance.state._tag)\n return [];\n const live = statuses(instance.id);\n const data = {\n model: instance.model,\n state: instance.state,\n props: instance.props,\n };\n return worker.keys(data).map((key): WorkerKey => {\n const id = `${instance.id}~${name}#${key}`;\n drawnAt.set(id, owner);\n return {\n id,\n key,\n instance,\n status: live\n ? (live.find((each) => each.name === name && each.key === key)\n ?.status ?? 'running')\n : 'on',\n };\n });\n }),\n }));\n\n const actor = (\n definition: ActorDefinition,\n instances: ReadonlyArray<Instance>,\n id: string,\n many: boolean,\n above: ReadonlySet<ActorDefinition>,\n ): Item => {\n for (const instance of instances) drawnAt.set(instance.id, id);\n const stateless = isStateless(definition);\n const pick = many\n ? (instances.find((each) => each.id === chosen.get(id)) ??\n instances.find((each) =>\n toward.some((to) => to === each.id || to.startsWith(`${each.id}/`)),\n ) ??\n instances[0])\n : undefined;\n // What the card shows and what hangs below it: the chosen one's, at a place of many.\n const shown = many ? (pick ? [pick] : []) : instances;\n const size = measure(\n definition.name,\n many,\n stateless ? undefined : definition.states,\n Object.keys(definition.workers),\n );\n const count = (tag: string) =>\n instances.filter((each) => each.state._tag === tag).length;\n const node = {\n id,\n name: definition.name,\n many,\n instances,\n ...(pick ? { chosen: pick } : {}),\n ...(stateless\n ? {}\n : {\n states: size.lines.map((line) =>\n line.map((tag) => ({ tag, count: count(tag) })),\n ),\n }),\n workers: workers(definition, shown, id),\n width: size.width,\n height: size.height,\n };\n // A dim Actor already above it would draw itself forever.\n if (instances.length === 0 && above.has(definition))\n return { node, children: [] };\n const below = new Set(above).add(definition);\n\n /** The slots written in one State, or at the top, held by `holders`. */\n const slots = (\n state: string,\n lasting: boolean,\n holders: ReadonlyArray<Instance>,\n ) =>\n Object.entries(definition.children[state] ?? {})\n .filter(([name]) => definition.lasting.includes(name) === lasting)\n .map(([name, held]) => {\n const slot = `${id}>${name}`;\n // An Instance this Step stopped is found at its slot.\n for (const holder of instances)\n drawnAt.set(`${holder.id}/${name}`, slot);\n return actor(\n held.definition,\n holders.flatMap((holder) => asList(holder.children[name])),\n slot,\n held.kind === 'each',\n below,\n );\n });\n\n const byState = stateless\n ? slots(SINGLE, false, shown).map((item) => ({ item }))\n : definition.states.flatMap((tag) =>\n slots(\n tag,\n false,\n shown.filter((each) => each.state._tag === tag),\n ).map((item) => ({ via: tag, item })),\n );\n const lasting = slots(\n stateless ? SINGLE : definition.states[0]!,\n true,\n shown,\n ).map((item) => ({ item }));\n return { node, children: [...byState, ...lasting] };\n };\n\n const tree = actor(root, [snapshot], root.name, false, new Set());\n return {\n tree,\n find: (id) => drawnAt.get(id) ?? drawnAt.get(slotOf(id)),\n };\n};\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.d.ts","names":[],"sources":["../../../../src/recipes/kabuki-devtools/sheet/sheet.tsx"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"sheet.d.ts","names":[],"sources":["../../../../src/recipes/kabuki-devtools/sheet/sheet.tsx"],"mappings":";;;;iBAuBiB;WACN;WACA,OAAO;;iBAGD;WACN;WACA;WACA,MAAM;;iBAGA;WACN;WACA;;wBAKK,QACd,MACA,MACA,OACA,OACA,MACA,UACA;;WAGS;WACA,OAAO;WACP;WACA,QAAQ,cAAc;;WAEtB;aACE;aACA,OAAO,cAAc;;WAEvB,WAAW,cAAc;WACzB;oBACV,IAAA"}
|
|
@@ -13,112 +13,118 @@ const OUT = [
|
|
|
13
13
|
];
|
|
14
14
|
function Sheet({ kind, tone = "lit", title, facts = [], rows, sections = [], onClose }) {
|
|
15
15
|
const still = useReducedMotion();
|
|
16
|
-
return /* @__PURE__ */
|
|
16
|
+
return /* @__PURE__ */ jsxs(motion.section, {
|
|
17
17
|
"aria-label": `${kind} ${title}`,
|
|
18
|
-
className: cn("bg-
|
|
19
|
-
initial: still ? false : {
|
|
20
|
-
|
|
18
|
+
className: cn("bg-popover text-popover-foreground absolute z-30 flex flex-col overflow-hidden border", "shadow-[0_1px_2px_rgb(0_0_0/0.06),0_12px_32px_rgb(0_0_0/0.16)]", "inset-x-0 bottom-0 max-h-[70%] rounded-t-xl border-b-0", "@3xl:inset-x-auto @3xl:top-3 @3xl:right-3 @3xl:bottom-auto @3xl:max-h-[calc(100%-1.5rem)] @3xl:w-88 @3xl:rounded-xl @3xl:border-b"),
|
|
19
|
+
initial: still ? false : {
|
|
20
|
+
opacity: 0,
|
|
21
|
+
y: 12
|
|
22
|
+
},
|
|
23
|
+
animate: {
|
|
24
|
+
opacity: 1,
|
|
25
|
+
y: 0
|
|
26
|
+
},
|
|
21
27
|
exit: still ? {
|
|
22
|
-
|
|
28
|
+
opacity: 0,
|
|
23
29
|
transition: { duration: 0 }
|
|
24
30
|
} : {
|
|
25
|
-
|
|
31
|
+
opacity: 0,
|
|
32
|
+
y: 8,
|
|
26
33
|
transition: {
|
|
27
34
|
duration: .12,
|
|
28
35
|
ease: "easeIn"
|
|
29
36
|
}
|
|
30
37
|
},
|
|
31
38
|
transition: {
|
|
32
|
-
duration: .
|
|
39
|
+
duration: .2,
|
|
33
40
|
ease: OUT
|
|
34
41
|
},
|
|
35
|
-
children: /* @__PURE__ */ jsxs("
|
|
36
|
-
className: "flex
|
|
37
|
-
children: [/* @__PURE__ */ jsxs("
|
|
38
|
-
className: "
|
|
39
|
-
children: [/* @__PURE__ */ jsxs("
|
|
40
|
-
className: "
|
|
41
|
-
children: [/* @__PURE__ */ jsx("
|
|
42
|
-
className: "
|
|
43
|
-
style: {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
})]
|
|
49
|
-
}), /* @__PURE__ */ jsx("button", {
|
|
50
|
-
type: "button",
|
|
51
|
-
"aria-label": "Close",
|
|
52
|
-
onClick: onClose,
|
|
53
|
-
className: "text-muted-foreground hover:text-foreground hover:bg-muted grid size-8 shrink-0 place-items-center rounded-md transition-colors duration-150 outline-none focus-visible:ring-2 [&_svg]:size-4",
|
|
54
|
-
children: /* @__PURE__ */ jsx(X, {})
|
|
42
|
+
children: [/* @__PURE__ */ jsxs("header", {
|
|
43
|
+
className: "flex shrink-0 items-start gap-3 border-b px-4 py-3",
|
|
44
|
+
children: [/* @__PURE__ */ jsxs("div", {
|
|
45
|
+
className: "min-w-0 flex-1",
|
|
46
|
+
children: [/* @__PURE__ */ jsxs("p", {
|
|
47
|
+
className: "text-muted-foreground flex items-center gap-1.5 text-[11px] font-medium",
|
|
48
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
49
|
+
className: "size-1.5 rounded-full",
|
|
50
|
+
style: { backgroundColor: ink(tone) }
|
|
51
|
+
}), kind]
|
|
52
|
+
}), /* @__PURE__ */ jsx("h2", {
|
|
53
|
+
className: "mt-0.5 truncate text-[15px] font-semibold",
|
|
54
|
+
children: title
|
|
55
55
|
})]
|
|
56
|
-
}), /* @__PURE__ */
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
56
|
+
}), /* @__PURE__ */ jsx("button", {
|
|
57
|
+
type: "button",
|
|
58
|
+
"aria-label": "Close",
|
|
59
|
+
onClick: onClose,
|
|
60
|
+
className: "text-muted-foreground hover:text-foreground hover:bg-muted -mr-1.5 grid size-8 shrink-0 place-items-center rounded-md transition-colors duration-150 outline-none focus-visible:ring-2 [&_svg]:size-4",
|
|
61
|
+
children: /* @__PURE__ */ jsx(X, {})
|
|
62
|
+
})]
|
|
63
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
64
|
+
className: "flex min-h-0 flex-col gap-5 overflow-y-auto overscroll-contain px-4 py-3.5",
|
|
65
|
+
children: [
|
|
66
|
+
facts.length > 0 && /* @__PURE__ */ jsx("dl", {
|
|
67
|
+
className: "grid grid-cols-[minmax(4.5rem,auto)_1fr] gap-x-4 gap-y-2 text-xs",
|
|
68
|
+
children: facts.map((fact) => /* @__PURE__ */ jsxs("div", {
|
|
69
|
+
className: "contents",
|
|
70
|
+
children: [/* @__PURE__ */ jsx("dt", {
|
|
71
|
+
className: "text-muted-foreground",
|
|
72
|
+
children: fact.label
|
|
73
|
+
}), /* @__PURE__ */ jsx("dd", {
|
|
74
|
+
className: "min-w-0 truncate font-mono text-[11.5px]",
|
|
75
|
+
children: fact.value
|
|
76
|
+
})]
|
|
77
|
+
}, fact.label))
|
|
78
|
+
}),
|
|
79
|
+
rows && /* @__PURE__ */ jsx(Part, {
|
|
80
|
+
title: rows.title,
|
|
81
|
+
children: rows.items.length === 0 ? /* @__PURE__ */ jsx("p", {
|
|
82
|
+
className: "text-muted-foreground text-xs",
|
|
83
|
+
children: "None running."
|
|
84
|
+
}) : /* @__PURE__ */ jsx("ul", {
|
|
85
|
+
className: "flex flex-col gap-1.5",
|
|
86
|
+
children: rows.items.map((row) => /* @__PURE__ */ jsxs("li", {
|
|
87
|
+
className: "flex items-center gap-2 text-xs",
|
|
88
|
+
children: [
|
|
89
|
+
/* @__PURE__ */ jsx("span", {
|
|
90
|
+
className: "size-1.5 shrink-0 rounded-full",
|
|
91
|
+
style: { backgroundColor: ink(row.tone) }
|
|
92
|
+
}),
|
|
93
|
+
/* @__PURE__ */ jsx("span", {
|
|
94
|
+
className: "min-w-0 flex-1 truncate font-mono text-[11.5px]",
|
|
95
|
+
children: row.label
|
|
96
|
+
}),
|
|
97
|
+
/* @__PURE__ */ jsx("span", {
|
|
98
|
+
className: "text-muted-foreground",
|
|
99
|
+
children: row.detail
|
|
100
|
+
})
|
|
101
|
+
]
|
|
102
|
+
}, row.label))
|
|
103
|
+
})
|
|
104
|
+
}),
|
|
105
|
+
sections.map((section) => /* @__PURE__ */ jsx(Part, {
|
|
99
106
|
title: section.title,
|
|
100
|
-
className: "@3xl:w-80 @3xl:shrink-0 @3xl:overflow-y-auto",
|
|
101
107
|
children: section.value !== null && typeof section.value === "object" ? /* @__PURE__ */ jsx("div", {
|
|
102
|
-
className: "bg-muted/40 rounded-lg
|
|
108
|
+
className: "bg-muted/40 -mx-1 rounded-lg px-2.5 py-2",
|
|
103
109
|
children: /* @__PURE__ */ jsx(JsonTree, {
|
|
104
110
|
value: section.value,
|
|
105
|
-
collapsed:
|
|
111
|
+
collapsed: 3,
|
|
106
112
|
tone: "syntax"
|
|
107
113
|
})
|
|
108
114
|
}) : /* @__PURE__ */ jsx("p", {
|
|
109
115
|
className: "font-mono text-xs",
|
|
110
116
|
children: JSON.stringify(section.value) ?? "nothing"
|
|
111
117
|
})
|
|
112
|
-
}, section.title))
|
|
113
|
-
|
|
114
|
-
})
|
|
118
|
+
}, section.title))
|
|
119
|
+
]
|
|
120
|
+
})]
|
|
115
121
|
});
|
|
116
122
|
}
|
|
117
123
|
function Part({ title, className, children }) {
|
|
118
124
|
return /* @__PURE__ */ jsxs("section", {
|
|
119
|
-
className: cn("flex flex-col gap-
|
|
125
|
+
className: cn("flex flex-col gap-2", className),
|
|
120
126
|
children: [/* @__PURE__ */ jsx("h3", {
|
|
121
|
-
className: "text-muted-foreground text-[
|
|
127
|
+
className: "text-muted-foreground text-[11px] font-medium",
|
|
122
128
|
children: title
|
|
123
129
|
}), children]
|
|
124
130
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sheet.js","names":[],"sources":["../../../../src/recipes/kabuki-devtools/sheet/sheet.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'motion/react';\nimport { X } from 'lucide-react';\nimport { cn } from '../../../components/lib/utils.ts';\nimport { JsonTree } from '../../../components/viewers/json/index.ts';\nimport { ink } from '../ink/index.ts';\nimport type { Tone } from '../ink/index.ts';\n\n/*\n * The Sheet: the data of what is picked,
|
|
1
|
+
{"version":3,"file":"sheet.js","names":[],"sources":["../../../../src/recipes/kabuki-devtools/sheet/sheet.tsx"],"sourcesContent":["import type { ReactNode } from 'react';\nimport { motion, useReducedMotion } from 'motion/react';\nimport { X } from 'lucide-react';\nimport { cn } from '../../../components/lib/utils.ts';\nimport { JsonTree } from '../../../components/viewers/json/index.ts';\nimport { ink } from '../ink/index.ts';\nimport type { Tone } from '../ink/index.ts';\n\n/*\n * The Sheet: the data of what is picked, over the Map.\n *\n * 1. Open it slides in quickly, and away when closed. Picking something\n * else changes what it says in place: it never closes and opens\n * again.\n * 2. Fit on a wide Inspector it is a panel floating on the Map's right,\n * as tall as it needs and no taller than the Map; on a narrow one\n * it is a drawer from the bottom, the full width. Either way\n * everything is stacked, since every value is a tree that grows\n * down.\n * 3. Read what it is and its name, a few facts, a list such as its\n * Workers, then each value as a tree.\n */\n\nexport interface SheetFact {\n readonly label: string;\n readonly value: ReactNode;\n}\n\nexport interface SheetRow {\n readonly label: string;\n readonly detail: string;\n readonly tone: Tone;\n}\n\nexport interface SheetSection {\n readonly title: string;\n readonly value: unknown;\n}\n\nconst OUT = [0.23, 1, 0.32, 1] as const;\n\nexport function Sheet({\n kind,\n tone = 'lit',\n title,\n facts = [],\n rows,\n sections = [],\n onClose,\n}: {\n /** What is picked: an Instance, a State, a Worker, the Message. */\n readonly kind: string;\n readonly tone?: Tone;\n readonly title: string;\n readonly facts?: ReadonlyArray<SheetFact>;\n /** A short list, such as Workers, with its title. */\n readonly rows?: {\n readonly title: string;\n readonly items: ReadonlyArray<SheetRow>;\n };\n readonly sections?: ReadonlyArray<SheetSection>;\n readonly onClose: () => void;\n}) {\n const still = useReducedMotion();\n return (\n // 1. Open\n <motion.section\n aria-label={`${kind} ${title}`}\n className={cn(\n 'bg-popover text-popover-foreground absolute z-30 flex flex-col overflow-hidden border',\n 'shadow-[0_1px_2px_rgb(0_0_0/0.06),0_12px_32px_rgb(0_0_0/0.16)]',\n // 2. Fit: a drawer from the bottom when narrow, a panel on the right when wide.\n 'inset-x-0 bottom-0 max-h-[70%] rounded-t-xl border-b-0',\n '@3xl:inset-x-auto @3xl:top-3 @3xl:right-3 @3xl:bottom-auto @3xl:max-h-[calc(100%-1.5rem)] @3xl:w-88 @3xl:rounded-xl @3xl:border-b',\n )}\n initial={still ? false : { opacity: 0, y: 12 }}\n animate={{ opacity: 1, y: 0 }}\n exit={\n still\n ? { opacity: 0, transition: { duration: 0 } }\n : { opacity: 0, y: 8, transition: { duration: 0.12, ease: 'easeIn' } }\n }\n transition={{ duration: 0.2, ease: OUT }}\n >\n {/* 3. Read: what it is */}\n <header className=\"flex shrink-0 items-start gap-3 border-b px-4 py-3\">\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-muted-foreground flex items-center gap-1.5 text-[11px] font-medium\">\n <span\n className=\"size-1.5 rounded-full\"\n style={{ backgroundColor: ink(tone) }}\n />\n {kind}\n </p>\n <h2 className=\"mt-0.5 truncate text-[15px] font-semibold\">{title}</h2>\n </div>\n <button\n type=\"button\"\n aria-label=\"Close\"\n onClick={onClose}\n className=\"text-muted-foreground hover:text-foreground hover:bg-muted -mr-1.5 grid size-8 shrink-0 place-items-center rounded-md transition-colors duration-150 outline-none focus-visible:ring-2 [&_svg]:size-4\"\n >\n <X />\n </button>\n </header>\n\n <div className=\"flex min-h-0 flex-col gap-5 overflow-y-auto overscroll-contain px-4 py-3.5\">\n {facts.length > 0 && (\n <dl className=\"grid grid-cols-[minmax(4.5rem,auto)_1fr] gap-x-4 gap-y-2 text-xs\">\n {facts.map((fact) => (\n <div key={fact.label} className=\"contents\">\n <dt className=\"text-muted-foreground\">{fact.label}</dt>\n <dd className=\"min-w-0 truncate font-mono text-[11.5px]\">\n {fact.value}\n </dd>\n </div>\n ))}\n </dl>\n )}\n\n {rows && (\n <Part title={rows.title}>\n {rows.items.length === 0 ? (\n <p className=\"text-muted-foreground text-xs\">None running.</p>\n ) : (\n <ul className=\"flex flex-col gap-1.5\">\n {rows.items.map((row) => (\n <li\n key={row.label}\n className=\"flex items-center gap-2 text-xs\"\n >\n <span\n className=\"size-1.5 shrink-0 rounded-full\"\n style={{ backgroundColor: ink(row.tone) }}\n />\n <span className=\"min-w-0 flex-1 truncate font-mono text-[11.5px]\">\n {row.label}\n </span>\n <span className=\"text-muted-foreground\">{row.detail}</span>\n </li>\n ))}\n </ul>\n )}\n </Part>\n )}\n\n {/* 3. Read: each value */}\n {sections.map((section) => (\n <Part key={section.title} title={section.title}>\n {section.value !== null && typeof section.value === 'object' ? (\n <div className=\"bg-muted/40 -mx-1 rounded-lg px-2.5 py-2\">\n <JsonTree value={section.value} collapsed={3} tone=\"syntax\" />\n </div>\n ) : (\n <p className=\"font-mono text-xs\">\n {JSON.stringify(section.value) ?? 'nothing'}\n </p>\n )}\n </Part>\n ))}\n </div>\n </motion.section>\n );\n}\n\nfunction Part({\n title,\n className,\n children,\n}: {\n readonly title: string;\n readonly className?: string;\n readonly children: ReactNode;\n}) {\n return (\n <section className={cn('flex flex-col gap-2', className)}>\n <h3 className=\"text-muted-foreground text-[11px] font-medium\">{title}</h3>\n {children}\n </section>\n );\n}\n"],"mappings":"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"}
|
|
@@ -7,7 +7,7 @@ export interface TimelineBranch {
|
|
|
7
7
|
readonly id: string;
|
|
8
8
|
readonly label: string;
|
|
9
9
|
}
|
|
10
|
-
export declare function Timeline({ steps, shown, live, onShow, onLive, branches, branch, onBranch }: {
|
|
10
|
+
export declare function Timeline({ steps, shown, live, onShow: showStep, onLive, head, branches, branch, onBranch, window, onWindow }: {
|
|
11
11
|
/** Every Step on the Branch in view, init first. */
|
|
12
12
|
readonly steps: ReadonlyArray<TimelineStep>;
|
|
13
13
|
/** The place of the Step shown. */
|
|
@@ -16,10 +16,15 @@ export declare function Timeline({ steps, shown, live, onShow, onLive, branches,
|
|
|
16
16
|
readonly live: boolean;
|
|
17
17
|
readonly onShow: (index: number) => void;
|
|
18
18
|
readonly onLive: () => void;
|
|
19
|
+
/** The last Step is the Head: picking it goes live. */
|
|
20
|
+
readonly head?: boolean;
|
|
19
21
|
readonly branches?: ReadonlyArray<TimelineBranch>;
|
|
20
22
|
/** The Branch in view. */
|
|
21
23
|
readonly branch?: string;
|
|
22
24
|
readonly onBranch?: (id: string) => void;
|
|
25
|
+
/** How many entries the History keeps on the Head's Branch, `null` for every one. */
|
|
26
|
+
readonly window?: number | null;
|
|
27
|
+
readonly onWindow?: (window: number | null) => void;
|
|
23
28
|
}): import("react").JSX.Element;
|
|
24
29
|
//#endregion
|
|
25
30
|
//# sourceMappingURL=timeline.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"timeline.d.ts","names":[],"sources":["../../../../src/recipes/kabuki-devtools/timeline/timeline.tsx"],"mappings":";
|
|
1
|
+
{"version":3,"file":"timeline.d.ts","names":[],"sources":["../../../../src/recipes/kabuki-devtools/timeline/timeline.tsx"],"mappings":";iBA0CiB;WACN;WACA;;iBAGM;WACN;WACA;;wBAGK,WACd,OACA,OACA,MACA,QAAQ,UACR,QACA,MACA,UACA,QACA,UACA,QACA;;WAGS,OAAO,cAAc;;WAErB;;WAEA;WACA,SAAS;WACT;;WAEA;WACA,WAAW,cAAc;;WAEzB;WACA,YAAY;;WAEZ;WACA,YAAY;oBACtB,IAAA"}
|