@pihanga2/shadcn 0.2.22 → 0.2.23
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 +22 -1
- package/cards/jsonViewer/jsonViewer.component.js +1 -2
- package/cards/jsonViewer/jsonViewer.component.js.map +1 -1
- package/cards/swimlane/index.d.ts +2 -0
- package/cards/swimlane/index.js +14 -0
- package/cards/swimlane/index.js.map +1 -0
- package/cards/swimlane/swimlane.component.d.ts +4 -0
- package/cards/swimlane/swimlane.component.js +520 -0
- package/cards/swimlane/swimlane.component.js.map +1 -0
- package/cards/swimlane/swimlane.css +1 -0
- package/cards/swimlane/swimlane.demo.d.ts +12 -0
- package/cards/swimlane/swimlane.model.d.ts +41 -0
- package/cards/swimlane/swimlane.model.js +203 -0
- package/cards/swimlane/swimlane.model.js.map +1 -0
- package/cards/swimlane/swimlane.model.test.d.ts +1 -0
- package/cards/swimlane/swimlane.types.d.ts +271 -0
- package/cards/swimlane/swimlane.types.js +7 -0
- package/cards/swimlane/swimlane.types.js.map +1 -0
- package/cards.schema.json +6874 -0
- package/components/ui/drawer.js +24 -24
- package/package.json +8 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swimlane.component.js","names":[],"sources":["../../../src/cards/swimlane/swimlane.component.tsx"],"sourcesContent":["import React, {memo, useEffect, useId, useLayoutEffect, useMemo, useRef, useState} from \"react\";\nimport type {PiCardProps} from \"@pihanga2/core\";\nimport clsx from \"clsx\";\n\nimport {\n DEF_GUTTER_WIDTH,\n DEF_HEADER_HEIGHT,\n DEF_LANE_WIDTH,\n DEF_ROW_HEIGHT,\n type SwimlaneEvents,\n type SwimlaneGhostRow,\n type SwimlaneLanesSnapshot,\n type SwimlaneModel,\n type SwimlaneProps,\n type SwimlaneRow,\n type SwimlaneTarget,\n type SwimlaneTargetKind,\n} from \"./swimlane.types\";\nimport {describeSwimlaneTarget, formatTs, projectPending, relatedCallIds, rowTouches} from \"./swimlane.model\";\n\nimport \"./swimlane.css\";\n\n/**\n * Real-time swimlane (sequence) diagram.\n *\n * Every transition is its own small, fixed-height SVG in normal document flow,\n * so the page (or nearest scroll container) scrolls — the SVGs never do.\n * Each row draws its top half from the `pre` lane snapshot and its bottom half\n * from `post`, which lets lifelines start, activities open/close and run\n * states change in the middle of a row.\n *\n * Hover/click use one delegated listener on the rows container; hit zones are\n * wide transparent shapes carrying `data-*` attributes.\n */\n\ntype Geo = {laneW: number; rowH: number; gutter: number; mid: number; mk: string};\n\nconst laneX = (g: Geo, i: number) => g.gutter + i * g.laneW + g.laneW / 2;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Drawing helpers\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype Parts = {visuals: React.ReactNode[]; hits: React.ReactNode[]};\n\nfunction drawLanes(\n p: Parts,\n g: Geo,\n laneOrder: string[],\n pre: SwimlaneLanesSnapshot,\n post: SwimlaneLanesSnapshot,\n hl: string | null,\n laneLabels: Record<string, string>,\n /** Activity of the process that sends/receives the hovered transition. */\n proc: string | null = null,\n) {\n laneOrder.forEach((l, i) => {\n const x = laneX(g, i);\n const halves: [SwimlaneLanesSnapshot, number, number][] = [\n [pre, 0, g.mid],\n [post, g.mid, g.rowH],\n ];\n halves.forEach(([snap, y1, y2], half) => {\n const s = snap[l];\n if (!s) return;\n p.visuals.push(\n <line key={`ll-${l}-${half}`} className={`swl-ll swl-ll-${s.state}`} x1={x} x2={x} y1={y1} y2={y2} />,\n );\n s.acts.forEach((a, k) => {\n const ax = x - 5 + k * 5;\n // fill + side borders only, so stacked half-row segments read as one\n // bar; caps only where the activity starts / ends inside this row\n const starts = half === 1 && !pre[l]?.acts.includes(a);\n const ends = half === 0 && !post[l]?.acts.includes(a);\n const d =\n `M${ax},${y1} V${y2} M${ax + 10},${y1} V${y2}` +\n (starts ? ` M${ax},${y1} H${ax + 10}` : \"\") +\n (ends ? ` M${ax},${y2} H${ax + 10}` : \"\");\n p.visuals.push(\n <g key={`a-${a}-${half}`} className={clsx(\"swl-act\", hl === a ? \"swl-hl\" : proc === a && \"swl-hl-proc\")}>\n <rect x={ax} y={y1} width={10} height={y2 - y1} />\n <path d={d} />\n </g>,\n );\n p.hits.push(\n <rect\n key={`ha-${a}-${half}`}\n className=\"swl-hit\"\n x={ax - 8}\n y={y1}\n width={26}\n height={y2 - y1}\n data-kind=\"activity\"\n data-id={a}\n data-lane={l}\n />,\n );\n });\n });\n if (!pre[l] && post[l]) {\n p.visuals.push(\n <rect key={`h-${l}`} className=\"swl-lane-head\" x={x - 16} y={g.mid - 5} width={32} height={10} rx={2} />,\n <text key={`ht-${l}`} className=\"swl-lbl-muted\" x={x + 20} y={g.mid + 3}>\n + {laneLabels[l] ?? l} joined\n </text>,\n );\n }\n });\n}\n\nfunction drawArrow(\n p: Parts,\n g: Geo,\n idx: (l: string) => number,\n a: {\n from: string;\n to: string;\n cls: string;\n marker: string;\n label: string;\n hitId: string;\n kind: SwimlaneTargetKind;\n rowId?: number;\n hl: boolean;\n /** Secondary highlight: a transition created/received by the hovered process. */\n rel?: boolean;\n },\n) {\n const i1 = idx(a.from);\n const i2 = idx(a.to);\n if (i1 < 0 || i2 < 0) return;\n const x1 = laneX(g, i1);\n const x2 = laneX(g, i2);\n const cls = clsx(\"swl-ar\", a.cls, a.hl ? \"swl-hl\" : a.rel && \"swl-hl-rel\");\n const markerEnd = `url(#${g.mk}-${a.marker})`;\n const hitData = {\"data-kind\": a.kind, \"data-id\": a.hitId, \"data-row\": a.rowId};\n if (a.from === a.to) {\n const d = `M${x1 + 6},${g.mid - 9} h26 v16 h-20`;\n p.visuals.push(\n <path key=\"ar\" className={cls} d={d} markerEnd={markerEnd} />,\n <text key=\"lb\" className=\"swl-lbl\" x={x1 + 38} y={g.mid + 3}>\n {a.label}\n </text>,\n );\n p.hits.push(<path key=\"hit\" className=\"swl-hit\" d={d} strokeWidth={14} pointerEvents=\"stroke\" {...hitData} />);\n return;\n }\n const dir = Math.sign(x2 - x1);\n const sx = x1 + dir * 6;\n const ex = x2 - dir * 6;\n p.visuals.push(\n <line key=\"ar\" className={cls} x1={sx} x2={ex} y1={g.mid} y2={g.mid} markerEnd={markerEnd} />,\n <text key=\"lb\" className=\"swl-lbl\" x={Math.min(sx, ex) + 8} y={g.mid - 5}>\n {a.label}\n </text>,\n );\n // broad hit zone: a 16px thick invisible band over the thin line\n p.hits.push(\n <line\n key=\"hit\"\n className=\"swl-hit\"\n x1={sx}\n x2={ex}\n y1={g.mid}\n y2={g.mid}\n strokeWidth={16}\n pointerEvents=\"stroke\"\n {...hitData}\n />,\n );\n}\n\nconst CALL_STYLE = {\n sync: [\"swl-ar-sync\", \"sync\"],\n async: [\"swl-ar-async\", \"async\"],\n event: [\"swl-ar-event\", \"event\"],\n} as const;\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Rows\n// ─────────────────────────────────────────────────────────────────────────────\n\ntype RowCommon = {\n g: Geo;\n laneOrder: string[];\n laneLabels: Record<string, string>;\n width: number;\n hl: string | null;\n /** True when this row's transition belongs to a child of the highlighted call. */\n rel?: boolean;\n /** Call id of the process (activity) that sends/receives the hovered transition. */\n proc?: string | null;\n};\n\nconst RealRow = memo(function RealRow({row, g, laneOrder, laneLabels, width, hl, rel, proc}: RowCommon & {row: SwimlaneRow}) {\n const p: Parts = {visuals: [], hits: []};\n const idx = (l: string) => laneOrder.indexOf(l);\n drawLanes(p, g, laneOrder, row.pre, row.post, hl, laneLabels, proc);\n const ev = row.event;\n switch (ev.type) {\n case \"call\": {\n const [cls, marker] = CALL_STYLE[ev.kind];\n drawArrow(p, g, idx, {\n from: ev.from, to: ev.to, cls, marker, label: ev.label,\n hitId: ev.id, kind: \"transition\", rowId: row.id, hl: hl === ev.id, rel,\n });\n break;\n }\n case \"reply\":\n if (row.route) {\n drawArrow(p, g, idx, {\n from: row.route.to, to: row.route.from,\n cls: ev.ok ? \"swl-ar-reply\" : \"swl-ar-error\", marker: ev.ok ? \"reply\" : \"error\",\n label: ev.label, hitId: ev.id, kind: \"transition\", rowId: row.id, hl: hl === ev.id, rel,\n });\n }\n break;\n case \"done\":\n if (row.route) {\n p.visuals.push(\n <text key=\"dn\" className=\"swl-lbl-muted\" x={laneX(g, idx(row.route.to)) + 14} y={g.mid + 3}>\n ✓ processed {row.route.label}\n </text>,\n );\n }\n break;\n case \"state\": {\n const x = laneX(g, idx(ev.lane));\n p.visuals.push(\n <circle key=\"st\" cx={x} cy={g.mid} r={5} className={`swl-dot-${ev.state}`} />,\n <text key=\"stl\" className=\"swl-lbl-muted\" x={x + 10} y={g.mid + 3}>\n → {ev.state}\n </text>,\n );\n p.hits.push(\n <circle\n key=\"sth\"\n className=\"swl-hit\"\n cx={x}\n cy={g.mid}\n r={12}\n data-kind=\"state\"\n data-id={`state-${row.id}`}\n data-lane={ev.lane}\n data-row={row.id}\n />,\n );\n break;\n }\n }\n return (\n <svg className={clsx(\"swl-row\", row.fulfilled && \"swl-arrived\")} width={width} height={g.rowH} data-row={row.id}>\n <text className=\"swl-ts\" x={0} y={g.mid + 3}>\n {formatTs(ev.ts)}\n </text>\n {row.fulfilled && (\n <text className=\"swl-ok\" x={g.gutter - 16} y={g.mid + 4}>\n <title>replaced an outstanding message</title>✓\n </text>\n )}\n {p.visuals}\n {p.hits}\n </svg>\n );\n});\n\nconst GhostRow = memo(function GhostRow({\n row, g, laneOrder, laneLabels, width, hl, rel, proc, now,\n}: RowCommon & {row: SwimlaneGhostRow; now: number}) {\n const p: Parts = {visuals: [], hits: []};\n const pd = row.pending;\n const waited = now - pd.since;\n const overdue = pd.timeoutMs != null && waited > pd.timeoutMs;\n drawLanes(p, g, laneOrder, row.pre, row.post, hl, laneLabels, proc);\n drawArrow(p, g, (l) => laneOrder.indexOf(l), {\n from: pd.from, to: pd.to, cls: clsx(\"swl-ar-ghost\", overdue && \"swl-overdue\"), marker: \"ghost\",\n label: `⌛ ${pd.label}`, hitId: pd.id, kind: \"pending\", hl: hl === pd.id, rel,\n });\n return (\n <svg className=\"swl-row swl-ghost\" width={width} height={g.rowH}>\n <text className={clsx(\"swl-ts\", overdue && \"swl-overdue\")} x={0} y={g.mid + 3}>\n waiting {(waited / 1000).toFixed(1)}s\n </text>\n {p.visuals}\n {p.hits}\n </svg>\n );\n});\n\nconst NowRow = memo(function NowRow({\n snap, count, g, laneOrder, laneLabels, width, proc,\n}: Omit<RowCommon, \"hl\" | \"rel\"> & {snap: SwimlaneLanesSnapshot; count: number}) {\n const p: Parts = {visuals: [], hits: []};\n drawLanes(p, g, laneOrder, snap, snap, null, laneLabels, proc);\n return (\n <svg className=\"swl-row swl-now\" width={width} height={g.rowH}>\n <text className=\"swl-now-lbl\" x={0} y={g.mid + 3}>now</text>\n {p.visuals}\n <line className=\"swl-now-line\" x1={g.gutter - 6} x2={width - 10} y1={g.mid} y2={g.mid} />\n <text className=\"swl-now-lbl\" x={width - 136} y={g.mid - 4}>▼ {count} outstanding</text>\n </svg>\n );\n});\n\nfunction PendingZone(\n props: Omit<RowCommon, \"hl\" | \"rel\" | \"proc\"> & {\n model: SwimlaneModel;\n hid: string | null;\n related: ReadonlySet<string>;\n procId: string | null;\n clock?: number;\n },\n) {\n const {model, hid, related, procId, clock, ...common} = props;\n const [tick, setNow] = useState(() => Date.now());\n const now = clock ?? tick;\n const any = model.pending.length > 0;\n useEffect(() => {\n // only the ghost rows tick (for the \"waiting\" timer); a `now` prop disables the ticker\n if (!any || clock != null) return;\n const iv = setInterval(() => setNow(Date.now()), 250);\n return () => clearInterval(iv);\n }, [any, clock]);\n const {current, pending} = model;\n const ghosts = useMemo(() => projectPending(current, pending), [current, pending]);\n return (\n <>\n <NowRow\n snap={model.current}\n count={model.pending.length}\n proc={procId && rowTouches({pre: model.current, post: model.current}, undefined, procId) ? procId : null}\n {...common}\n />\n {ghosts.map((gr) => (\n <GhostRow\n key={gr.id}\n row={gr}\n now={now}\n hl={hid && rowTouches(gr, gr.pending.id, hid) ? hid : null}\n rel={related.has(gr.pending.id)}\n proc={procId && rowTouches(gr, undefined, procId) ? procId : null}\n {...common}\n />\n ))}\n </>\n );\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Header\n// ─────────────────────────────────────────────────────────────────────────────\n\nfunction Header({model, g, headH, width}: {model: SwimlaneModel; g: Geo; headH: number; width: number}) {\n return (\n <svg width={width} height={headH}>\n {model.laneOrder.map((l, i) => {\n const x = laneX(g, i);\n const s = model.current[l];\n const y = headH - 24;\n return (\n <g key={l}>\n <text className=\"swl-lane-name\" x={x + 4} y={y} transform={`rotate(-90 ${x + 4} ${y})`}>\n {model.laneLabels[l] ?? l}\n </text>\n <circle cx={x} cy={headH - 12} r={5} className={`swl-dot-${s?.state}`}>\n <title>{s?.state}</title>\n </circle>\n {s && s.acts.length > 0 && (\n <text className=\"swl-lbl-muted\" x={x + 9} y={headH - 8}>\n {s.acts.length}\n </text>\n )}\n </g>\n );\n })}\n </svg>\n );\n}\n\nfunction Defs({mk}: {mk: string}) {\n const m = (id: string, cls: string, open: boolean) => (\n <marker\n id={`${mk}-${id}`}\n viewBox=\"0 0 10 10\"\n refX={9}\n refY={5}\n markerWidth={9}\n markerHeight={9}\n markerUnits=\"userSpaceOnUse\"\n orient=\"auto-start-reverse\"\n >\n {open ? <path d=\"M1,1 L9,5 L1,9\" className={cls} /> : <path d=\"M0,0 L10,5 L0,10 z\" className={cls} />}\n </marker>\n );\n return (\n <svg width={0} height={0} style={{position: \"absolute\"}} aria-hidden=\"true\">\n <defs>\n {m(\"sync\", \"swl-mk-sync\", false)}\n {m(\"async\", \"swl-mk-async\", true)}\n {m(\"event\", \"swl-mk-event\", true)}\n {m(\"reply\", \"swl-mk-reply\", true)}\n {m(\"error\", \"swl-mk-error\", false)}\n {m(\"ghost\", \"swl-mk-ghost\", true)}\n </defs>\n </svg>\n );\n}\n\n/**\n * For a hovered transition (call / reply arrow, or outstanding reply): the\n * call id of the caller-side activity that sent it / receives the reply.\n */\nfunction processOf(m: Pick<SwimlaneModel, \"spans\">, t: SwimlaneTarget): string | null {\n if (t.kind !== \"transition\" && t.kind !== \"pending\") return null;\n return m.spans[t.id]?.parent ?? null;\n}\n\nfunction scrollParent(el: HTMLElement | null): HTMLElement | null {\n for (let e = el?.parentElement; e; e = e.parentElement) {\n const oy = getComputedStyle(e).overflowY;\n if (oy === \"auto\" || oy === \"scroll\") return e;\n }\n return null; // → window\n}\n\n// ─────────────────────────────────────────────────────────────────────────────\n// Card\n// ─────────────────────────────────────────────────────────────────────────────\n\nexport const SwimlaneComponent = (props: PiCardProps<SwimlaneProps, SwimlaneEvents>): React.ReactNode => {\n const {\n cardName,\n model,\n showPending = true,\n autoFollow = true,\n showTooltip = true,\n highlightId,\n laneWidth = DEF_LANE_WIDTH,\n rowHeight = DEF_ROW_HEIGHT,\n headerHeight = DEF_HEADER_HEIGHT,\n gutterWidth = DEF_GUTTER_WIDTH,\n stickyTop = 0,\n now: clock,\n className,\n onHoverChanged,\n onItemClicked,\n } = props;\n\n const mk = \"swl\" + useId().replace(/[^a-zA-Z0-9_-]/g, \"\");\n const g = useMemo<Geo>(\n () => ({laneW: laneWidth, rowH: rowHeight, gutter: gutterWidth, mid: rowHeight / 2, mk}),\n [laneWidth, rowHeight, gutterWidth, mk],\n );\n const width = gutterWidth + model.laneOrder.length * laneWidth + 140; // + room for labels\n const common = {g, laneOrder: model.laneOrder, laneLabels: model.laneLabels, width};\n\n const rootRef = useRef<HTMLDivElement>(null);\n const tipRef = useRef<HTMLDivElement>(null);\n const modelRef = useRef(model);\n modelRef.current = model;\n\n // ── hover / click (event delegation) ─────────────────────────────────────\n const [hovered, setHovered] = useState<SwimlaneTarget | null>(null);\n const hoveredRef = useRef<SwimlaneTarget | null>(null);\n\n const targetOf = (el: Element | null): SwimlaneTarget | null => {\n const hit = (el as HTMLElement | null)?.closest?.(\".swl-hit\") as SVGElement | null;\n if (!hit) return null;\n const d = hit.dataset;\n return {\n kind: d.kind as SwimlaneTargetKind,\n id: d.id!,\n lane: d.lane,\n rowId: d.row != null ? Number(d.row) : undefined,\n };\n };\n const same = (a: SwimlaneTarget | null, b: SwimlaneTarget | null) =>\n a === b || (!!a && !!b && a.kind === b.kind && a.id === b.id);\n\n const details = (t: SwimlaneTarget) => {\n const m = modelRef.current;\n const related = t.kind === \"state\" ? undefined : relatedCallIds(m, t.id);\n const process = processOf(m, t) ?? undefined;\n return t.kind === \"pending\"\n ? {pending: m.pending.find((p) => p.id === t.id), span: m.spans[t.id], related, process}\n : {span: m.spans[t.id], related, process};\n };\n\n const placeTip = (x: number, y: number) => {\n const tip = tipRef.current;\n if (tip) {\n tip.style.left = `${x + 14}px`;\n tip.style.top = `${y + 14}px`;\n }\n };\n\n const changeHover = (next: SwimlaneTarget | null, e: React.PointerEvent) => {\n const prev = hoveredRef.current;\n if (same(prev, next)) return; // e.g. same activity continuing in the next row\n hoveredRef.current = next;\n setHovered(next);\n const tip = tipRef.current;\n if (tip) {\n if (next && showTooltip) {\n tip.textContent = describeSwimlaneTarget(modelRef.current, next, clock ?? Date.now());\n tip.style.display = \"block\";\n placeTip(e.clientX, e.clientY);\n } else {\n tip.style.display = \"none\";\n }\n }\n onHoverChanged({\n target: next,\n previous: prev,\n ...(next ? details(next) : {}),\n clientX: e.clientX,\n clientY: e.clientY,\n });\n };\n\n const onOver = (e: React.PointerEvent) => {\n const t = targetOf(e.target as Element);\n if (t) changeHover(t, e);\n };\n const onOut = (e: React.PointerEvent) => {\n const t = targetOf(e.relatedTarget as Element | null);\n if (!t || !same(t, hoveredRef.current)) changeHover(t, e);\n };\n const onMove = (e: React.PointerEvent) => {\n if (hoveredRef.current && showTooltip) placeTip(e.clientX, e.clientY);\n };\n const onClick = (e: React.MouseEvent) => {\n const t = targetOf(e.target as Element);\n if (t) onItemClicked({target: t, ...details(t)});\n };\n\n // ── highlighting: only rows touching the chain get a new `hl` prop ───────\n const hid =\n hovered && hovered.kind !== \"state\" ? hovered.id : highlightId ?? null;\n // children of the highlighted call: transitions the process creates/receives\n const {spans} = model;\n const related = useMemo<ReadonlySet<string>>(\n () => new Set(hid ? relatedCallIds({spans}, hid) : []),\n [hid, spans],\n );\n // hovering a transition: also mark the process on the caller side that sent\n // the call / receives the reply (the parent activity) — only that activity\n const procId = hovered ? processOf(model, hovered) : null;\n\n // ── auto-follow (page or nearest scroll container) ───────────────────────\n const follow = useRef(true);\n useEffect(() => {\n if (!autoFollow) return;\n const sp = scrollParent(rootRef.current);\n const target: HTMLElement | Window = sp ?? window;\n const onScroll = () => {\n follow.current = sp\n ? sp.scrollTop + sp.clientHeight >= sp.scrollHeight - 40\n : window.innerHeight + window.scrollY >= document.documentElement.scrollHeight - 40;\n };\n target.addEventListener(\"scroll\", onScroll, {passive: true});\n return () => target.removeEventListener(\"scroll\", onScroll);\n }, [autoFollow]);\n useLayoutEffect(() => {\n if (!autoFollow || !follow.current) return;\n const sp = scrollParent(rootRef.current);\n if (sp) sp.scrollTop = sp.scrollHeight;\n else window.scrollTo(0, document.documentElement.scrollHeight);\n }, [autoFollow, model.rows.length, model.pending.length]);\n\n return (\n <div ref={rootRef} className={clsx(\"pihanga-swimlane\", className)} data-pihanga={cardName}>\n <Defs mk={mk} />\n <div className=\"swl-header\" style={{top: stickyTop}}>\n <Header model={model} g={g} headH={headerHeight} width={width} />\n </div>\n <div className=\"swl-rows\" onPointerOver={onOver} onPointerOut={onOut} onPointerMove={onMove} onClick={onClick}>\n {model.rows.map((r) => (\n <RealRow\n key={r.id}\n row={r}\n hl={hid && rowTouches(r, \"id\" in r.event ? r.event.id : undefined, hid) ? hid : null}\n rel={\"id\" in r.event && r.event.type !== \"done\" && related.has(r.event.id)}\n proc={procId && rowTouches(r, undefined, procId) ? procId : null}\n {...common}\n />\n ))}\n {showPending && <PendingZone model={model} hid={hid} related={related} procId={procId} clock={clock} {...common} />}\n </div>\n <div ref={tipRef} className=\"swl-tooltip\" role=\"tooltip\" />\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root{--swl-sync:oklch(55% .2 260);--swl-async:oklch(55% .22 295);--swl-event:oklch(60% .11 185);--swl-act-fill:oklch(93% .04 255);--swl-hl:oklch(77% .16 70);--swl-starting:oklch(67% .16 60);--swl-degraded:oklch(65% .19 45);--swl-running:oklch(60% .15 150)}.dark{--swl-sync:oklch(72% .14 260);--swl-async:oklch(74% .14 295);--swl-event:oklch(78% .11 185);--swl-act-fill:oklch(35% .07 255);--swl-hl:oklch(80% .15 75);--swl-starting:oklch(75% .15 65);--swl-degraded:oklch(72% .17 45);--swl-running:oklch(75% .15 150)}.pihanga-swimlane{width:max-content;min-width:100%;color:var(--foreground);font-size:13px}.pihanga-swimlane .swl-header{z-index:2;background:var(--background);border-bottom:1px solid var(--border);position:sticky}.pihanga-swimlane svg{display:block;overflow:visible}.pihanga-swimlane svg text{fill:var(--foreground)}.pihanga-swimlane .swl-lane-name{font-size:12px;font-weight:600}.pihanga-swimlane .swl-ts{fill:var(--muted-foreground);font:10px ui-monospace,monospace}.pihanga-swimlane .swl-lbl{font-size:11px}.pihanga-swimlane .swl-lbl-muted{fill:var(--muted-foreground);font-size:10px}.pihanga-swimlane .swl-ll{stroke-width:1.5px}.pihanga-swimlane .swl-ll-created,.pihanga-swimlane .swl-ll-running{stroke:color-mix(in srgb, var(--muted-foreground) 70%, transparent)}.pihanga-swimlane .swl-ll-starting{stroke:var(--swl-starting);stroke-dasharray:4 3}.pihanga-swimlane .swl-ll-degraded{stroke:var(--swl-degraded);stroke-width:3px}.pihanga-swimlane .swl-ll-stopped{stroke:var(--border);stroke-dasharray:1 4}.pihanga-swimlane .swl-dot-created,.pihanga-swimlane .swl-dot-starting{fill:var(--swl-starting)}.pihanga-swimlane .swl-dot-running{fill:var(--swl-running)}.pihanga-swimlane .swl-dot-degraded{fill:var(--swl-degraded)}.pihanga-swimlane .swl-dot-stopped{fill:var(--border)}.pihanga-swimlane .swl-lane-head{fill:var(--background);stroke:var(--muted-foreground)}.pihanga-swimlane .swl-act rect{fill:var(--swl-act-fill);stroke:none}.pihanga-swimlane .swl-act path{fill:none;stroke:var(--swl-sync);stroke-width:1px}.pihanga-swimlane .swl-act.swl-hl rect{fill:var(--swl-hl)}.pihanga-swimlane .swl-act.swl-hl path{stroke:var(--swl-hl)}.pihanga-swimlane .swl-act.swl-hl-proc rect{fill:color-mix(in srgb, var(--swl-hl) 45%, var(--swl-act-fill))}.pihanga-swimlane .swl-act.swl-hl-proc path{stroke:var(--swl-hl);stroke-width:2px}.pihanga-swimlane .swl-ar{fill:none;stroke-width:1.5px}.pihanga-swimlane .swl-ar-sync{stroke:var(--swl-sync)}.pihanga-swimlane .swl-ar-async{stroke:var(--swl-async)}.pihanga-swimlane .swl-ar-event{stroke:var(--swl-event);stroke-dasharray:2 3}.pihanga-swimlane .swl-ar-reply{stroke:var(--muted-foreground);stroke-dasharray:5 3}.pihanga-swimlane .swl-ar-error{stroke:var(--destructive);stroke-dasharray:5 3}.pihanga-swimlane .swl-ar-ghost{stroke:var(--muted-foreground);stroke-dasharray:7 4}.pihanga-swimlane .swl-ar-ghost.swl-overdue{stroke:var(--destructive)}.pihanga-swimlane .swl-ar.swl-hl{stroke:var(--swl-hl);stroke-width:3px}.pihanga-swimlane .swl-ar.swl-hl-rel{stroke:var(--swl-hl);stroke-width:2px}.pihanga-swimlane .swl-mk-sync{fill:var(--swl-sync)}.pihanga-swimlane .swl-mk-async{fill:none;stroke:var(--swl-async);stroke-width:1.5px}.pihanga-swimlane .swl-mk-event{fill:none;stroke:var(--swl-event);stroke-width:1.5px}.pihanga-swimlane .swl-mk-reply,.pihanga-swimlane .swl-mk-ghost{fill:none;stroke:var(--muted-foreground);stroke-width:1.5px}.pihanga-swimlane .swl-mk-error{fill:var(--destructive)}.pihanga-swimlane .swl-ghost{opacity:.55}.pihanga-swimlane .swl-ts.swl-overdue{fill:var(--destructive);font-weight:600}.pihanga-swimlane .swl-now-line{stroke:var(--swl-hl);stroke-width:1.5px;stroke-dasharray:10 4}.pihanga-swimlane text.swl-now-lbl{fill:var(--swl-hl);font-size:11px;font-weight:600}.pihanga-swimlane text.swl-ok{fill:var(--swl-running);font-size:12px;font-weight:700}.pihanga-swimlane .swl-arrived{animation:1.6s ease-out swl-arrive}@keyframes swl-arrive{0%{background:color-mix(in srgb, var(--swl-hl) 45%, transparent)}to{background:0 0}}.pihanga-swimlane .swl-hit{fill:#0000;stroke:#0000;cursor:pointer}.pihanga-swimlane.swl-debug-hits .swl-hit{fill:color-mix(in srgb, var(--destructive) 10%, transparent);stroke:color-mix(in srgb, var(--destructive) 20%, transparent)}.pihanga-swimlane .swl-tooltip{z-index:50;pointer-events:none;border-radius:calc(var(--radius) - 2px);border:1px solid var(--border);background:var(--popover);max-width:340px;color:var(--popover-foreground);box-shadow:0 4px 12px color-mix(in srgb, var(--foreground) 15%, transparent);padding:6px 8px;font-size:12px;display:none;position:fixed}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SwimlaneEvent, SwimlaneModel } from './swimlane.types';
|
|
2
|
+
/**
|
|
3
|
+
* A checkout flow through a small microservice deployment, stopped while
|
|
4
|
+
* several replies and one announced callback are still outstanding.
|
|
5
|
+
*/
|
|
6
|
+
export declare function demoEvents(t0: number, opts?: {
|
|
7
|
+
complete?: boolean;
|
|
8
|
+
}): SwimlaneEvent[];
|
|
9
|
+
export declare function buildDemoModel(opts?: {
|
|
10
|
+
complete?: boolean;
|
|
11
|
+
t0?: number;
|
|
12
|
+
}): SwimlaneModel;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { PiRegister, ReduxState } from '@pihanga2/core';
|
|
2
|
+
import { SwimlaneEvent, SwimlaneGhostRow, SwimlaneLanesSnapshot, SwimlaneModel, SwimlanePending, SwimlaneTarget } from './swimlane.types';
|
|
3
|
+
export declare function createSwimlaneModel(opts?: {
|
|
4
|
+
maxRows?: number;
|
|
5
|
+
}): SwimlaneModel;
|
|
6
|
+
/**
|
|
7
|
+
* Apply a batch of stream events to the model (mutating it).
|
|
8
|
+
* Safe to call on an Immer draft or on a plain object.
|
|
9
|
+
*/
|
|
10
|
+
export declare function ingestSwimlaneEvents(m: SwimlaneModel, events: SwimlaneEvent[], now?: number): void;
|
|
11
|
+
/** Remove all rows but keep lanes, open activities and outstanding messages. */
|
|
12
|
+
export declare function clearSwimlaneRows(m: SwimlaneModel): void;
|
|
13
|
+
/**
|
|
14
|
+
* Project outstanding messages below the "now" line (pure).
|
|
15
|
+
* Expected replies are replayed onto the current snapshot so that open
|
|
16
|
+
* activities visibly run down to the ghost reply that will close them.
|
|
17
|
+
* Newest first ≈ innermost call replies first.
|
|
18
|
+
*/
|
|
19
|
+
export declare function projectPending(current: SwimlaneLanesSnapshot, pending: SwimlanePending[]): SwimlaneGhostRow[];
|
|
20
|
+
/**
|
|
21
|
+
* Direct children of call `id`: calls made while it was being handled.
|
|
22
|
+
* Their call/reply transitions (and outstanding replies) are the transitions
|
|
23
|
+
* the process "creates and receives".
|
|
24
|
+
*/
|
|
25
|
+
export declare function relatedCallIds(m: Pick<SwimlaneModel, "spans">, id: string): string[];
|
|
26
|
+
/** Does a row (or ghost row) involve the call chain `id`? */
|
|
27
|
+
export declare function rowTouches(r: {
|
|
28
|
+
pre: SwimlaneLanesSnapshot;
|
|
29
|
+
post: SwimlaneLanesSnapshot;
|
|
30
|
+
}, eventId: string | undefined, id: string): boolean;
|
|
31
|
+
export declare function formatTs(t: number): string;
|
|
32
|
+
/** Human-readable description of a hover target (used by the tooltip). */
|
|
33
|
+
export declare function describeSwimlaneTarget(m: SwimlaneModel, t: SwimlaneTarget, now?: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Wire the data actions for one model:
|
|
36
|
+
*
|
|
37
|
+
* register((r) => registerSwimlaneModel<AppState>(r, "main", (s) => s.swimlane));
|
|
38
|
+
* // …later, from a stream adapter:
|
|
39
|
+
* dispatchSwimlaneEvents(dispatch, {modelId: "main", events});
|
|
40
|
+
*/
|
|
41
|
+
export declare function registerSwimlaneModel<S extends ReduxState>(r: PiRegister, modelId: string, select: (state: S) => SwimlaneModel): void;
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { onSwimlaneEventsReceived as e, onSwimlaneRowsCleared as t } from "./swimlane.types.js";
|
|
2
|
+
//#region src/cards/swimlane/swimlane.model.ts
|
|
3
|
+
function n(e = {}) {
|
|
4
|
+
return {
|
|
5
|
+
laneOrder: [],
|
|
6
|
+
laneLabels: {},
|
|
7
|
+
current: {},
|
|
8
|
+
rows: [],
|
|
9
|
+
pending: [],
|
|
10
|
+
spans: {},
|
|
11
|
+
seq: 0,
|
|
12
|
+
maxRows: e.maxRows ?? 2e3
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
function r(e) {
|
|
16
|
+
let t = {};
|
|
17
|
+
for (let n of Object.keys(e)) t[n] = {
|
|
18
|
+
state: e[n].state,
|
|
19
|
+
acts: [...e[n].acts]
|
|
20
|
+
};
|
|
21
|
+
return t;
|
|
22
|
+
}
|
|
23
|
+
function i(e, t) {
|
|
24
|
+
switch (t.type) {
|
|
25
|
+
case "state": return [t.lane];
|
|
26
|
+
case "call": return t.from === t.to ? [t.from] : [t.from, t.to];
|
|
27
|
+
case "expect": return t.from === t.to ? [t.from] : [t.from, t.to];
|
|
28
|
+
case "reply":
|
|
29
|
+
case "done": {
|
|
30
|
+
let n = e.spans[t.id];
|
|
31
|
+
return n ? [n.to, n.from] : [];
|
|
32
|
+
}
|
|
33
|
+
default: return [];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function a(e, t, n = Date.now()) {
|
|
37
|
+
for (let r of t) {
|
|
38
|
+
let t = r.ts ?? n;
|
|
39
|
+
for (let n of i(e, r)) e.current[n] || o(e, {
|
|
40
|
+
type: "lane",
|
|
41
|
+
lane: n
|
|
42
|
+
}, t);
|
|
43
|
+
o(e, r, t);
|
|
44
|
+
}
|
|
45
|
+
s(e);
|
|
46
|
+
}
|
|
47
|
+
function o(e, t, n) {
|
|
48
|
+
let i = e.current;
|
|
49
|
+
if (t.type === "expect") {
|
|
50
|
+
e.pending.push({
|
|
51
|
+
id: t.id,
|
|
52
|
+
type: "expect",
|
|
53
|
+
from: t.from,
|
|
54
|
+
to: t.to,
|
|
55
|
+
label: t.label,
|
|
56
|
+
since: n,
|
|
57
|
+
timeoutMs: t.timeoutMs
|
|
58
|
+
});
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
let a = r(i), o;
|
|
62
|
+
switch (t.type) {
|
|
63
|
+
case "lane":
|
|
64
|
+
if (t.label && (e.laneLabels[t.lane] = t.label), i[t.lane]) return;
|
|
65
|
+
i[t.lane] = {
|
|
66
|
+
state: "created",
|
|
67
|
+
acts: []
|
|
68
|
+
}, e.laneOrder.push(t.lane);
|
|
69
|
+
break;
|
|
70
|
+
case "state":
|
|
71
|
+
i[t.lane].state = t.state;
|
|
72
|
+
break;
|
|
73
|
+
case "call": {
|
|
74
|
+
let r = i[t.from]?.acts ?? [], a = t.parentId ?? r[r.length - 1];
|
|
75
|
+
e.spans[t.id] = {
|
|
76
|
+
id: t.id,
|
|
77
|
+
kind: t.kind,
|
|
78
|
+
from: t.from,
|
|
79
|
+
to: t.to,
|
|
80
|
+
label: t.label,
|
|
81
|
+
start: n,
|
|
82
|
+
end: t.kind === "event" ? n : void 0,
|
|
83
|
+
status: t.kind === "event" ? "done" : "open"
|
|
84
|
+
}, a && a !== t.id && (e.spans[t.id].parent = a), t.kind !== "event" && i[t.to].acts.push(t.id);
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
case "reply":
|
|
88
|
+
case "done": {
|
|
89
|
+
let r = e.spans[t.id];
|
|
90
|
+
if (!r) return;
|
|
91
|
+
r.end = n, r.status = t.type === "done" ? "done" : t.ok ? "ok" : "error", t.type === "reply" && (r.result = t.label);
|
|
92
|
+
let a = i[r.to];
|
|
93
|
+
if (a) {
|
|
94
|
+
let e = a.acts.indexOf(t.id);
|
|
95
|
+
e >= 0 && a.acts.splice(e, 1);
|
|
96
|
+
}
|
|
97
|
+
o = {
|
|
98
|
+
from: r.from,
|
|
99
|
+
to: r.to,
|
|
100
|
+
label: r.label
|
|
101
|
+
};
|
|
102
|
+
break;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
let s, c = t.type === "reply" ? t.id : t.fulfils;
|
|
106
|
+
if (c) {
|
|
107
|
+
let t = e.pending.findIndex((e) => e.id === c);
|
|
108
|
+
t >= 0 && (e.pending.splice(t, 1), s = c);
|
|
109
|
+
}
|
|
110
|
+
t.type === "call" && t.kind === "sync" && e.pending.push({
|
|
111
|
+
id: t.id,
|
|
112
|
+
type: "reply",
|
|
113
|
+
from: t.to,
|
|
114
|
+
to: t.from,
|
|
115
|
+
label: `reply: ${t.label}`,
|
|
116
|
+
since: n,
|
|
117
|
+
timeoutMs: t.timeoutMs
|
|
118
|
+
});
|
|
119
|
+
let l = {
|
|
120
|
+
id: ++e.seq,
|
|
121
|
+
event: {
|
|
122
|
+
...t,
|
|
123
|
+
ts: n
|
|
124
|
+
},
|
|
125
|
+
pre: a,
|
|
126
|
+
post: r(i)
|
|
127
|
+
};
|
|
128
|
+
s && (l.fulfilled = s), o && (l.route = o), e.rows.push(l);
|
|
129
|
+
}
|
|
130
|
+
function s(e) {
|
|
131
|
+
let t = Math.max(50, Math.floor(e.maxRows / 10));
|
|
132
|
+
if (e.rows.length <= e.maxRows + t) return;
|
|
133
|
+
e.rows.splice(0, e.rows.length - e.maxRows);
|
|
134
|
+
let n = /* @__PURE__ */ new Set();
|
|
135
|
+
for (let t of e.rows) "id" in t.event && n.add(t.event.id);
|
|
136
|
+
for (let t of Object.values(e.current)) t.acts.forEach((e) => n.add(e));
|
|
137
|
+
e.pending.forEach((e) => n.add(e.id));
|
|
138
|
+
for (let t of Object.keys(e.spans)) n.has(t) || delete e.spans[t];
|
|
139
|
+
}
|
|
140
|
+
function c(e) {
|
|
141
|
+
e.rows.splice(0, e.rows.length);
|
|
142
|
+
}
|
|
143
|
+
function l(e, t) {
|
|
144
|
+
let n = e, r = [];
|
|
145
|
+
for (let e = t.length - 1; e >= 0; e--) {
|
|
146
|
+
let i = t[e], a = n, o = n[i.from];
|
|
147
|
+
i.type === "reply" && o?.acts.includes(i.id) && (n = {
|
|
148
|
+
...n,
|
|
149
|
+
[i.from]: {
|
|
150
|
+
...o,
|
|
151
|
+
acts: o.acts.filter((e) => e !== i.id)
|
|
152
|
+
}
|
|
153
|
+
}), r.push({
|
|
154
|
+
id: `g-${i.id}`,
|
|
155
|
+
pending: i,
|
|
156
|
+
pre: a,
|
|
157
|
+
post: n
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
return r;
|
|
161
|
+
}
|
|
162
|
+
function u(e, t) {
|
|
163
|
+
let n = [];
|
|
164
|
+
for (let r of Object.values(e.spans)) r.parent === t && n.push(r.id);
|
|
165
|
+
return n.sort((t, n) => e.spans[t].start - e.spans[n].start);
|
|
166
|
+
}
|
|
167
|
+
function d(e, t, n) {
|
|
168
|
+
if (t === n) return !0;
|
|
169
|
+
for (let t of Object.values(e.pre)) if (t.acts.includes(n)) return !0;
|
|
170
|
+
for (let t of Object.values(e.post)) if (t.acts.includes(n)) return !0;
|
|
171
|
+
return !1;
|
|
172
|
+
}
|
|
173
|
+
function f(e) {
|
|
174
|
+
let t = new Date(e), n = (e, t = 2) => String(e).padStart(t, "0");
|
|
175
|
+
return `${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}.${n(t.getMilliseconds(), 3)}`;
|
|
176
|
+
}
|
|
177
|
+
function p(e, t, n = Date.now()) {
|
|
178
|
+
if (t.kind === "state") {
|
|
179
|
+
let n = e.rows.find((e) => e.id === t.rowId);
|
|
180
|
+
return !n || n.event.type !== "state" ? t.id : `${t.lane} → ${n.event.state} at ${f(n.event.ts)}`;
|
|
181
|
+
}
|
|
182
|
+
if (t.kind === "pending") {
|
|
183
|
+
let r = e.pending.find((e) => e.id === t.id);
|
|
184
|
+
if (!r) return "already fulfilled";
|
|
185
|
+
let i = n - r.since, a = r.type === "reply" ? "reply" : "message", o = r.timeoutMs ? ` / timeout ${r.timeoutMs} ms${i > r.timeoutMs ? " · OVERDUE" : ""}` : "";
|
|
186
|
+
return `expected ${a} ${r.from} → ${r.to}: "${r.label}" · waiting ${i} ms${o}`;
|
|
187
|
+
}
|
|
188
|
+
let r = e.spans[t.id];
|
|
189
|
+
if (!r) return t.id;
|
|
190
|
+
let i = r.end == null ? `running ${n - r.start} ms` : `${r.end - r.start} ms`, a = r.result ? ` (${r.result})` : "";
|
|
191
|
+
return t.kind === "activity" ? `${r.to} handling "${r.label}" from ${r.from} · started ${f(r.start)} · ${i}${a}` : `${r.kind.toUpperCase()} ${r.from} → ${r.to}: "${r.label}" · ${r.status}${a} · ${i}`;
|
|
192
|
+
}
|
|
193
|
+
function m(n, r, i) {
|
|
194
|
+
e(n, (e, t) => {
|
|
195
|
+
t.modelId === r && a(i(e), t.events);
|
|
196
|
+
}), t(n, (e, t) => {
|
|
197
|
+
t.modelId === r && c(i(e));
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
//#endregion
|
|
201
|
+
export { c as clearSwimlaneRows, n as createSwimlaneModel, p as describeSwimlaneTarget, f as formatTs, a as ingestSwimlaneEvents, l as projectPending, m as registerSwimlaneModel, u as relatedCallIds, d as rowTouches };
|
|
202
|
+
|
|
203
|
+
//# sourceMappingURL=swimlane.model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"swimlane.model.js","names":[],"sources":["../../../src/cards/swimlane/swimlane.model.ts"],"sourcesContent":["/**\n * Swimlane model helpers — pure TypeScript, no React.\n *\n * The model is plain JSON that lives in the app's Redux state.\n * `ingestSwimlaneEvents` MUTATES the model it is given, which is exactly what\n * a Pihanga (Immer) reducer wants:\n *\n * onSwimlaneEventsReceived<AppState>(r, (state, {events}) => {\n * ingestSwimlaneEvents(state.swimlane, events);\n * });\n *\n * or simply `registerSwimlaneModel(r, \"main\", (s) => s.swimlane)`.\n *\n * Every row stores its own plain copies of the lane snapshots, so no object is\n * ever referenced from two places in the state tree (Immer-safe, serialisable).\n */\nimport type {PiRegister, ReduxState} from \"@pihanga2/core\";\n\nimport {\n DEF_MAX_ROWS,\n onSwimlaneEventsReceived,\n onSwimlaneRowsCleared,\n type SwimlaneEvent,\n type SwimlaneGhostRow,\n type SwimlaneLanesSnapshot,\n type SwimlaneModel,\n type SwimlanePending,\n type SwimlaneRow,\n type SwimlaneRowEvent,\n type SwimlaneTarget,\n} from \"./swimlane.types\";\n\nexport function createSwimlaneModel(\n opts: {maxRows?: number} = {},\n): SwimlaneModel {\n return {\n laneOrder: [],\n laneLabels: {},\n current: {},\n rows: [],\n pending: [],\n spans: {},\n seq: 0,\n maxRows: opts.maxRows ?? DEF_MAX_ROWS,\n };\n}\n\nfunction copySnapshot(s: SwimlaneLanesSnapshot): SwimlaneLanesSnapshot {\n const out: SwimlaneLanesSnapshot = {};\n for (const k of Object.keys(s)) {\n out[k] = {state: s[k].state, acts: [...s[k].acts]};\n }\n return out;\n}\n\n/** Lanes an event refers to (used to add lanes implicitly). */\nfunction referencedLanes(m: SwimlaneModel, ev: SwimlaneEvent): string[] {\n switch (ev.type) {\n case \"state\":\n return [ev.lane];\n case \"call\":\n return ev.from === ev.to ? [ev.from] : [ev.from, ev.to];\n case \"expect\":\n return ev.from === ev.to ? [ev.from] : [ev.from, ev.to];\n case \"reply\":\n case \"done\": {\n const s = m.spans[ev.id];\n return s ? [s.to, s.from] : [];\n }\n default:\n return [];\n }\n}\n\n/**\n * Apply a batch of stream events to the model (mutating it).\n * Safe to call on an Immer draft or on a plain object.\n */\nexport function ingestSwimlaneEvents(\n m: SwimlaneModel,\n events: SwimlaneEvent[],\n now: number = Date.now(),\n): void {\n for (const raw of events) {\n const ts = raw.ts ?? now;\n // lanes referenced before they were announced get an implicit lane row\n for (const lane of referencedLanes(m, raw)) {\n if (!m.current[lane]) applyOne(m, {type: \"lane\", lane}, ts);\n }\n applyOne(m, raw, ts);\n }\n trim(m);\n}\n\nfunction applyOne(m: SwimlaneModel, ev: SwimlaneEvent, ts: number): void {\n const cur = m.current;\n\n if (ev.type === \"expect\") {\n m.pending.push({\n id: ev.id,\n type: \"expect\",\n from: ev.from,\n to: ev.to,\n label: ev.label,\n since: ts,\n timeoutMs: ev.timeoutMs,\n });\n return;\n }\n\n const pre = copySnapshot(cur);\n let route: SwimlaneRow[\"route\"];\n\n switch (ev.type) {\n case \"lane\":\n if (ev.label) m.laneLabels[ev.lane] = ev.label;\n if (cur[ev.lane]) return; // already known → only a label update\n cur[ev.lane] = {state: \"created\", acts: []};\n m.laneOrder.push(ev.lane);\n break;\n case \"state\":\n cur[ev.lane].state = ev.state;\n break;\n case \"call\": {\n // causality: explicit parentId, else the innermost open activity of the caller\n const callerActs = cur[ev.from]?.acts ?? [];\n const parent = ev.parentId ?? callerActs[callerActs.length - 1];\n m.spans[ev.id] = {\n id: ev.id,\n kind: ev.kind,\n from: ev.from,\n to: ev.to,\n label: ev.label,\n start: ts,\n end: ev.kind === \"event\" ? ts : undefined,\n status: ev.kind === \"event\" ? \"done\" : \"open\",\n };\n if (parent && parent !== ev.id) m.spans[ev.id].parent = parent;\n if (ev.kind !== \"event\") cur[ev.to].acts.push(ev.id);\n break;\n }\n case \"reply\":\n case \"done\": {\n const s = m.spans[ev.id];\n if (!s) return; // unknown call → nothing sensible to draw\n s.end = ts;\n s.status = ev.type === \"done\" ? \"done\" : ev.ok ? \"ok\" : \"error\";\n if (ev.type === \"reply\") s.result = ev.label;\n const lane = cur[s.to];\n if (lane) {\n const i = lane.acts.indexOf(ev.id);\n if (i >= 0) lane.acts.splice(i, 1);\n }\n route = {from: s.from, to: s.to, label: s.label};\n break;\n }\n }\n\n // does this real event replace an outstanding one?\n let fulfilled: string | undefined;\n const pid = ev.type === \"reply\" ? ev.id : ev.fulfils;\n if (pid) {\n const i = m.pending.findIndex((p) => p.id === pid);\n if (i >= 0) {\n m.pending.splice(i, 1);\n fulfilled = pid;\n }\n }\n // a sync call means we KNOW a reply will come back\n if (ev.type === \"call\" && ev.kind === \"sync\") {\n m.pending.push({\n id: ev.id,\n type: \"reply\",\n from: ev.to,\n to: ev.from,\n label: `reply: ${ev.label}`,\n since: ts,\n timeoutMs: ev.timeoutMs,\n });\n }\n\n const row: SwimlaneRow = {\n id: ++m.seq,\n event: {...ev, ts} as SwimlaneRowEvent,\n pre,\n post: copySnapshot(cur),\n };\n if (fulfilled) row.fulfilled = fulfilled;\n if (route) row.route = route;\n m.rows.push(row);\n}\n\n/** Ring buffer with amortised span garbage collection. */\nfunction trim(m: SwimlaneModel): void {\n const slack = Math.max(50, Math.floor(m.maxRows / 10));\n if (m.rows.length <= m.maxRows + slack) return;\n m.rows.splice(0, m.rows.length - m.maxRows);\n const keep = new Set<string>();\n for (const r of m.rows) {\n if (\"id\" in r.event) keep.add(r.event.id);\n }\n for (const l of Object.values(m.current)) l.acts.forEach((a) => keep.add(a));\n m.pending.forEach((p) => keep.add(p.id));\n for (const id of Object.keys(m.spans)) {\n if (!keep.has(id)) delete m.spans[id];\n }\n}\n\n/** Remove all rows but keep lanes, open activities and outstanding messages. */\nexport function clearSwimlaneRows(m: SwimlaneModel): void {\n m.rows.splice(0, m.rows.length);\n}\n\n/**\n * Project outstanding messages below the \"now\" line (pure).\n * Expected replies are replayed onto the current snapshot so that open\n * activities visibly run down to the ghost reply that will close them.\n * Newest first ≈ innermost call replies first.\n */\nexport function projectPending(\n current: SwimlaneLanesSnapshot,\n pending: SwimlanePending[],\n): SwimlaneGhostRow[] {\n let snap = current;\n const out: SwimlaneGhostRow[] = [];\n for (let i = pending.length - 1; i >= 0; i--) {\n const p = pending[i];\n const pre = snap;\n const lane = snap[p.from];\n if (p.type === \"reply\" && lane?.acts.includes(p.id)) {\n snap = {...snap, [p.from]: {...lane, acts: lane.acts.filter((a) => a !== p.id)}};\n }\n out.push({id: `g-${p.id}`, pending: p, pre, post: snap});\n }\n return out;\n}\n\n/**\n * Direct children of call `id`: calls made while it was being handled.\n * Their call/reply transitions (and outstanding replies) are the transitions\n * the process \"creates and receives\".\n */\nexport function relatedCallIds(m: Pick<SwimlaneModel, \"spans\">, id: string): string[] {\n const out: string[] = [];\n for (const s of Object.values(m.spans)) if (s.parent === id) out.push(s.id);\n return out.sort((a, b) => m.spans[a].start - m.spans[b].start);\n}\n\n/** Does a row (or ghost row) involve the call chain `id`? */\nexport function rowTouches(\n r: {pre: SwimlaneLanesSnapshot; post: SwimlaneLanesSnapshot},\n eventId: string | undefined,\n id: string,\n): boolean {\n if (eventId === id) return true;\n for (const l of Object.values(r.pre)) if (l.acts.includes(id)) return true;\n for (const l of Object.values(r.post)) if (l.acts.includes(id)) return true;\n return false;\n}\n\nexport function formatTs(t: number): string {\n const d = new Date(t);\n const p = (n: number, w = 2) => String(n).padStart(w, \"0\");\n return `${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}.${p(d.getMilliseconds(), 3)}`;\n}\n\n/** Human-readable description of a hover target (used by the tooltip). */\nexport function describeSwimlaneTarget(\n m: SwimlaneModel,\n t: SwimlaneTarget,\n now: number = Date.now(),\n): string {\n if (t.kind === \"state\") {\n const r = m.rows.find((x) => x.id === t.rowId);\n if (!r || r.event.type !== \"state\") return t.id;\n return `${t.lane} → ${r.event.state} at ${formatTs(r.event.ts)}`;\n }\n if (t.kind === \"pending\") {\n const p = m.pending.find((x) => x.id === t.id);\n if (!p) return \"already fulfilled\";\n const w = now - p.since;\n const what = p.type === \"reply\" ? \"reply\" : \"message\";\n const to = p.timeoutMs\n ? ` / timeout ${p.timeoutMs} ms${w > p.timeoutMs ? \" · OVERDUE\" : \"\"}`\n : \"\";\n return `expected ${what} ${p.from} → ${p.to}: \"${p.label}\" · waiting ${w} ms${to}`;\n }\n const s = m.spans[t.id];\n if (!s) return t.id;\n const dur = s.end != null ? `${s.end - s.start} ms` : `running ${now - s.start} ms`;\n const res = s.result ? ` (${s.result})` : \"\";\n if (t.kind === \"activity\") {\n return `${s.to} handling \"${s.label}\" from ${s.from} · started ${formatTs(s.start)} · ${dur}${res}`;\n }\n return `${s.kind.toUpperCase()} ${s.from} → ${s.to}: \"${s.label}\" · ${s.status}${res} · ${dur}`;\n}\n\n/**\n * Wire the data actions for one model:\n *\n * register((r) => registerSwimlaneModel<AppState>(r, \"main\", (s) => s.swimlane));\n * // …later, from a stream adapter:\n * dispatchSwimlaneEvents(dispatch, {modelId: \"main\", events});\n */\nexport function registerSwimlaneModel<S extends ReduxState>(\n r: PiRegister,\n modelId: string,\n select: (state: S) => SwimlaneModel,\n): void {\n onSwimlaneEventsReceived<S>(r, (state, a) => {\n if (a.modelId === modelId) ingestSwimlaneEvents(select(state), a.events);\n });\n onSwimlaneRowsCleared<S>(r, (state, a) => {\n if (a.modelId === modelId) clearSwimlaneRows(select(state));\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|