@swirls/looms 0.0.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +24 -2
- package/dist/actor/cell.d.ts +9 -4
- package/dist/actor/cell.js +7 -11
- package/dist/actor/host.d.ts +7 -3
- package/dist/actor/host.js +57 -33
- package/dist/actor/index.d.ts +1 -1
- package/dist/actor/index.js +1 -1
- package/dist/actor/routing.d.ts +12 -1
- package/dist/actor/routing.js +57 -51
- package/dist/agent/agent-session.d.ts +5 -0
- package/dist/agent/agent-session.js +270 -0
- package/dist/agent/debugger/agent-chat.d.ts +2 -0
- package/dist/agent/debugger/agent-chat.js +176 -0
- package/dist/agent/debugger/index.d.ts +5 -0
- package/dist/agent/debugger/index.js +19 -0
- package/dist/agent/debugger/summarize.d.ts +2 -0
- package/dist/agent/debugger/summarize.js +51 -0
- package/dist/agent/debugger/token-usage.d.ts +3 -0
- package/dist/agent/debugger/token-usage.js +11 -0
- package/dist/agent/definitions-store.d.ts +2 -3
- package/dist/agent/definitions-store.js +2 -4
- package/dist/agent/definitions.d.ts +47 -29
- package/dist/agent/definitions.js +39 -12
- package/dist/agent/effects.d.ts +359 -2
- package/dist/agent/effects.js +60 -34
- package/dist/agent/events.d.ts +175 -19
- package/dist/agent/events.js +45 -10
- package/dist/agent/index.d.ts +1 -0
- package/dist/agent/index.js +1 -0
- package/dist/agent/llm.d.ts +11 -2
- package/dist/agent/llm.js +13 -3
- package/dist/agent/module.d.ts +812 -78
- package/dist/agent/module.js +16 -4
- package/dist/agent/scope.d.ts +174 -18
- package/dist/agent/signals.d.ts +26 -1
- package/dist/agent/signals.js +79 -0
- package/dist/agent/threads.d.ts +3 -33
- package/dist/agent/threads.js +20 -25
- package/dist/agent/tool-schema.d.ts +1 -17
- package/dist/agent/tool-schema.js +3 -43
- package/dist/agent/types.d.ts +49 -4
- package/dist/agent/types.js +5 -5
- package/dist/ai-vercel/index.d.ts +1 -0
- package/dist/ai-vercel/index.js +1 -0
- package/dist/ai-vercel/messages.js +22 -7
- package/dist/ai-vercel/vercel-evaluator.d.ts +36 -0
- package/dist/ai-vercel/vercel-evaluator.js +73 -0
- package/dist/ai-vercel/vercel-llm.js +64 -44
- package/dist/approval/debugger/approvals-view.d.ts +3 -0
- package/dist/approval/debugger/approvals-view.js +16 -0
- package/dist/approval/debugger/index.d.ts +4 -0
- package/dist/approval/debugger/index.js +11 -0
- package/dist/approval/debugger/summarize.d.ts +2 -0
- package/dist/approval/debugger/summarize.js +17 -0
- package/dist/approval/effects.d.ts +89 -0
- package/dist/approval/module.d.ts +179 -29
- package/dist/approval/projections.d.ts +0 -14
- package/dist/approval/projections.js +20 -3
- package/dist/approval/signals.d.ts +2 -0
- package/dist/approval/signals.js +18 -2
- package/dist/cli/cli.js +2 -0
- package/dist/cli/commands.js +12 -16
- package/dist/client/client.d.ts +1213 -18
- package/dist/client/client.js +120 -120
- package/dist/client/index.d.ts +1 -1
- package/dist/client/index.js +1 -1
- package/dist/client/sse.d.ts +13 -0
- package/dist/client/sse.js +38 -44
- package/dist/cloudflare/alarm-scheduler.d.ts +12 -2
- package/dist/cloudflare/alarm-scheduler.js +34 -7
- package/dist/cloudflare/durable-object.d.ts +10 -2
- package/dist/cloudflare/durable-object.js +59 -28
- package/dist/cloudflare/event-store.d.ts +7 -2
- package/dist/cloudflare/event-store.js +8 -7
- package/dist/cloudflare/index.d.ts +2 -2
- package/dist/cloudflare/index.js +2 -2
- package/dist/cloudflare/router.d.ts +1 -1
- package/dist/cloudflare/router.js +15 -16
- package/dist/core/catalog.d.ts +3 -3
- package/dist/core/catalog.js +2 -5
- package/dist/core/coalesce.js +5 -3
- package/dist/core/compose.d.ts +2 -1
- package/dist/core/compose.js +19 -2
- package/dist/core/debugger-host.d.ts +16 -0
- package/dist/core/debugger-host.js +1 -0
- package/dist/core/definition-store.d.ts +11 -0
- package/dist/core/definition-store.js +6 -0
- package/dist/core/effects.d.ts +127 -15
- package/dist/core/effects.js +89 -17
- package/dist/core/envelope.d.ts +27 -7
- package/dist/core/envelope.js +20 -6
- package/dist/core/event-codec.d.ts +4 -2
- package/dist/core/event-codec.js +10 -5
- package/dist/core/event-index.js +1 -3
- package/dist/core/fold.d.ts +3 -0
- package/dist/core/fold.js +171 -24
- package/dist/core/ids.d.ts +5 -0
- package/dist/core/ids.js +35 -2
- package/dist/core/index.d.ts +7 -1
- package/dist/core/index.js +6 -1
- package/dist/core/kind.d.ts +22 -0
- package/dist/core/kind.js +17 -0
- package/dist/core/limits.d.ts +34 -0
- package/dist/core/limits.js +46 -0
- package/dist/core/match.d.ts +1 -0
- package/dist/core/match.js +10 -1
- package/dist/core/module-scope.d.ts +22 -20
- package/dist/core/module-scope.js +38 -28
- package/dist/core/module.d.ts +18 -5
- package/dist/core/module.js +4 -0
- package/dist/core/projection.d.ts +13 -13
- package/dist/core/projection.js +9 -5
- package/dist/core/protocol.d.ts +68 -11
- package/dist/core/protocol.js +82 -11
- package/dist/core/published.d.ts +43 -0
- package/dist/core/published.js +44 -0
- package/dist/core/replay.d.ts +312 -3
- package/dist/core/replay.js +11 -1
- package/dist/core/schema.d.ts +11 -20
- package/dist/core/schema.js +63 -80
- package/dist/core/serialize.js +2 -4
- package/dist/core/snapshot-store.d.ts +6 -1
- package/dist/core/snapshot-store.js +15 -2
- package/dist/core/snapshots.d.ts +5 -1
- package/dist/core/snapshots.js +10 -15
- package/dist/core/sqlite-store.js +95 -11
- package/dist/core/state.d.ts +142 -4
- package/dist/core/state.js +78 -0
- package/dist/core/store.d.ts +38 -3
- package/dist/core/store.js +77 -22
- package/dist/core/thread.d.ts +10 -9
- package/dist/core/types.d.ts +5 -8
- package/dist/core/types.js +28 -24
- package/dist/core/validate-event.js +14 -4
- package/dist/core/worker-callback.d.ts +17 -0
- package/dist/core/worker-callback.js +1 -0
- package/dist/debugger/app/assets/index-B3Ah8WAE.css +2 -0
- package/dist/debugger/app/assets/index-Qb371t_O.js +22 -0
- package/dist/debugger/app/assets/rolldown-runtime-CbXtAM7H.js +1 -0
- package/dist/debugger/app/assets/routes-CjtEHgPQ.js +47 -0
- package/dist/debugger/app/index.html +14 -0
- package/dist/debugger/components/composer.d.ts +17 -0
- package/dist/debugger/components/composer.js +25 -0
- package/dist/debugger/components/debugger-split.d.ts +2 -1
- package/dist/debugger/components/debugger-split.js +2 -2
- package/dist/debugger/components/event-inspector.d.ts +2 -1
- package/dist/debugger/components/event-inspector.js +25 -16
- package/dist/debugger/components/event-stream.d.ts +2 -1
- package/dist/debugger/components/event-stream.js +14 -74
- package/dist/debugger/components/follow-list.d.ts +19 -0
- package/dist/debugger/components/follow-list.js +96 -0
- package/dist/debugger/components/json-tree.d.ts +11 -0
- package/dist/debugger/components/json-tree.js +120 -0
- package/dist/debugger/components/json-view.js +2 -2
- package/dist/debugger/components/run-tree.js +53 -20
- package/dist/debugger/components/status-dot.d.ts +4 -0
- package/dist/debugger/components/status-dot.js +6 -2
- package/dist/debugger/context.d.ts +14 -0
- package/dist/debugger/context.js +17 -0
- package/dist/debugger/contracts.d.ts +7 -4
- package/dist/debugger/events/builtin-families.d.ts +3 -0
- package/dist/debugger/events/builtin-families.js +9 -0
- package/dist/debugger/events/catalog.d.ts +3 -4
- package/dist/debugger/events/catalog.js +27 -17
- package/dist/debugger/events/protocol-summarize.d.ts +1 -2
- package/dist/debugger/events/protocol-summarize.js +42 -24
- package/dist/debugger/index.d.ts +20 -6
- package/dist/debugger/index.js +19 -5
- package/dist/debugger/lib/cn.d.ts +1 -0
- package/dist/debugger/lib/cn.js +3 -0
- package/dist/debugger/lib/event-meta.d.ts +2 -3
- package/dist/debugger/lib/event-meta.js +6 -12
- package/dist/debugger/lib/json.d.ts +25 -0
- package/dist/debugger/lib/json.js +87 -0
- package/dist/debugger/lib/payload.d.ts +7 -0
- package/dist/debugger/lib/payload.js +11 -0
- package/dist/debugger/lib/time.d.ts +3 -0
- package/dist/debugger/lib/time.js +24 -0
- package/dist/debugger/plugin.d.ts +36 -0
- package/dist/debugger/plugin.js +14 -0
- package/dist/debugger/server/index.d.ts +14 -0
- package/dist/debugger/server/index.js +114 -0
- package/dist/debugger/shell/debugger-shell.d.ts +15 -0
- package/dist/debugger/shell/debugger-shell.js +43 -0
- package/dist/debugger/shell/definition-key.d.ts +3 -0
- package/dist/debugger/shell/definition-key.js +4 -0
- package/dist/debugger/shell/events-pane.d.ts +12 -0
- package/dist/debugger/shell/events-pane.js +25 -0
- package/dist/debugger/shell/projections-panel.d.ts +4 -0
- package/dist/debugger/shell/projections-panel.js +59 -0
- package/dist/debugger/shell/run-controls.d.ts +4 -0
- package/dist/debugger/shell/run-controls.js +72 -0
- package/dist/debugger/shell/run-search.d.ts +6 -0
- package/dist/debugger/shell/run-search.js +102 -0
- package/dist/debugger/shell/run-type-menu.d.ts +6 -0
- package/dist/debugger/shell/run-type-menu.js +11 -0
- package/dist/debugger/shell/schema-form.d.ts +31 -0
- package/dist/debugger/shell/schema-form.js +117 -0
- package/dist/debugger/shell/start-form.d.ts +3 -0
- package/dist/debugger/shell/start-form.js +75 -0
- package/dist/debugger/shell/use-start-run.d.ts +14 -0
- package/dist/debugger/shell/use-start-run.js +32 -0
- package/dist/debugger/shell/workspace.d.ts +6 -0
- package/dist/debugger/shell/workspace.js +15 -0
- package/dist/debugger/styles.css +111 -27
- package/dist/debugger/ui/button.d.ts +24 -0
- package/dist/debugger/ui/button.js +24 -0
- package/dist/debugger/ui/command.d.ts +9 -0
- package/dist/debugger/ui/command.js +24 -0
- package/dist/debugger/ui/dropdown-menu.d.ts +13 -0
- package/dist/debugger/ui/dropdown-menu.js +37 -0
- package/dist/debugger/ui/panel.d.ts +14 -0
- package/dist/debugger/ui/panel.js +16 -0
- package/dist/debugger/ui/popover.d.ts +7 -0
- package/dist/debugger/ui/popover.js +17 -0
- package/dist/debugger/ui/resizable.d.ts +7 -0
- package/dist/debugger/ui/resizable.js +14 -0
- package/dist/debugger/ui/scroll-area.d.ts +2 -1
- package/dist/debugger/ui/scroll-area.js +5 -3
- package/dist/debugger/ui/select.d.ts +10 -0
- package/dist/debugger/ui/select.js +21 -0
- package/dist/debugger/ui/tabs.js +2 -2
- package/dist/debugger/ui/textarea.d.ts +3 -0
- package/dist/debugger/ui/textarea.js +7 -0
- package/dist/evaluator/definitions-store.d.ts +11 -0
- package/dist/evaluator/definitions-store.js +8 -0
- package/dist/evaluator/definitions.d.ts +18 -0
- package/dist/evaluator/definitions.js +4 -0
- package/dist/evaluator/effects.d.ts +226 -0
- package/dist/evaluator/effects.js +132 -0
- package/dist/evaluator/evaluator.d.ts +24 -0
- package/dist/evaluator/evaluator.js +67 -0
- package/dist/evaluator/events.d.ts +59 -0
- package/dist/evaluator/events.js +21 -0
- package/dist/evaluator/index.d.ts +11 -0
- package/dist/evaluator/index.js +11 -0
- package/dist/evaluator/module.d.ts +606 -0
- package/dist/evaluator/module.js +25 -0
- package/dist/evaluator/projections.d.ts +50 -0
- package/dist/evaluator/projections.js +20 -0
- package/dist/evaluator/scope.d.ts +59 -0
- package/dist/evaluator/scope.js +7 -0
- package/dist/evaluator/signals.d.ts +3 -0
- package/dist/evaluator/signals.js +17 -0
- package/dist/evaluator/threads.d.ts +62 -0
- package/dist/evaluator/threads.js +117 -0
- package/dist/evaluator/types.d.ts +125 -0
- package/dist/evaluator/types.js +51 -0
- package/dist/projectors/delivery.d.ts +83 -0
- package/dist/projectors/delivery.js +182 -0
- package/dist/projectors/index-model.d.ts +1 -1
- package/dist/projectors/index-model.js +4 -6
- package/dist/projectors/index.d.ts +2 -0
- package/dist/projectors/index.js +2 -0
- package/dist/projectors/memory.d.ts +1 -1
- package/dist/projectors/memory.js +10 -8
- package/dist/projectors/postgres.js +44 -58
- package/dist/projectors/projector.d.ts +17 -0
- package/dist/projectors/projector.js +26 -1
- package/dist/projectors/sqlite-delivery.d.ts +4 -0
- package/dist/projectors/sqlite-delivery.js +147 -0
- package/dist/projectors/sqlite.js +23 -23
- package/dist/projectors/with-projectors.d.ts +7 -2
- package/dist/projectors/with-projectors.js +27 -28
- package/dist/react/notify.js +11 -8
- package/dist/react/react.d.ts +2 -3
- package/dist/react/react.js +14 -30
- package/dist/react/sse.d.ts +1 -1
- package/dist/react/sse.js +1 -1
- package/dist/react/store.d.ts +3 -3
- package/dist/react/store.js +109 -119
- package/dist/runtime/cancellation.d.ts +16 -0
- package/dist/runtime/cancellation.js +129 -0
- package/dist/runtime/deadline-transition-planner.d.ts +31 -0
- package/dist/runtime/deadline-transition-planner.js +139 -0
- package/dist/runtime/dispatch-effect.d.ts +3 -2
- package/dist/runtime/dispatch-effect.js +7 -13
- package/dist/runtime/errors.d.ts +51 -0
- package/dist/runtime/errors.js +39 -0
- package/dist/runtime/{runtime-helpers.d.ts → helpers.d.ts} +24 -4
- package/dist/runtime/{runtime-helpers.js → helpers.js} +56 -19
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime/index.js +3 -3
- package/dist/runtime/ingress.d.ts +15 -0
- package/dist/runtime/ingress.js +42 -0
- package/dist/runtime/local-effect-executor.d.ts +21 -0
- package/dist/runtime/local-effect-executor.js +137 -0
- package/dist/runtime/looms.d.ts +30 -4
- package/dist/runtime/looms.js +57 -37
- package/dist/runtime/observer.d.ts +45 -0
- package/dist/runtime/observer.js +11 -0
- package/dist/runtime/repository.d.ts +55 -0
- package/dist/runtime/repository.js +293 -0
- package/dist/runtime/retry-policy.d.ts +16 -0
- package/dist/runtime/retry-policy.js +29 -0
- package/dist/runtime/run-execution-context.d.ts +30 -0
- package/dist/runtime/run-execution-context.js +56 -0
- package/dist/runtime/run-start.d.ts +4 -0
- package/dist/runtime/run-start.js +44 -0
- package/dist/runtime/runtime.d.ts +6 -87
- package/dist/runtime/runtime.js +248 -470
- package/dist/runtime/server/auth.d.ts +41 -0
- package/dist/runtime/server/auth.js +45 -0
- package/dist/runtime/server/debugger-host.d.ts +11 -0
- package/dist/runtime/server/debugger-host.js +32 -0
- package/dist/runtime/server/fetch-handler.d.ts +18 -0
- package/dist/runtime/server/fetch-handler.js +62 -0
- package/dist/runtime/server/http.d.ts +38 -0
- package/dist/runtime/server/http.js +66 -0
- package/dist/runtime/server/route-table.d.ts +55 -0
- package/dist/runtime/server/route-table.js +74 -0
- package/dist/runtime/server/routes/definitions.d.ts +3 -0
- package/dist/runtime/server/routes/definitions.js +20 -0
- package/dist/runtime/server/routes/effects.d.ts +3 -0
- package/dist/runtime/server/routes/effects.js +59 -0
- package/dist/runtime/server/routes/operations.d.ts +3 -0
- package/dist/runtime/server/routes/operations.js +14 -0
- package/dist/runtime/server/routes/runs.d.ts +3 -0
- package/dist/runtime/server/routes/runs.js +199 -0
- package/dist/runtime/server.d.ts +4 -8
- package/dist/runtime/server.js +6 -211
- package/dist/runtime/sse.d.ts +2 -1
- package/dist/runtime/sse.js +24 -17
- package/dist/runtime/types.d.ts +117 -0
- package/dist/runtime/types.js +1 -0
- package/dist/runtime/wake-engine.d.ts +20 -0
- package/dist/runtime/wake-engine.js +311 -0
- package/dist/runtime/wake-scheduler.d.ts +3 -4
- package/dist/runtime/wake-scheduler.js +25 -31
- package/dist/runtime/worker-callbacks.d.ts +16 -0
- package/dist/runtime/worker-callbacks.js +167 -0
- package/dist/runtime/worker-effect-dispatch.d.ts +36 -0
- package/dist/runtime/worker-effect-dispatch.js +126 -0
- package/dist/s2/config.d.ts +1 -1
- package/dist/s2/config.js +1 -1
- package/dist/s2/lite.js +70 -82
- package/dist/s2/pagination.d.ts +15 -0
- package/dist/s2/pagination.js +36 -0
- package/dist/s2/projector.js +6 -5
- package/dist/s2/snapshot-store.js +188 -161
- package/dist/s2/store.d.ts +3 -21
- package/dist/s2/store.js +71 -165
- package/dist/testing/index.d.ts +15 -0
- package/dist/testing/index.js +53 -16
- package/dist/workflow/debugger/index.d.ts +4 -0
- package/dist/workflow/debugger/index.js +11 -0
- package/dist/workflow/debugger/nodes-view.d.ts +3 -0
- package/dist/workflow/debugger/nodes-view.js +13 -0
- package/dist/workflow/debugger/summarize.d.ts +2 -0
- package/dist/workflow/debugger/summarize.js +30 -0
- package/dist/workflow/definitions-store.d.ts +2 -3
- package/dist/workflow/definitions-store.js +2 -2
- package/dist/workflow/definitions.d.ts +78 -22
- package/dist/workflow/definitions.js +80 -14
- package/dist/workflow/effects.d.ts +453 -107
- package/dist/workflow/effects.js +214 -115
- package/dist/workflow/events.d.ts +149 -5
- package/dist/workflow/events.js +27 -5
- package/dist/workflow/graph.d.ts +56 -0
- package/dist/workflow/graph.js +207 -0
- package/dist/workflow/index.d.ts +1 -0
- package/dist/workflow/index.js +1 -0
- package/dist/workflow/module.d.ts +1047 -266
- package/dist/workflow/projections.d.ts +14 -12
- package/dist/workflow/projections.js +30 -8
- package/dist/workflow/scope.d.ts +146 -4
- package/dist/workflow/threads.d.ts +383 -155
- package/dist/workflow/threads.js +308 -110
- package/package.json +26 -2
- package/dist/debugger/events/domain-summarize.d.ts +0 -3
- package/dist/debugger/events/domain-summarize.js +0 -107
- package/dist/debugger/events/family.d.ts +0 -8
- package/dist/debugger/events/family.js +0 -44
- package/dist/runtime/events-api.d.ts +0 -4
- package/dist/runtime/events-api.js +0 -84
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>Looms debugger</title>
|
|
7
|
+
<script type="module" crossorigin src="./assets/index-Qb371t_O.js"></script>
|
|
8
|
+
<link rel="modulepreload" crossorigin href="./assets/rolldown-runtime-CbXtAM7H.js">
|
|
9
|
+
<link rel="stylesheet" crossorigin href="./assets/index-B3Ah8WAE.css">
|
|
10
|
+
</head>
|
|
11
|
+
<body>
|
|
12
|
+
<div id="root"></div>
|
|
13
|
+
</body>
|
|
14
|
+
</html>
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Message input with one primary action. Enter sends, Shift+Enter adds a newline,
|
|
4
|
+
* and `toolbar` holds secondary controls such as delivery mode.
|
|
5
|
+
*/
|
|
6
|
+
export declare function Composer({ value, onChange, onSubmit, pending, disabled, placeholder, submitLabel, toolbar, error, className, }: {
|
|
7
|
+
value: string;
|
|
8
|
+
onChange: (value: string) => void;
|
|
9
|
+
onSubmit: () => void;
|
|
10
|
+
pending?: boolean;
|
|
11
|
+
disabled?: boolean;
|
|
12
|
+
placeholder?: string;
|
|
13
|
+
submitLabel?: string;
|
|
14
|
+
toolbar?: ReactNode;
|
|
15
|
+
error?: string | null;
|
|
16
|
+
className?: string;
|
|
17
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { ArrowUp, LoaderCircle } from 'lucide-react';
|
|
3
|
+
import { cn } from '../lib/cn.js';
|
|
4
|
+
import { Button } from '../ui/button.js';
|
|
5
|
+
import { Kbd } from '../ui/panel.js';
|
|
6
|
+
/**
|
|
7
|
+
* Message input with one primary action. Enter sends, Shift+Enter adds a newline,
|
|
8
|
+
* and `toolbar` holds secondary controls such as delivery mode.
|
|
9
|
+
*/
|
|
10
|
+
export function Composer({ value, onChange, onSubmit, pending = false, disabled = false, placeholder = 'Message', submitLabel = 'Send', toolbar, error, className, }) {
|
|
11
|
+
const canSubmit = !pending && !disabled && value.trim().length > 0;
|
|
12
|
+
return (_jsxs("form", { className: cn('border-border shrink-0 border-t p-3', className), onSubmit: (event) => {
|
|
13
|
+
event.preventDefault();
|
|
14
|
+
if (canSubmit) {
|
|
15
|
+
onSubmit();
|
|
16
|
+
}
|
|
17
|
+
}, children: [_jsxs("div", { className: "border-input bg-card focus-within:border-ring/70 focus-within:ring-ring/25 rounded-lg border transition-[border-color,box-shadow] duration-150 focus-within:ring-[3px]", children: [_jsx("textarea", { "data-slot": "composer-input", value: value, rows: 1, placeholder: placeholder, "aria-label": placeholder, disabled: disabled, onChange: (event) => onChange(event.target.value), onKeyDown: (event) => {
|
|
18
|
+
if (event.key === 'Enter' && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
19
|
+
event.preventDefault();
|
|
20
|
+
if (canSubmit) {
|
|
21
|
+
onSubmit();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}, className: "placeholder:text-muted-foreground block field-sizing-content max-h-48 min-h-11 w-full resize-none bg-transparent px-3 pt-2.5 pb-1 text-[13px] leading-relaxed outline-none disabled:cursor-not-allowed disabled:opacity-50" }), _jsxs("div", { className: "flex h-10 items-center gap-1 pr-1.5 pl-1.5", children: [toolbar, _jsxs("span", { className: "text-muted-foreground ml-auto hidden items-center gap-1 pr-1 text-[10px] @sm:flex", children: [_jsx(Kbd, { children: "\u21B5" }), submitLabel.toLowerCase(), _jsx(Kbd, { className: "ml-1.5", children: "\u21E7\u21B5" }), "newline"] }), _jsx(Button, { type: "submit", size: "icon-sm", "aria-label": submitLabel, disabled: !canSubmit, className: "ml-auto rounded-full @sm:ml-0", children: pending ? _jsx(LoaderCircle, { className: "animate-spin" }) : _jsx(ArrowUp, {}) })] })] }), error ? _jsx("p", { className: "text-status-failed mt-2 px-1 text-[11px]", children: error }) : null] }));
|
|
25
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { ReactNode } from 'react';
|
|
2
|
-
import type { ThreadTree } from '@swirls/looms/core';
|
|
2
|
+
import type { ThreadNode, ThreadTree } from '@swirls/looms/core';
|
|
3
3
|
import type { DebuggerEvent, EventStreamCatalog, ReplayLoader } from '../contracts';
|
|
4
|
+
export declare function findThreadName(node: ThreadNode | null, threadId: string | undefined): string | undefined;
|
|
4
5
|
export declare function DebuggerSplit<TEvent extends DebuggerEvent>({ runId, runStatus, tree, events, eventCounts, startedAt, selectedThread, onSelectThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, orientation, showTreeHeading, className, treeClassName, streamClassName, }: {
|
|
5
6
|
runId: string;
|
|
6
7
|
runStatus: string;
|
|
@@ -2,7 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { cn } from '../lib/cn.js';
|
|
3
3
|
import { EventStream } from './event-stream.js';
|
|
4
4
|
import { RunTree } from './run-tree.js';
|
|
5
|
-
function findThreadName(node, threadId) {
|
|
5
|
+
export function findThreadName(node, threadId) {
|
|
6
6
|
if (!node || !threadId) {
|
|
7
7
|
return undefined;
|
|
8
8
|
}
|
|
@@ -21,5 +21,5 @@ export function DebuggerSplit({ runId, runStatus, tree, events, eventCounts, sta
|
|
|
21
21
|
const vertical = orientation === 'vertical';
|
|
22
22
|
return (_jsxs("div", { className: cn('grid h-full min-h-0', vertical
|
|
23
23
|
? 'grid-rows-[minmax(0,38%)_minmax(0,1fr)]'
|
|
24
|
-
: 'grid-cols-1 min-[36rem]:grid-cols-[
|
|
24
|
+
: 'grid-cols-1 min-[36rem]:grid-cols-[14rem_minmax(0,1fr)]', className), children: [_jsx("section", { "aria-label": "Run tree", className: cn('border-border min-h-0 overflow-hidden border-b', !vertical && 'min-[36rem]:border-r min-[36rem]:border-b-0', treeClassName), children: _jsx(RunTree, { showHeading: showTreeHeading, runId: runId, runStatus: runStatus, tree: tree, eventCounts: eventCounts, totalEventCount: events.length, selectedThread: selectedThread, onSelectThread: onSelectThread }) }), _jsx("section", { className: cn('min-h-0 overflow-hidden', streamClassName), children: _jsx(EventStream, { events: events, startedAt: startedAt, threadId: selectedThread, threadLabel: findThreadName(tree.root, selectedThread), onClearThread: selectedThread ? () => onSelectThread(undefined) : undefined, selectedSeq: selectedSeq, onSelectSeq: onSelectSeq, catalog: catalog, loadReplayStep: loadReplayStep, renderInspector: renderInspector }) })] }));
|
|
25
25
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { DebuggerEvent, ReplayLoader } from '../contracts';
|
|
2
|
-
export declare function EventInspector({ event, loadReplayStep, }: {
|
|
2
|
+
export declare function EventInspector({ event, loadReplayStep, onClose, }: {
|
|
3
3
|
event: DebuggerEvent;
|
|
4
4
|
loadReplayStep?: ReplayLoader;
|
|
5
|
+
onClose?: () => void;
|
|
5
6
|
}): import("react").JSX.Element;
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { X } from 'lucide-react';
|
|
2
3
|
import { useEffect, useState } from 'react';
|
|
3
4
|
import { asJson } from '@swirls/looms/core';
|
|
5
|
+
import { Button } from '../ui/button.js';
|
|
6
|
+
import { ScrollArea } from '../ui/scroll-area.js';
|
|
4
7
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from '../ui/tabs.js';
|
|
5
|
-
import {
|
|
6
|
-
|
|
8
|
+
import { JsonTree } from './json-tree.js';
|
|
9
|
+
function Pane({ value, children }) {
|
|
10
|
+
return (_jsx(TabsContent, { value: value, className: "min-h-0", children: _jsx(ScrollArea, { className: "h-full", children: _jsx("div", { className: "px-3 pb-3", children: children }) }) }));
|
|
11
|
+
}
|
|
12
|
+
function StepJson({ step, pick, }) {
|
|
13
|
+
return step ? (_jsx(JsonTree, { value: pick(step), defaultExpandDepth: 2 })) : (_jsx("p", { className: "text-muted-foreground text-xs", children: "Loading\u2026" }));
|
|
14
|
+
}
|
|
15
|
+
export function EventInspector({ event, loadReplayStep, onClose, }) {
|
|
7
16
|
const [step, setStep] = useState(null);
|
|
8
17
|
const canReplay = loadReplayStep !== undefined;
|
|
9
18
|
useEffect(() => {
|
|
@@ -21,18 +30,18 @@ export function EventInspector({ event, loadReplayStep, }) {
|
|
|
21
30
|
cancelled = true;
|
|
22
31
|
};
|
|
23
32
|
}, [event.runId, event.seq, loadReplayStep]);
|
|
24
|
-
return (
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
33
|
+
return (_jsx("div", { className: "border-border bg-sidebar flex h-[45%] min-h-40 shrink-0 flex-col border-t", children: _jsxs(Tabs, { defaultValue: "payload", className: "min-h-0 flex-1 gap-0", children: [_jsxs("div", { className: "flex h-10 shrink-0 items-center gap-2 pr-2 pl-3", children: [_jsxs("span", { className: "text-muted-foreground font-mono text-[11px] tabular-nums", children: ["#", event.seq] }), _jsx("span", { className: "min-w-0 flex-1 truncate font-mono text-xs", children: event.type }), _jsxs(TabsList, { children: [_jsx(TabsTrigger, { value: "payload", children: "Payload" }), _jsx(TabsTrigger, { value: "envelope", children: "Envelope" }), canReplay ? _jsx(TabsTrigger, { value: "state", children: "State" }) : null, canReplay ? _jsx(TabsTrigger, { value: "effects", children: "Effects" }) : null] }), onClose ? (_jsx(Button, { variant: "ghost", size: "icon-sm", "aria-label": "Close inspector", onClick: onClose, children: _jsx(X, {}) })) : null] }), _jsx(Pane, { value: "payload", children: _jsx(JsonTree, { value: event.payload, defaultExpandDepth: 2 }) }), _jsx(Pane, { value: "envelope", children: _jsx(JsonTree, { value: asJson({
|
|
34
|
+
id: event.id,
|
|
35
|
+
runId: event.runId,
|
|
36
|
+
seq: event.seq,
|
|
37
|
+
ts: event.ts,
|
|
38
|
+
type: event.type,
|
|
39
|
+
threadId: event.threadId,
|
|
40
|
+
parentThreadId: event.parentThreadId ?? null,
|
|
41
|
+
causationId: event.causationId ?? null,
|
|
42
|
+
correlationId: event.correlationId ?? null,
|
|
43
|
+
effectId: event.effectId ?? null,
|
|
44
|
+
origin: event.origin,
|
|
45
|
+
ephemeral: event.ephemeral ?? false,
|
|
46
|
+
}) }) }), canReplay ? (_jsx(Pane, { value: "state", children: _jsxs("div", { className: "grid gap-3", children: [_jsxs("section", { children: [_jsx("h4", { className: "text-muted-foreground mb-1 text-[11px] font-medium", children: "Before" }), _jsx(StepJson, { step: step, pick: (loaded) => asJson(loaded.before) })] }), _jsxs("section", { children: [_jsx("h4", { className: "text-muted-foreground mb-1 text-[11px] font-medium", children: "After" }), _jsx(StepJson, { step: step, pick: (loaded) => asJson(loaded.after) })] })] }) })) : null, canReplay ? (_jsx(Pane, { value: "effects", children: _jsx(StepJson, { step: step, pick: (loaded) => asJson(loaded.effects) }) })) : null] }) }));
|
|
38
47
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import type { DebuggerEvent, EventStreamCatalog, ReplayLoader } from '../contracts';
|
|
3
|
-
export declare function EventStream<TEvent extends DebuggerEvent>({ events, startedAt, threadId, threadLabel, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, }: {
|
|
3
|
+
export declare function EventStream<TEvent extends DebuggerEvent>({ events, startedAt, threadId, threadLabel, onClearThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, }: {
|
|
4
4
|
events: readonly TEvent[];
|
|
5
5
|
startedAt?: number;
|
|
6
6
|
threadId?: string;
|
|
7
7
|
threadLabel?: string;
|
|
8
|
+
onClearThread?: () => void;
|
|
8
9
|
selectedSeq?: number;
|
|
9
10
|
onSelectSeq: (seq: number | undefined) => void;
|
|
10
11
|
catalog?: EventStreamCatalog<TEvent>;
|
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { DropdownMenu as DropdownMenuPrimitive } from 'radix-ui';
|
|
5
|
-
import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
2
|
+
import { ArrowDownToLine, Search, SlidersHorizontal, X } from 'lucide-react';
|
|
3
|
+
import { useCallback, useMemo, useState } from 'react';
|
|
6
4
|
import { defaultEventCatalog } from '../events/catalog.js';
|
|
7
5
|
import { cn, shortId } from '../lib/cn.js';
|
|
8
6
|
import { getEventMeta } from '../lib/event-meta.js';
|
|
9
7
|
import { useVisibleEvents } from '../lib/visible-events.js';
|
|
10
|
-
import {
|
|
8
|
+
import { Button } from '../ui/button.js';
|
|
9
|
+
import { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuLabel, DropdownMenuTrigger, } from '../ui/dropdown-menu.js';
|
|
11
10
|
import { Input } from '../ui/input.js';
|
|
12
|
-
import {
|
|
11
|
+
import { PanelHeader, PanelTitle } from '../ui/panel.js';
|
|
13
12
|
import { EventInspector } from './event-inspector.js';
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
export function EventStream({ events, startedAt, threadId, threadLabel, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, }) {
|
|
13
|
+
import { FollowList } from './follow-list.js';
|
|
14
|
+
const ROW_ESTIMATE = 26;
|
|
15
|
+
export function EventStream({ events, startedAt, threadId, threadLabel, onClearThread, selectedSeq, onSelectSeq, catalog, loadReplayStep, renderInspector, }) {
|
|
17
16
|
const eventCatalog = catalog ?? defaultEventCatalog;
|
|
18
17
|
const [query, setQuery] = useState('');
|
|
19
18
|
const [follow, setFollow] = useState(true);
|
|
20
19
|
const [families, setFamilies] = useState(() => new Set(eventCatalog.families));
|
|
21
|
-
const parentRef = useRef(null);
|
|
22
|
-
const followLock = useRef(false);
|
|
23
20
|
const selected = useMemo(() => {
|
|
24
21
|
if (selectedSeq === undefined) {
|
|
25
22
|
return undefined;
|
|
@@ -37,53 +34,6 @@ export function EventStream({ events, startedAt, threadId, threadLabel, selected
|
|
|
37
34
|
threadId,
|
|
38
35
|
catalog: eventCatalog,
|
|
39
36
|
});
|
|
40
|
-
const virtualizer = useVirtualizer({
|
|
41
|
-
count: visible.length,
|
|
42
|
-
getScrollElement: () => parentRef.current,
|
|
43
|
-
estimateSize: () => ROW_ESTIMATE,
|
|
44
|
-
overscan: 16,
|
|
45
|
-
paddingStart: 4,
|
|
46
|
-
paddingEnd: 4,
|
|
47
|
-
getItemKey: (index) => visible[index]?.id ?? index,
|
|
48
|
-
// Avoid flushSync during ref measurement / follow-scroll under high event rates.
|
|
49
|
-
useFlushSync: false,
|
|
50
|
-
});
|
|
51
|
-
useEffect(() => {
|
|
52
|
-
if (!follow || visible.length === 0) {
|
|
53
|
-
return () => undefined;
|
|
54
|
-
}
|
|
55
|
-
followLock.current = true;
|
|
56
|
-
let timer;
|
|
57
|
-
const rafId = requestAnimationFrame(() => {
|
|
58
|
-
virtualizer.scrollToIndex(visible.length - 1, { align: 'end' });
|
|
59
|
-
timer = setTimeout(() => {
|
|
60
|
-
followLock.current = false;
|
|
61
|
-
}, 50);
|
|
62
|
-
});
|
|
63
|
-
return () => {
|
|
64
|
-
cancelAnimationFrame(rafId);
|
|
65
|
-
if (timer !== undefined) {
|
|
66
|
-
clearTimeout(timer);
|
|
67
|
-
}
|
|
68
|
-
followLock.current = false;
|
|
69
|
-
};
|
|
70
|
-
}, [follow, visible.length, virtualizer]);
|
|
71
|
-
const onScroll = useCallback(() => {
|
|
72
|
-
if (followLock.current) {
|
|
73
|
-
return;
|
|
74
|
-
}
|
|
75
|
-
const el = parentRef.current;
|
|
76
|
-
if (!el) {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < FOLLOW_THRESHOLD;
|
|
80
|
-
if (follow && !atBottom) {
|
|
81
|
-
setFollow(false);
|
|
82
|
-
}
|
|
83
|
-
else if (!follow && atBottom) {
|
|
84
|
-
setFollow(true);
|
|
85
|
-
}
|
|
86
|
-
}, [follow]);
|
|
87
37
|
const toggleFamily = useCallback((family) => {
|
|
88
38
|
setFamilies((prev) => {
|
|
89
39
|
const next = new Set(prev);
|
|
@@ -97,23 +47,13 @@ export function EventStream({ events, startedAt, threadId, threadLabel, selected
|
|
|
97
47
|
});
|
|
98
48
|
}, []);
|
|
99
49
|
const hiddenFamilyCount = eventCatalog.families.length - families.size;
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
return (_jsx("div", { "data-index": row.index, ref: virtualizer.measureElement, className: "absolute top-0 left-0 w-full", style: { transform: `translateY(${row.start}px)` }, children: _jsx(EventRow, { event: event, catalog: eventCatalog, startedAt: startedAt, selected: selectedSeq === event.seq, onSelectSeq: onSelectSeq }) }, event.id));
|
|
106
|
-
}) }) }), selected
|
|
107
|
-
? (renderInspector?.(selected) ?? (_jsx(EventInspector, { event: selected, loadReplayStep: loadReplayStep })))
|
|
50
|
+
const filtered = visible.length !== events.length;
|
|
51
|
+
return (_jsxs("div", { className: "debugger-event-stream flex h-full min-h-0 flex-col", children: [_jsxs(PanelHeader, { className: "gap-1.5 pr-2", children: [_jsx(PanelTitle, { className: "hidden @md:flex", meta: filtered ? `${visible.length}/${events.length}` : events.length, children: "Events" }), _jsxs("div", { className: "relative ml-auto min-w-0 flex-1 @md:max-w-64", children: [_jsx(Search, { "aria-hidden": true, className: "text-muted-foreground pointer-events-none absolute top-1/2 left-2 size-3 -translate-y-1/2" }), _jsx(Input, { value: query, onChange: (event) => setQuery(event.target.value), placeholder: "Search events", "aria-label": "Search events", className: "h-7 w-full pl-7 text-xs" })] }), _jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs(Button, { variant: "ghost", size: "icon-sm", className: "relative", "aria-label": "Filter event families", children: [_jsx(SlidersHorizontal, {}), hiddenFamilyCount > 0 ? (_jsx("span", { className: "bg-foreground text-background absolute top-0.5 right-0.5 flex size-3.5 items-center justify-center rounded-full font-mono text-[8px]", children: hiddenFamilyCount })) : null] }) }), _jsxs(DropdownMenuContent, { align: "end", children: [_jsx(DropdownMenuLabel, { children: "Event families" }), eventCatalog.families.map((family) => (_jsxs(DropdownMenuCheckboxItem, { checked: families.has(family), onCheckedChange: () => toggleFamily(family), onSelect: (event) => event.preventDefault(), className: "font-mono", children: [_jsx("span", { "aria-hidden": true, className: "size-1.5 rounded-full", style: { backgroundColor: eventCatalog.familyColor(family) } }), family] }, family)))] })] }), _jsxs(Button, { variant: "ghost", size: "sm", "aria-pressed": follow, "aria-label": "Follow newest events", onClick: () => setFollow(!follow), className: "aria-pressed:bg-accent aria-pressed:text-foreground px-2", children: [_jsx(ArrowDownToLine, {}), _jsx("span", { className: "hidden @lg:inline", children: "Follow" })] })] }), threadId ? (_jsxs("div", { className: "border-border flex h-8 shrink-0 items-center gap-2 border-b px-3 text-[11px]", children: [_jsx("span", { className: "text-muted-foreground", children: "Thread" }), _jsxs("span", { className: "bg-accent text-foreground inline-flex h-5 min-w-0 items-center gap-1 rounded-sm pr-0.5 pl-1.5 font-mono", children: [_jsx("span", { className: "truncate", title: threadId, children: threadLabel ?? shortId(threadId) }), onClearThread ? (_jsx("button", { type: "button", "aria-label": "Show all threads", onClick: onClearThread, className: "text-muted-foreground hover:text-foreground hover:bg-background/60 flex size-4 items-center justify-center rounded-sm", children: _jsx(X, { className: "size-3" }) })) : null] })] })) : null, _jsx(FollowList, { items: visible, getKey: (event) => event.id, estimateSize: ROW_ESTIMATE, overscan: 16, follow: follow, onFollowChange: setFollow, className: "min-h-32", itemClassName: "px-1.5", empty: _jsx("p", { className: "text-muted-foreground p-6 text-center text-xs", children: events.length === 0 ? 'Waiting for events…' : 'No events match these filters.' }), renderItem: (event) => (_jsx(EventRow, { event: event, catalog: eventCatalog, startedAt: startedAt, selected: selectedSeq === event.seq, onSelectSeq: onSelectSeq })) }), selected
|
|
52
|
+
? (renderInspector?.(selected) ?? (_jsx(EventInspector, { event: selected, loadReplayStep: loadReplayStep, onClose: () => onSelectSeq(undefined) })))
|
|
108
53
|
: null] }));
|
|
109
54
|
}
|
|
110
|
-
|
|
111
|
-
const EventRow = memo(function EventRow({ event, catalog, startedAt, selected, onSelectSeq, }) {
|
|
55
|
+
function EventRow({ event, catalog, startedAt, selected, onSelectSeq, }) {
|
|
112
56
|
const meta = getEventMeta(event, catalog);
|
|
113
57
|
const offset = startedAt !== undefined ? Math.max(0, event.ts - startedAt) : undefined;
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
}, [onSelectSeq, selected, event.seq]);
|
|
117
|
-
return (_jsxs("button", { type: "button", onClick: handleClick, title: [meta.summary.title, meta.summary.detail].filter(Boolean).join(' — '), className: cn('flex w-full items-start gap-1.5 rounded-sm px-1.5 py-1 text-left font-mono text-[11px]', selected ? 'bg-accent' : 'hover:bg-accent/40', event.ephemeral && 'opacity-50'), children: [_jsx("span", { className: cn('mt-1.5 size-1.5 shrink-0 rounded-full', catalog.familyClass(meta.family)) }), _jsx("span", { className: "text-muted-foreground w-6 shrink-0 text-right tabular-nums", children: event.seq }), _jsx("span", { className: "debugger-event-offset text-muted-foreground w-10 shrink-0 text-right tabular-nums", children: offset !== undefined ? `+${offset}` : '' }), _jsxs("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: [_jsx("span", { className: "text-foreground", children: meta.summary.title }), meta.summary.detail ? (_jsx("span", { className: "text-muted-foreground ml-1", children: meta.summary.detail })) : null] }), event.causationId ? (_jsxs("span", { className: "debugger-event-causation text-muted-foreground shrink-0", children: ["\u2190 ", shortId(event.causationId)] })) : null, event.threadId ? (_jsx("span", { className: "debugger-event-thread text-muted-foreground shrink-0", children: shortId(event.threadId) })) : null] }));
|
|
118
|
-
// SAFETY: memoized EventRow preserves generic EventRowProps contract.
|
|
119
|
-
});
|
|
58
|
+
return (_jsxs("button", { type: "button", onClick: () => onSelectSeq(selected ? undefined : event.seq), title: [meta.summary.title, meta.summary.detail].filter(Boolean).join(' — '), className: cn('flex h-[26px] w-full items-center gap-2 rounded-sm px-1.5 text-left font-mono text-[11px]', selected ? 'bg-accent' : 'hover:bg-accent/40', event.ephemeral && 'opacity-50'), children: [_jsx("span", { className: "size-1.5 shrink-0 rounded-full", style: { backgroundColor: catalog.familyColor(meta.family) } }), _jsx("span", { className: "text-muted-foreground w-7 shrink-0 text-right tabular-nums", children: event.seq }), _jsx("span", { className: "debugger-event-offset text-muted-foreground/70 w-12 shrink-0 text-right tabular-nums", children: offset !== undefined ? `+${offset}` : '' }), _jsxs("span", { className: "min-w-0 flex-1 truncate whitespace-nowrap", children: [_jsx("span", { className: "text-foreground", children: meta.summary.title }), meta.summary.detail ? (_jsx("span", { className: "text-muted-foreground ml-1.5", children: meta.summary.detail })) : null] }), event.causationId ? (_jsxs("span", { className: "debugger-event-causation text-muted-foreground/70 shrink-0", children: ["\u2190 ", shortId(event.causationId)] })) : null, event.threadId ? (_jsx("span", { className: "debugger-event-thread text-muted-foreground/70 shrink-0", children: shortId(event.threadId) })) : null] }));
|
|
59
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* A virtualized list that stays pinned to its newest item while following, stops following
|
|
4
|
+
* when the reader scrolls up, and resumes when they return to the bottom.
|
|
5
|
+
*/
|
|
6
|
+
export declare function FollowList<T>({ items, getKey, renderItem, estimateSize, overscan, follow: controlledFollow, onFollowChange, paddingStart, paddingEnd, className, itemClassName, empty, }: {
|
|
7
|
+
items: readonly T[];
|
|
8
|
+
getKey: (item: T, index: number) => string | number;
|
|
9
|
+
renderItem: (item: T, index: number) => ReactNode;
|
|
10
|
+
estimateSize: number;
|
|
11
|
+
overscan?: number;
|
|
12
|
+
follow?: boolean;
|
|
13
|
+
onFollowChange?: (follow: boolean) => void;
|
|
14
|
+
paddingStart?: number;
|
|
15
|
+
paddingEnd?: number;
|
|
16
|
+
className?: string;
|
|
17
|
+
itemClassName?: string;
|
|
18
|
+
empty?: ReactNode;
|
|
19
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useVirtualizer } from '@tanstack/react-virtual';
|
|
3
|
+
import { Effect, Fiber } from 'effect';
|
|
4
|
+
import { ArrowDown } from 'lucide-react';
|
|
5
|
+
import { useCallback, useEffect, useRef, useState, } from 'react';
|
|
6
|
+
import { cn } from '../lib/cn.js';
|
|
7
|
+
import { ScrollArea } from '../ui/scroll-area.js';
|
|
8
|
+
const FOLLOW_THRESHOLD = 24;
|
|
9
|
+
const USER_INPUT_WINDOW = 400;
|
|
10
|
+
/**
|
|
11
|
+
* A virtualized list that stays pinned to its newest item while following, stops following
|
|
12
|
+
* when the reader scrolls up, and resumes when they return to the bottom.
|
|
13
|
+
*/
|
|
14
|
+
export function FollowList({ items, getKey, renderItem, estimateSize, overscan = 8, follow: controlledFollow, onFollowChange, paddingStart = 4, paddingEnd = 4, className, itemClassName, empty, }) {
|
|
15
|
+
const [ownFollow, setOwnFollow] = useState(true);
|
|
16
|
+
const follow = controlledFollow ?? ownFollow;
|
|
17
|
+
const parentRef = useRef(null);
|
|
18
|
+
const followLock = useRef(false);
|
|
19
|
+
const lastScrollTop = useRef(0);
|
|
20
|
+
const lastInputAt = useRef(Number.NEGATIVE_INFINITY);
|
|
21
|
+
const setFollow = useCallback((next) => {
|
|
22
|
+
setOwnFollow(next);
|
|
23
|
+
onFollowChange?.(next);
|
|
24
|
+
}, [onFollowChange]);
|
|
25
|
+
const virtualizer = useVirtualizer({
|
|
26
|
+
count: items.length,
|
|
27
|
+
getScrollElement: () => parentRef.current,
|
|
28
|
+
estimateSize: () => estimateSize,
|
|
29
|
+
overscan,
|
|
30
|
+
paddingStart,
|
|
31
|
+
paddingEnd,
|
|
32
|
+
getItemKey: (index) => {
|
|
33
|
+
const item = items[index];
|
|
34
|
+
return item === undefined ? index : getKey(item, index);
|
|
35
|
+
},
|
|
36
|
+
// Avoid flushSync during ref measurement / follow-scroll under high event rates.
|
|
37
|
+
useFlushSync: false,
|
|
38
|
+
});
|
|
39
|
+
const totalSize = virtualizer.getTotalSize();
|
|
40
|
+
const viewportHeight = virtualizer.scrollRect?.height ?? 0;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const el = parentRef.current;
|
|
43
|
+
// Content that shrank to fit (e.g. after filtering) never scrolls, so resume following here.
|
|
44
|
+
if (!follow && el && el.scrollHeight <= el.clientHeight + 1) {
|
|
45
|
+
setFollow(true);
|
|
46
|
+
}
|
|
47
|
+
if (!follow || items.length === 0) {
|
|
48
|
+
return () => undefined;
|
|
49
|
+
}
|
|
50
|
+
followLock.current = true;
|
|
51
|
+
let followRelease;
|
|
52
|
+
const rafId = requestAnimationFrame(() => {
|
|
53
|
+
virtualizer.scrollToIndex(items.length - 1, { align: 'end' });
|
|
54
|
+
followRelease = Effect.runFork(Effect.sleep(50).pipe(Effect.andThen(Effect.sync(() => {
|
|
55
|
+
followLock.current = false;
|
|
56
|
+
}))));
|
|
57
|
+
});
|
|
58
|
+
return () => {
|
|
59
|
+
cancelAnimationFrame(rafId);
|
|
60
|
+
if (followRelease !== undefined) {
|
|
61
|
+
Effect.runFork(Fiber.interrupt(followRelease));
|
|
62
|
+
}
|
|
63
|
+
followLock.current = false;
|
|
64
|
+
};
|
|
65
|
+
}, [follow, items.length, setFollow, totalSize, viewportHeight, virtualizer]);
|
|
66
|
+
const noteInput = useCallback((event) => {
|
|
67
|
+
lastInputAt.current = event.timeStamp;
|
|
68
|
+
}, []);
|
|
69
|
+
const onScroll = useCallback((event) => {
|
|
70
|
+
const el = parentRef.current;
|
|
71
|
+
if (!el) {
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// Row measurement also moves scrollTop, so only an upward scroll right after reader input unfollows.
|
|
75
|
+
const movedUp = el.scrollTop < lastScrollTop.current - 1;
|
|
76
|
+
const fromReader = event.timeStamp - lastInputAt.current < USER_INPUT_WINDOW;
|
|
77
|
+
lastScrollTop.current = el.scrollTop;
|
|
78
|
+
if (followLock.current) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const atBottom = el.scrollHeight - el.scrollTop - el.clientHeight < FOLLOW_THRESHOLD;
|
|
82
|
+
if (follow && !atBottom && movedUp && fromReader) {
|
|
83
|
+
setFollow(false);
|
|
84
|
+
}
|
|
85
|
+
else if (!follow && atBottom) {
|
|
86
|
+
setFollow(true);
|
|
87
|
+
}
|
|
88
|
+
}, [follow, setFollow]);
|
|
89
|
+
return (_jsxs("div", { className: cn('relative min-h-0 flex-1', className), children: [_jsx(ScrollArea, { viewportRef: parentRef, className: "size-full", onViewportScroll: onScroll, onWheel: noteInput, onTouchMove: noteInput, onPointerDown: noteInput, onKeyDown: noteInput, children: items.length === 0 && empty !== undefined ? (empty) : (_jsx("div", { className: "relative w-full", style: { height: totalSize }, children: virtualizer.getVirtualItems().map((row) => {
|
|
90
|
+
const item = items[row.index];
|
|
91
|
+
if (item === undefined) {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
return (_jsx("div", { "data-index": row.index, ref: virtualizer.measureElement, className: cn('absolute top-0 left-0 w-full', itemClassName), style: { transform: `translateY(${row.start}px)` }, children: renderItem(item, row.index) }, row.key));
|
|
95
|
+
}) })) }), !follow && items.length > 0 ? (_jsxs("button", { type: "button", onClick: () => setFollow(true), className: "debugger-float-in bg-card/95 text-foreground border-border hover:bg-accent absolute bottom-3 left-1/2 z-20 flex h-7 -translate-x-1/2 items-center gap-1.5 rounded-full border px-3 text-[11px] font-medium shadow-lg shadow-black/30 backdrop-blur active:scale-[0.97]", children: [_jsx(ArrowDown, { className: "size-3" }), "Jump to latest"] })) : null] }));
|
|
96
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { JsonValue } from '@swirls/looms/core';
|
|
2
|
+
/**
|
|
3
|
+
* Collapsible, syntax-coloured JSON. Strings that hold JSON expand in place,
|
|
4
|
+
* wide nodes start collapsed, and very large arrays render in buckets.
|
|
5
|
+
*/
|
|
6
|
+
export declare function JsonTree({ value, defaultExpandDepth, copyable, className, }: {
|
|
7
|
+
value: JsonValue;
|
|
8
|
+
defaultExpandDepth?: number;
|
|
9
|
+
copyable?: boolean;
|
|
10
|
+
className?: string;
|
|
11
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Check, ChevronRight, Copy } from 'lucide-react';
|
|
3
|
+
import { memo, useMemo, useState } from 'react';
|
|
4
|
+
import { cn } from '../lib/cn.js';
|
|
5
|
+
import { jsonEntries, jsonNode, jsonPreview, parseStructuredJson } from '../lib/json.js';
|
|
6
|
+
/** Children past this count render in collapsed buckets so huge arrays never flood the DOM. */
|
|
7
|
+
const CHUNK_SIZE = 100;
|
|
8
|
+
/** Nodes wider than this start collapsed even inside the default expand depth. */
|
|
9
|
+
const AUTO_EXPAND_LIMIT = 50;
|
|
10
|
+
const STRING_CLAMP = 320;
|
|
11
|
+
function Chevron({ open }) {
|
|
12
|
+
return (_jsx(ChevronRight, { "aria-hidden": true, className: cn('ease-snappy size-3 transition-transform duration-100', open && 'rotate-90') }));
|
|
13
|
+
}
|
|
14
|
+
function Key({ name, index }) {
|
|
15
|
+
if (name === undefined) {
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
return (_jsxs("span", { className: cn('mr-1 shrink-0', index ? 'text-muted-foreground' : 'text-json-key'), children: [name, _jsx("span", { className: "text-muted-foreground", children: ":" })] }));
|
|
19
|
+
}
|
|
20
|
+
function StringValue({ value }) {
|
|
21
|
+
const [expanded, setExpanded] = useState(false);
|
|
22
|
+
const clamped = !expanded && value.length > STRING_CLAMP;
|
|
23
|
+
const shown = clamped ? value.slice(0, STRING_CLAMP) : value;
|
|
24
|
+
return (_jsxs("span", { className: "text-json-string", children: ["\"", shown, clamped ? (_jsxs("button", { type: "button", className: "text-muted-foreground hover:text-foreground ml-0.5 rounded-sm px-0.5 hover:underline", onClick: () => setExpanded(true), children: ["\u2026+", value.length - STRING_CLAMP] })) : null, "\""] }));
|
|
25
|
+
}
|
|
26
|
+
function Scalar({ node }) {
|
|
27
|
+
switch (node.kind) {
|
|
28
|
+
case 'string':
|
|
29
|
+
return _jsx(StringValue, { value: node.value });
|
|
30
|
+
case 'number':
|
|
31
|
+
return _jsx("span", { className: "text-json-number", children: String(node.value) });
|
|
32
|
+
case 'boolean':
|
|
33
|
+
return _jsx("span", { className: "text-json-boolean", children: String(node.value) });
|
|
34
|
+
case 'null':
|
|
35
|
+
return _jsx("span", { className: "text-json-null italic", children: "null" });
|
|
36
|
+
case 'object':
|
|
37
|
+
return _jsx("span", { className: "text-muted-foreground", children: '{}' });
|
|
38
|
+
case 'array':
|
|
39
|
+
return _jsx("span", { className: "text-muted-foreground", children: "[]" });
|
|
40
|
+
default: {
|
|
41
|
+
const exhaustive = node;
|
|
42
|
+
return exhaustive;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/** Leaf values flow inline so long strings wrap beneath their key instead of in a narrow column. */
|
|
47
|
+
function Leaf({ children, className }) {
|
|
48
|
+
return (_jsx("div", { className: cn('py-px pl-4 break-words whitespace-pre-wrap', className), children: children }));
|
|
49
|
+
}
|
|
50
|
+
function Branch({ label, summary, defaultOpen, children, }) {
|
|
51
|
+
const [open, setOpen] = useState(defaultOpen);
|
|
52
|
+
const [deep, setDeep] = useState(false);
|
|
53
|
+
const [generation, setGeneration] = useState(0);
|
|
54
|
+
function toggle(event) {
|
|
55
|
+
const next = !open;
|
|
56
|
+
setOpen(next);
|
|
57
|
+
// Alt-click opens or closes the whole subtree, like browser devtools.
|
|
58
|
+
if (event.altKey) {
|
|
59
|
+
setDeep(next);
|
|
60
|
+
setGeneration((value) => value + 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return (_jsxs("div", { children: [_jsxs("button", { type: "button", "aria-expanded": open, onClick: toggle, className: "hover:bg-accent/50 group/branch -mx-1 flex w-[calc(100%+0.5rem)] min-w-0 items-start gap-1 rounded-sm px-1 py-px text-left", children: [_jsx("span", { className: "text-muted-foreground group-hover/branch:text-foreground flex h-5 w-3 shrink-0 items-center", children: _jsx(Chevron, { open: open }) }), label, _jsx("span", { className: "text-muted-foreground min-w-0 truncate", children: summary })] }), open ? (_jsx("div", { className: "border-border/70 ml-1.5 border-l pl-2.5", children: children(deep) }, generation)) : null] }));
|
|
64
|
+
}
|
|
65
|
+
function Children({ node, depth, options, forceOpen, }) {
|
|
66
|
+
const entries = jsonEntries(node);
|
|
67
|
+
const index = node.kind === 'array';
|
|
68
|
+
if (entries.length <= CHUNK_SIZE) {
|
|
69
|
+
return entries.map(([key, value]) => (_jsx(Entry, { name: key, index: index, value: value, depth: depth, options: options, forceOpen: forceOpen }, key)));
|
|
70
|
+
}
|
|
71
|
+
const chunks = [];
|
|
72
|
+
for (let start = 0; start < entries.length; start += CHUNK_SIZE) {
|
|
73
|
+
chunks.push(entries.slice(start, start + CHUNK_SIZE));
|
|
74
|
+
}
|
|
75
|
+
return chunks.map((chunk, chunkIndex) => {
|
|
76
|
+
const first = chunkIndex * CHUNK_SIZE;
|
|
77
|
+
const last = first + chunk.length - 1;
|
|
78
|
+
return (_jsx(Branch, { defaultOpen: forceOpen, label: _jsxs("span", { className: "text-muted-foreground shrink-0", children: ["[", first, " \u2026 ", last, "]"] }), summary: null, children: (deep) => chunk.map(([key, value]) => (_jsx(Entry, { name: key, index: index, value: value, depth: depth, options: options, forceOpen: deep }, key))) }, first));
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
const Entry = memo(function Entry({ name, index = false, value, depth, options, forceOpen = false, }) {
|
|
82
|
+
const node = useMemo(() => jsonNode(value), [value]);
|
|
83
|
+
const embedded = useMemo(() => (node.kind === 'string' ? parseStructuredJson(node.value) : undefined), [node]);
|
|
84
|
+
const branch = embedded !== undefined
|
|
85
|
+
? jsonNode(embedded)
|
|
86
|
+
: node.kind === 'object' || node.kind === 'array'
|
|
87
|
+
? node
|
|
88
|
+
: undefined;
|
|
89
|
+
const size = branch ? jsonEntries(branch).length : 0;
|
|
90
|
+
if (!branch || size === 0) {
|
|
91
|
+
return (_jsxs(Leaf, { children: [_jsx(Key, { name: name, index: index }), _jsx(Scalar, { node: branch ?? node })] }));
|
|
92
|
+
}
|
|
93
|
+
const defaultOpen = forceOpen ||
|
|
94
|
+
(embedded === undefined && depth < options.defaultExpandDepth && size <= AUTO_EXPAND_LIMIT);
|
|
95
|
+
return (_jsx(Branch, { defaultOpen: defaultOpen, label: _jsxs(_Fragment, { children: [_jsx(Key, { name: name, index: index }), embedded !== undefined ? (_jsx("span", { className: "border-border text-muted-foreground mt-0.5 shrink-0 rounded-sm border px-1 text-[9px] leading-3.5 uppercase", title: "String containing JSON", children: "json" })) : null] }), summary: jsonPreview(branch), children: (deep) => _jsx(Children, { node: branch, depth: depth + 1, options: options, forceOpen: deep }) }));
|
|
96
|
+
});
|
|
97
|
+
function CopyButton({ value }) {
|
|
98
|
+
const [copied, setCopied] = useState(false);
|
|
99
|
+
return (_jsx("button", { type: "button", "aria-label": "Copy JSON", className: "text-muted-foreground hover:text-foreground bg-card/90 hover:bg-accent border-border absolute top-0 right-0 flex size-6 items-center justify-center rounded-md border opacity-0 transition-opacity duration-150 group-hover/json:opacity-100 focus-visible:opacity-100", onClick: () => {
|
|
100
|
+
void navigator.clipboard
|
|
101
|
+
.writeText(JSON.stringify(value, null, 2))
|
|
102
|
+
.then(() => setCopied(true));
|
|
103
|
+
}, onPointerLeave: () => setCopied(false), children: copied ? _jsx(Check, { className: "size-3" }) : _jsx(Copy, { className: "size-3" }) }));
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Collapsible, syntax-coloured JSON. Strings that hold JSON expand in place,
|
|
107
|
+
* wide nodes start collapsed, and very large arrays render in buckets.
|
|
108
|
+
*/
|
|
109
|
+
export function JsonTree({ value, defaultExpandDepth = 1, copyable = true, className, }) {
|
|
110
|
+
const options = useMemo(() => ({ defaultExpandDepth }), [defaultExpandDepth]);
|
|
111
|
+
const { root, source } = useMemo(() => {
|
|
112
|
+
const node = jsonNode(value);
|
|
113
|
+
const embedded = node.kind === 'string' ? parseStructuredJson(node.value) : undefined;
|
|
114
|
+
return embedded !== undefined
|
|
115
|
+
? { root: jsonNode(embedded), source: embedded }
|
|
116
|
+
: { root: node, source: value };
|
|
117
|
+
}, [value]);
|
|
118
|
+
const hasChildren = (root.kind === 'object' || root.kind === 'array') && jsonEntries(root).length > 0;
|
|
119
|
+
return (_jsxs("div", { className: cn('group/json relative min-w-0 font-mono text-[11px] leading-5', className), children: [hasChildren ? (_jsx(Children, { node: root, depth: 0, options: options, forceOpen: false })) : (_jsx(Leaf, { className: "pl-0", children: _jsx(Scalar, { node: root }) })), copyable ? _jsx(CopyButton, { value: source }) : null] }));
|
|
120
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { JsonTree } from './json-tree.js';
|
|
3
3
|
export function JsonView({ value, className }) {
|
|
4
|
-
return
|
|
4
|
+
return _jsx(JsonTree, { value: value, className: className });
|
|
5
5
|
}
|