jev-devtools 0.1.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/README.md +5 -3
- package/THIRD_PARTY_NOTICES.txt +27 -0
- package/lib/server/index.js +78 -8
- package/lib/server/recorder.d.ts +8 -1
- package/lib/server/recorder.js +35 -8
- package/lib/src/ChoiceProbabilities.d.ts +4 -0
- package/lib/src/ChoiceProbabilities.js +19 -0
- package/lib/src/ConnectedInspector.js +37 -29
- package/lib/src/JevDevtools.js +37 -47
- package/lib/src/JevDrawer.d.ts +4 -3
- package/lib/src/JevDrawer.js +33 -6
- package/lib/src/Questions.d.ts +5 -0
- package/lib/src/Questions.js +174 -0
- package/lib/src/TraceContent.d.ts +11 -0
- package/lib/src/TraceContent.js +32 -0
- package/lib/src/TraceDiagram.js +99 -38
- package/lib/src/diagram-camera.d.ts +16 -0
- package/lib/src/diagram-camera.js +20 -0
- package/lib/src/flow-model.js +1 -1
- package/lib/src/index.d.ts +1 -1
- package/lib/src/inspector-styles.d.ts +2 -0
- package/lib/src/inspector-styles.js +1 -0
- package/lib/src/inspector.css +280 -7
- package/lib/src/protocol.d.ts +13 -0
- package/lib/src/trace-store.d.ts +6 -0
- package/lib/src/trace-store.js +66 -0
- package/lib/src/trace-stream.d.ts +2 -0
- package/lib/src/trace-stream.js +65 -0
- package/lib/src/useDiagramViewport.d.ts +10 -0
- package/lib/src/useDiagramViewport.js +183 -0
- package/package.json +3 -2
- package/skills/setup-jev-devtools/references/verify.md +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const inspectorStyles = ".jev-devtools {\n all: initial;\n display: block;\n direction: ltr;\n unicode-bidi: isolate;\n pointer-events: auto;\n color-scheme: dark;\n font:\n 12px system-ui,\n sans-serif;\n color: #eee8ed;\n line-height: 1.5;\n}\n.jev-devtools,\n.jev-devtools * {\n box-sizing: border-box;\n}\n.jev-devtools button,\n.jev-devtools input,\n.jev-devtools select,\n.jev-devtools textarea {\n font: inherit;\n color: inherit;\n border: 1px solid #3b303d;\n border-radius: 5px;\n background: #17121b;\n}\n.jev-devtools button {\n padding: 7px 11px;\n cursor: pointer;\n}\n.jev-devtools button:disabled {\n opacity: 0.45;\n cursor: default;\n}\n.jev-devtools input,\n.jev-devtools select {\n padding: 8px;\n width: 100%;\n}\n.jev-devtools button:hover:not(:disabled) {\n border-color: #e551ba;\n}\n.jev-devtools :is(button, input, select, textarea, summary):focus-visible {\n outline: 2px solid #e551ba;\n outline-offset: 3px;\n}\n.jev-devtools .eyebrow {\n font-size: 10px;\n letter-spacing: 0.12em;\n color: #bc9db5;\n text-transform: uppercase;\n}\n.jev-devtools .jev-inspector {\n height: 100%;\n display: flex;\n color: #eee8ed;\n background: #0d0a0f;\n font:\n 12px \"DM Sans\",\n sans-serif;\n min-height: 0;\n overflow: hidden;\n}\n.jev-devtools .jev-inspector h2 {\n font-size: 15px;\n margin: 0;\n font-weight: 600;\n}\n.jev-devtools .jev-inspector h3 {\n font-size: 13px;\n margin: 0 0 8px;\n}\n.jev-devtools .jev-inspector code,\n.jev-devtools .jev-inspector pre {\n font:\n 11px \"JetBrains Mono\",\n monospace;\n}\n.jev-devtools .jev-inspector pre {\n line-height: 1.7;\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n max-height: 460px;\n overflow: auto;\n background: #100d13;\n padding: 16px;\n border: 1px solid #2e2533;\n border-radius: 5px;\n}\n.jev-devtools .jev-inspector .muted {\n color: #a89bac;\n font-size: 11px;\n line-height: 1.6;\n}\n.jev-devtools .request-list {\n flex: 0 0 300px;\n border-right: 1px solid #302634;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n.jev-devtools .request-list > header {\n padding: 17px 14px;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n.jev-devtools .request-list h2 span {\n font-size: 11px;\n color: #b796ad;\n margin-left: 5px;\n}\n.jev-devtools .request-list button {\n font-size: 11px;\n}\n.jev-devtools .list-filters {\n display: flex;\n gap: 7px;\n padding: 0 12px 12px;\n}\n.jev-devtools .list-filters input {\n min-width: 0;\n}\n.jev-devtools .list-filters select {\n width: 86px;\n font-size: 11px;\n}\n.jev-devtools .request-items {\n overflow: auto;\n flex: 1;\n}\n.jev-devtools .request-row {\n display: flex;\n align-items: center;\n gap: 8px;\n width: 100%;\n text-align: left;\n border: 0;\n border-top: 1px solid #251d2a;\n border-radius: 0;\n background: transparent;\n padding: 16px 12px;\n}\n.jev-devtools .request-row.selected {\n background: #251423;\n box-shadow: inset 2px 0 #e551ba;\n}\n.jev-devtools .request-row strong {\n display: block;\n font-size: 12px;\n font-weight: 500;\n}\n.jev-devtools .request-row small {\n display: block;\n font-size: 10px;\n color: #a99aac;\n margin-top: 6px;\n}\n.jev-devtools .row-status {\n font-size: 9px;\n color: #ad9bb0;\n margin-left: auto;\n}\n.jev-devtools .status-dot {\n width: 6px;\n height: 6px;\n display: inline-block;\n border-radius: 50%;\n background: #b09ab7;\n flex-shrink: 0;\n}\n.jev-devtools .status-dot.success {\n background: #b3c79c;\n}\n.jev-devtools .status-dot.pending {\n background: #e551ba;\n}\n.jev-devtools .status-dot.error {\n background: #ee8e85;\n}\n.jev-devtools .request-list footer {\n padding: 12px;\n color: #9e8ea5;\n border-top: 1px solid #302634;\n font-size: 10px;\n display: flex;\n gap: 7px;\n}\n.jev-devtools .request-detail {\n flex: 1;\n min-width: 0;\n min-height: 0;\n display: flex;\n flex-direction: column;\n}\n.jev-devtools .detail-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 20px;\n padding: 18px 24px 10px;\n}\n.jev-devtools .detail-header h2 {\n margin-top: 6px;\n}\n.jev-devtools .metrics {\n display: flex;\n gap: 24px;\n font:\n 12px \"JetBrains Mono\",\n monospace;\n white-space: nowrap;\n}\n.jev-devtools .metrics small {\n display: block;\n font:\n 9px \"DM Sans\",\n sans-serif;\n color: #97879f;\n margin-top: 6px;\n}\n.jev-devtools .detail-meta {\n display: flex;\n gap: 20px;\n flex-wrap: wrap;\n padding: 0 24px 14px;\n color: #a08aa5;\n}\n.jev-devtools .detail-meta code {\n font-size: 10px;\n}\n.jev-devtools .detail-tabs {\n display: flex;\n gap: 20px;\n border-bottom: 1px solid #302634;\n padding: 0 24px;\n}\n.jev-devtools .detail-tabs button {\n border: 0;\n border-radius: 0;\n background: none;\n padding: 10px 0;\n color: #a89bac;\n font-size: 11px;\n}\n.jev-devtools .detail-tabs button[aria-pressed=\"true\"] {\n box-shadow: inset 0 -2px #e551ba;\n color: #f5c1e6;\n}\n.jev-devtools .detail-body {\n overflow: auto;\n flex: 1;\n min-height: 0;\n padding: 16px 24px 28px;\n background-image: radial-gradient(#342337 0.6px, transparent 0.6px);\n background-size: 16px 16px;\n}\n.jev-devtools .request-flow {\n display: flex;\n align-items: center;\n gap: 40px;\n padding: 14px 0 20px;\n}\n.jev-devtools .state-node {\n position: relative;\n flex: 0 0 160px;\n border: 1px solid #e551ba;\n padding: 20px 15px;\n background: #1f1320;\n border-radius: 6px;\n}\n.jev-devtools .state-node h3 {\n margin-top: 12px;\n}\n.jev-devtools .state-node p {\n color: #b69db3;\n line-height: 1.5;\n}\n.jev-devtools .state-node code {\n font-size: 10px;\n}\n.jev-devtools .state-node:after {\n content: \"\";\n position: absolute;\n left: 100%;\n top: 50%;\n width: 20px;\n border-top: 1px solid #7f416d;\n}\n.jev-devtools .question-branches {\n border-left: 1px solid #7f416d;\n padding-left: 20px;\n display: grid;\n grid-template-columns: repeat(3, minmax(160px, 1fr));\n gap: 12px;\n flex: 1;\n position: relative;\n}\n.jev-devtools .question-node {\n position: relative;\n border: 1px solid #75516c;\n border-radius: 6px;\n padding: 15px;\n background: #110d14;\n min-width: 0;\n}\n.jev-devtools .question-node:before {\n content: \"\";\n position: absolute;\n right: 100%;\n top: 50%;\n width: 21px;\n border-top: 1px solid #7f416d;\n}\n.jev-devtools .question-branches .question-node:not(:first-child):before {\n display: none;\n}\n.jev-devtools .question-node header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n.jev-devtools .question-node header h3 {\n margin: 0;\n overflow-wrap: anywhere;\n}\n.jev-devtools .type {\n font:\n 9px \"JetBrains Mono\",\n monospace;\n color: #e59acc;\n border: 1px solid #5e3654;\n border-radius: 3px;\n padding: 3px 5px;\n}\n.jev-devtools .instructions {\n font-size: 11px;\n color: #b8a6bc;\n line-height: 1.5;\n}\n.jev-devtools .answer-value {\n display: block;\n font-size: 20px;\n font-weight: 500;\n margin: 15px 0;\n color: #f0c7e5;\n}\n.jev-devtools .answer-value small {\n font-size: 10px;\n color: #b5a1ba;\n}\n.jev-devtools .probability {\n margin: 10px 0;\n font-size: 10px;\n}\n.jev-devtools .probability > div:first-child {\n display: flex;\n justify-content: space-between;\n gap: 5px;\n margin-bottom: 5px;\n}\n.jev-devtools .probability b {\n font-weight: 400;\n color: #d8abc9;\n}\n.jev-devtools .bar {\n height: 4px;\n background: #302033;\n border-radius: 2px;\n overflow: hidden;\n}\n.jev-devtools .bar i {\n display: block;\n height: 100%;\n background: #e551ba;\n}\n.jev-devtools .confidence {\n font-size: 10px;\n color: #a68cac;\n border-top: 1px solid #302634;\n padding-top: 10px;\n display: flex;\n justify-content: space-between;\n}\n.jev-devtools .confidence b {\n color: #d1bad2;\n font-weight: 400;\n}\n.jev-devtools details {\n margin-top: 12px;\n font-size: 10px;\n color: #b7a0bd;\n}\n.jev-devtools summary {\n cursor: pointer;\n}\n.jev-devtools .standalone {\n margin-bottom: 14px;\n max-width: 750px;\n}\n.jev-devtools .standalone:before {\n display: none;\n}\n.jev-devtools .standalone .answer {\n max-width: 450px;\n}\n.jev-devtools .box-preview {\n position: relative;\n margin-top: 14px;\n min-height: 80px;\n background: #19121c;\n}\n.jev-devtools .box-preview img {\n width: 100%;\n display: block;\n}\n.jev-devtools .bounding-box {\n position: absolute;\n border: 1.5px solid #e551ba;\n background: #e551ba15;\n}\n.jev-devtools .coordinates {\n display: block;\n font-size: 9px !important;\n line-height: 1.5;\n margin-top: 8px;\n color: #ac93b0;\n}\n.jev-devtools .context-image {\n max-width: 400px;\n width: 100%;\n border: 1px solid #75516c;\n border-radius: 6px;\n}\n.jev-devtools .empty {\n padding: 20px;\n color: #a294a8;\n line-height: 1.7;\n}\n.jev-devtools .empty-detail {\n margin: auto;\n padding: 40px;\n text-align: center;\n color: #b2a0ba;\n}\n.jev-devtools .empty-symbol {\n font-size: 36px;\n color: #e551ba;\n display: block;\n margin-bottom: 20px;\n}\n.jev-devtools .empty-detail p {\n font-size: 12px;\n line-height: 1.8;\n}\n.jev-devtools .error-banner {\n border: 1px solid #874b50;\n background: #31191e;\n padding: 12px;\n line-height: 1.5;\n margin-bottom: 16px;\n}\n.jev-devtools .warning {\n color: #e2b97d;\n}\n.jev-devtools .jev-inspector table {\n border-collapse: collapse;\n width: 100%;\n text-align: left;\n}\n.jev-devtools .jev-inspector th,\n.jev-devtools .jev-inspector td {\n border-bottom: 1px solid #302634;\n padding: 14px;\n}\n.jev-devtools .flow-dot {\n display: none;\n}\n.jev-devtools .pending .flow-dot {\n display: block;\n position: absolute;\n right: -23px;\n top: calc(50% - 3px);\n width: 6px;\n height: 6px;\n background: #e551ba;\n border-radius: 50%;\n animation: jev-inspector-pulse 1s infinite;\n}\n@keyframes jev-inspector-pulse {\n 50% {\n opacity: 0.2;\n transform: scale(0.7);\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .jev-devtools .pending .flow-dot {\n animation: none;\n }\n}\n@media (max-width: 1100px) {\n .jev-devtools .question-branches {\n grid-template-columns: 1fr;\n }\n .jev-devtools .question-branches .question-node:not(:first-child):before {\n display: block;\n }\n .jev-devtools .request-flow {\n align-items: flex-start;\n }\n .jev-devtools .state-node {\n margin-top: 45px;\n }\n .jev-devtools .metrics {\n gap: 12px;\n }\n .jev-devtools .request-list {\n flex-basis: 250px;\n }\n}\n@media (max-width: 700px) {\n .jev-devtools .jev-inspector {\n flex-direction: column;\n }\n .jev-devtools .request-list {\n flex-basis: 140px;\n flex-shrink: 0;\n border-right: 0;\n border-bottom: 1px solid #302634;\n }\n .jev-devtools .request-list > header {\n padding: 6px 12px;\n }\n .jev-devtools .request-list footer {\n display: none;\n }\n .jev-devtools .list-filters {\n padding-bottom: 6px;\n }\n .jev-devtools .request-items {\n display: flex;\n }\n .jev-devtools .request-row {\n min-width: 240px;\n padding: 8px 12px;\n }\n .jev-devtools .request-detail {\n min-height: 0;\n }\n .jev-devtools .detail-header {\n padding: 12px;\n }\n .jev-devtools .detail-meta,\n .jev-devtools .detail-tabs {\n padding-left: 12px;\n padding-right: 12px;\n }\n .jev-devtools .detail-body {\n padding: 12px;\n }\n .jev-devtools .metrics {\n display: none;\n }\n .jev-devtools .state-node {\n flex-basis: 115px;\n padding: 12px;\n }\n .jev-devtools .request-flow {\n gap: 25px;\n }\n .jev-devtools .state-node:after {\n width: 12px;\n }\n .jev-devtools .question-branches {\n padding-left: 12px;\n }\n .jev-devtools .question-node:before {\n width: 13px;\n }\n .jev-devtools .question-node {\n padding: 12px;\n }\n}\n\n.jev-devtools .request-flow {\n flex-direction: column;\n align-items: stretch;\n gap: 24px;\n}\n.jev-devtools .state-node {\n flex: auto;\n width: fit-content;\n align-self: center;\n padding: 12px 24px;\n text-align: center;\n margin: 0;\n}\n.jev-devtools .state-node h3 {\n display: inline-block;\n margin: 0 12px;\n}\n.jev-devtools .state-node p {\n display: inline-block;\n margin: 0 12px 0 0;\n font-size: 11px;\n}\n.jev-devtools .state-node .eyebrow {\n display: none;\n}\n.jev-devtools .state-node:after {\n left: 50%;\n top: 100%;\n width: 0;\n height: 24px;\n border-top: 0;\n border-left: 1px solid #7f416d;\n}\n.jev-devtools .question-branches {\n border-left: 0;\n border-top: 1px solid #7f416d;\n padding: 20px 0 0;\n grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));\n}\n.jev-devtools .question-node:before,\n.jev-devtools .question-branches .question-node:not(:first-child):before {\n display: block;\n right: auto;\n left: 50%;\n top: -21px;\n width: 0;\n height: 20px;\n border-top: 0;\n border-left: 1px solid #7f416d;\n}\n.jev-devtools .standalone:before {\n display: none;\n}\n.jev-devtools .flow-caption {\n margin-top: 0;\n}\n.jev-devtools .pending .flow-dot {\n right: auto;\n left: calc(50% - 3px);\n top: calc(100% + 10px);\n}\n@media (max-width: 900px) {\n .jev-devtools .question-branches {\n grid-template-columns: 1fr;\n }\n .jev-devtools .question-node:before,\n .jev-devtools .question-branches .question-node:not(:first-child):before {\n height: 12px;\n top: -13px;\n }\n .jev-devtools .question-branches .question-node:first-child:before {\n height: 20px;\n top: -21px;\n }\n .jev-devtools .state-node {\n padding: 10px;\n }\n .jev-devtools .state-node p {\n display: block;\n margin: 6px 0;\n }\n .jev-devtools .state-node h3 {\n font-size: 12px;\n }\n}\n\n.jev-devtools.jev-drawer {\n margin: 0;\n padding: 0;\n position: fixed;\n z-index: 1000;\n inset: auto 0 0;\n display: flex;\n flex-direction: column;\n background: #0d0a0f;\n color: #eee8ed;\n border-top: 1px solid #604058;\n box-shadow: 0 -12px 48px #0005;\n font:\n 12px \"DM Sans\",\n sans-serif;\n}\n.jev-devtools.jev-drawer[hidden],\n.jev-devtools.jev-drawer-trigger[hidden] {\n display: none;\n}\n.jev-devtools .jev-drawer-resize {\n position: absolute;\n z-index: 1;\n top: -6px;\n width: 100%;\n height: 12px;\n cursor: ns-resize;\n touch-action: none;\n display: flex;\n justify-content: center;\n align-items: center;\n}\n.jev-devtools .jev-drawer-resize span {\n width: 42px;\n height: 3px;\n border-radius: 3px;\n background: #79506f;\n}\n.jev-devtools .jev-drawer-resize:hover span,\n.jev-devtools .jev-drawer-resize:focus-visible span {\n background: #e551ba;\n}\n.jev-devtools .jev-drawer-resize:focus-visible {\n outline: 2px solid #e551ba;\n outline-offset: -2px;\n}\n.jev-devtools .jev-drawer-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex: 0 0 46px;\n padding: 0 16px;\n border-bottom: 1px solid #302634;\n}\n.jev-devtools .jev-drawer-header strong {\n display: flex;\n align-items: center;\n gap: 9px;\n font-weight: 600;\n}\n.jev-devtools .jev-mark {\n color: #e551ba;\n font-size: 21px;\n}\n.jev-devtools .jev-drawer-header > div {\n display: flex;\n align-items: center;\n gap: 10px;\n}\n.jev-devtools .jev-drawer-header button {\n background: transparent;\n border-color: transparent;\n font-size: 11px;\n color: #c4afc8;\n}\n.jev-devtools .jev-drawer-header .jev-drawer-close {\n font-size: 22px;\n line-height: 1;\n padding: 4px 8px;\n}\n.jev-devtools .jev-drawer-content {\n flex: 1;\n min-height: 0;\n overflow: hidden;\n}\n.jev-devtools.jev-drawer-trigger {\n position: fixed;\n bottom: 18px;\n right: 18px;\n z-index: 1000;\n display: flex;\n align-items: center;\n gap: 9px;\n padding: 10px 16px;\n border: 1px solid #a24487;\n border-radius: 8px;\n cursor: pointer;\n background: #211321;\n color: #f1cbe6;\n font:\n 600 13px \"DM Sans\",\n sans-serif;\n box-shadow: 0 4px 20px #0005;\n}\n.jev-devtools.jev-drawer-trigger span {\n color: #e551ba;\n font-size: 20px;\n}\n/* Baseline cards and authored left-to-right composition from the lab's Jev presets. */\n.jev-devtools .detail-body.detail-body-flow {\n display: flex;\n padding: 0;\n overflow: hidden;\n}\n.jev-devtools .lab-flow {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-width: 0;\n min-height: 0;\n}\n.jev-devtools .lab-flow-toolbar {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 12px;\n flex-shrink: 0;\n padding: 12px 16px;\n flex-wrap: wrap;\n}\n.jev-devtools .lab-flow-toolbar label {\n display: flex;\n gap: 10px;\n align-items: center;\n font-size: 11px;\n min-width: 0;\n}\n.jev-devtools .lab-flow-toolbar select {\n width: auto;\n max-width: 240px;\n font-size: 11px;\n}\n.jev-devtools .lab-flow-toolbar > div {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n.jev-devtools .lab-flow-toolbar button {\n font-size: 11px;\n}\n.jev-devtools .lab-flow-toolbar output {\n font-size: 11px;\n min-width: 35px;\n text-align: center;\n}\n.jev-devtools .lab-flow-viewport {\n position: relative;\n flex: 1;\n min-height: 100px;\n overflow: hidden;\n background-color: #0d0a0f;\n background-image: radial-gradient(#463449 0.8px, transparent 0.8px);\n background-size: 20px 20px;\n border-block: 1px solid #302634;\n cursor: grab;\n touch-action: none;\n user-select: none;\n overscroll-behavior: contain;\n}\n.jev-devtools .lab-flow-viewport[data-panning] {\n cursor: grabbing;\n}\n.jev-devtools .lab-flow-viewport:focus-visible {\n outline: 2px solid #e551ba;\n outline-offset: -2px;\n}\n.jev-devtools .lab-flow-caption {\n flex-shrink: 0;\n padding: 8px 16px;\n margin: 0;\n}\n.jev-devtools .lab-gesture-hint {\n display: block;\n color: #c6a7bd;\n}\n.jev-devtools .lab-flow-canvas {\n position: absolute;\n inset: 0 auto auto 0;\n transform-origin: top left;\n will-change: transform;\n font-family: system-ui, sans-serif;\n color: #f5f3f6;\n}\n.jev-devtools .lab-connections {\n position: absolute;\n inset: 0;\n overflow: visible;\n}\n.jev-devtools .lab-connections path {\n fill: none;\n stroke: #645a68;\n stroke-width: 1;\n}\n.jev-devtools .lab-connections path.active {\n stroke: #968592;\n}\n.jev-devtools .lab-connections text {\n fill: #b7b1bc;\n font:\n 12px ui-monospace,\n monospace;\n}\n.jev-devtools .lab-flow-dot {\n fill: #e551ba;\n}\n.jev-devtools .lab-card {\n position: absolute;\n border: 1.3px solid #f5f3f6;\n border-radius: 14px;\n padding: 20px;\n background: #0d0a0f;\n overflow: hidden;\n}\n.jev-devtools .lab-eyebrow {\n display: block;\n color: #e551ba;\n font:\n 500 10px ui-monospace,\n monospace;\n letter-spacing: 1px;\n margin: 0 0 10px;\n}\n.jev-devtools .lab-card h3 {\n font-size: 18px;\n font-weight: 500;\n line-height: 1.25;\n margin: 0 0 16px;\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.jev-devtools .lab-card p {\n font-size: 15px;\n line-height: 1.4;\n margin: 10px 0;\n}\n.jev-devtools .lab-copy {\n display: -webkit-box;\n -webkit-line-clamp: 5;\n -webkit-box-orient: vertical;\n overflow: hidden;\n overflow-wrap: anywhere;\n}\n.jev-devtools .lab-probability {\n margin: 17px 0;\n}\n.jev-devtools .lab-probability > div:first-child {\n display: flex;\n justify-content: space-between;\n gap: 10px;\n margin-bottom: 9px;\n font-size: 15px;\n}\n.jev-devtools .lab-probability span {\n overflow: hidden;\n white-space: nowrap;\n text-overflow: ellipsis;\n}\n.jev-devtools .lab-probability code {\n font:\n 13px ui-monospace,\n monospace;\n}\n.jev-devtools .lab-track {\n height: 7px;\n border-radius: 4px;\n background: #f5f3f61a;\n overflow: hidden;\n}\n.jev-devtools .lab-track i {\n display: block;\n height: 100%;\n border-radius: 4px;\n background: #e551ba;\n}\n.jev-devtools .lab-track i.lab-alternative {\n opacity: 0.35;\n}\n.jev-devtools .lab-card .lab-result {\n font-size: 14px;\n margin-top: 24px;\n}\n.jev-devtools .lab-card .lab-small {\n font-size: 12px;\n color: #b7b1bc;\n margin-top: 20px;\n}\n.jev-devtools .lab-idle {\n opacity: 0.65;\n}\n.jev-devtools .lab-scale {\n position: relative;\n margin-top: 18px;\n border-top: 1px solid #b7b1bc;\n display: flex;\n justify-content: space-between;\n padding-top: 9px;\n}\n.jev-devtools .lab-scale > span {\n position: absolute;\n top: -5px;\n width: 9px;\n height: 9px;\n border-radius: 50%;\n background: #e551ba;\n transform: translateX(-50%);\n}\n.jev-devtools .lab-big {\n display: block;\n font-size: 36px;\n font-weight: 500;\n margin-top: 26px;\n}\n.jev-devtools .lab-endpoints {\n display: flex;\n justify-content: space-between;\n font-size: 12px;\n color: #b7b1bc;\n margin-top: 10px;\n}\n.jev-devtools .lab-image {\n position: relative;\n}\n.jev-devtools .lab-image img {\n display: block;\n width: 100%;\n}\n.jev-devtools .lab-box {\n position: absolute;\n border: 1.5px solid #e551ba;\n background: #e551ba15;\n}\n.jev-devtools .lab-source-image {\n width: 100%;\n max-height: 105px;\n object-fit: contain;\n}\n.jev-devtools .lab-coordinates {\n font-family: ui-monospace, monospace;\n}\n@media (prefers-reduced-motion: reduce) {\n .jev-devtools .lab-flow-dot {\n display: none;\n }\n}\n/* The public TypeSafe mark travels with the package, independent of host assets. */\n.jev-devtools .typesafe-mark {\n display: inline-block;\n flex: 0 0 23px;\n width: 23px;\n height: 32px;\n overflow: hidden;\n line-height: 0;\n}\n.jev-devtools .typesafe-mark img {\n display: block;\n height: 32px;\n width: auto;\n max-width: none;\n}\n\n.jev-devtools .detail-body-questions {\n display: flex;\n flex-direction: column;\n overflow: auto;\n}\n.jev-devtools .questions-list {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 180px;\n}\n.jev-devtools .questions-navigation {\n display: flex;\n align-items: end;\n gap: 8px;\n flex-wrap: wrap;\n flex-shrink: 0;\n}\n.jev-devtools .questions-navigation label {\n display: flex;\n align-items: center;\n gap: 8px;\n white-space: nowrap;\n flex: 1;\n min-width: 240px;\n max-width: 480px;\n font-size: 11px;\n color: #bc9db5;\n}\n.jev-devtools .questions-navigation select {\n display: block;\n min-width: 0;\n}\n.jev-devtools .questions-navigation > span {\n align-self: center;\n color: #a89bac;\n}\n.jev-devtools .questions-help {\n margin: 8px 0;\n flex-shrink: 0;\n}\n.jev-devtools .questions-viewport {\n flex: 1;\n min-height: 0;\n overflow: auto;\n overflow-anchor: none;\n scrollbar-gutter: stable;\n padding: 3px;\n}\n.jev-devtools .questions-spacer {\n position: relative;\n width: 100%;\n}\n.jev-devtools .questions-row {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n padding-bottom: 14px;\n}\n.jev-devtools .questions-row .standalone {\n margin-bottom: 0;\n}\n.jev-devtools .questions-viewport:focus-visible,\n.jev-devtools .questions-row article:focus-visible {\n outline: 2px solid #e551ba;\n outline-offset: -2px;\n}\n\n/* Reserve measured card geometry while rapid scrolling uses a light preview. */\n.jev-devtools .questions-row[data-preview=\"true\"] article {\n height: 100%;\n}\n.jev-devtools .question-scroll-preview p {\n margin-top: 16px;\n}\n\n/* The full distribution stays at reading size, outside the zoomable canvas. */\n.jev-devtools .lab-flow-viewport[hidden] {\n display: none;\n}\n.jev-devtools .lab-choice-browser {\n display: flex;\n flex-direction: column;\n flex: 1;\n min-height: 0;\n padding: 8px 16px;\n gap: 10px;\n}\n.jev-devtools .lab-choice-controls {\n display: flex;\n gap: 12px;\n align-items: end;\n flex-wrap: wrap;\n font-size: 12px;\n}\n.jev-devtools .lab-choice-controls label {\n display: grid;\n gap: 5px;\n}\n.jev-devtools .lab-choice-table-scroll {\n overflow: auto;\n min-height: 0;\n flex: 1;\n}\n.jev-devtools .lab-choice-browser table {\n width: 100%;\n border-collapse: collapse;\n font-size: 13px;\n}\n.jev-devtools .lab-choice-browser caption {\n text-align: left;\n padding: 8px 0;\n color: #aaa0af;\n}\n.jev-devtools .lab-choice-browser th,\n.jev-devtools .lab-choice-browser td {\n padding: 9px 12px;\n border-bottom: 1px solid #302634;\n text-align: left;\n}\n.jev-devtools .lab-choice-browser th {\n overflow-wrap: anywhere;\n max-width: 320px;\n}\n.jev-devtools .lab-choice-browser td {\n font-variant-numeric: tabular-nums;\n}\n.jev-devtools .lab-choice-browser code {\n white-space: normal;\n overflow-wrap: anywhere;\n}\n.jev-devtools .lab-choice-returned {\n display: block;\n font-size: 10px;\n color: #e551ba;\n}\n.jev-devtools .lab-choice-pagination {\n display: flex;\n align-items: center;\n gap: 12px;\n justify-content: space-between;\n font-size: 12px;\n}\n.jev-devtools .lab-choice-remainder button {\n margin-top: 7px;\n}\n\n.jev-devtools .lab-flow-compact .lab-output {\n padding: 12px;\n height: 104px;\n}\n.jev-devtools .lab-flow-compact .lab-output .lab-eyebrow {\n margin-bottom: 6px;\n}\n.jev-devtools .lab-flow-compact .lab-output h3 {\n font-size: 16px;\n margin-bottom: 8px;\n}\n.jev-devtools .lab-flow-compact .lab-output p {\n font-size: 12px;\n margin: 4px 0;\n}\n.jev-devtools .lab-flow-compact .lab-result {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n overflow-wrap: anywhere;\n}\n.jev-devtools .lab-choice-remainder {\n padding: 12px;\n}\n.jev-devtools .lab-choice-remainder .lab-small {\n margin: 8px 0 0;\n font-size: 11px;\n}\n\n.jev-devtools .detail-body.detail-body-flow {\n flex-direction: column;\n}\n.jev-devtools .detail-body-flow > .warning,\n.jev-devtools .detail-body-flow > .error-banner {\n flex-shrink: 0;\n margin: 6px 16px;\n}\n.jev-devtools .lab-choice-browser {\n overflow: auto;\n}\n.jev-devtools .lab-choice-table-scroll {\n flex: 1 0 110px;\n}\n\n/* Only freshly revealed branches enter; pan/zoom remains on the canvas. */\n.jev-devtools .lab-card-enter {\n animation: jev-card-enter 280ms cubic-bezier(0.2, 0.8, 0.2, 1) backwards;\n}\n.jev-devtools .lab-branch-enter {\n stroke-dasharray: 1;\n animation: jev-branch-enter 280ms ease-out backwards;\n}\n@keyframes jev-card-enter {\n from {\n opacity: 0;\n transform: translateX(-16px) scale(0.96);\n }\n}\n@keyframes jev-branch-enter {\n from {\n opacity: 0;\n stroke-dashoffset: 1;\n }\n to {\n opacity: 1;\n stroke-dashoffset: 0;\n }\n}\n@media (prefers-reduced-motion: reduce) {\n .jev-devtools .lab-card-enter,\n .jev-devtools .lab-branch-enter {\n animation: none;\n }\n}\n";
|
package/lib/src/inspector.css
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
.jev-devtools {
|
|
2
|
+
all: initial;
|
|
3
|
+
display: block;
|
|
4
|
+
direction: ltr;
|
|
5
|
+
unicode-bidi: isolate;
|
|
6
|
+
pointer-events: auto;
|
|
2
7
|
color-scheme: dark;
|
|
3
8
|
font:
|
|
4
9
|
12px system-ui,
|
|
5
10
|
sans-serif;
|
|
6
11
|
color: #eee8ed;
|
|
12
|
+
line-height: 1.5;
|
|
7
13
|
}
|
|
8
14
|
.jev-devtools,
|
|
9
15
|
.jev-devtools * {
|
|
@@ -184,6 +190,7 @@
|
|
|
184
190
|
.jev-devtools .request-detail {
|
|
185
191
|
flex: 1;
|
|
186
192
|
min-width: 0;
|
|
193
|
+
min-height: 0;
|
|
187
194
|
display: flex;
|
|
188
195
|
flex-direction: column;
|
|
189
196
|
}
|
|
@@ -664,6 +671,8 @@
|
|
|
664
671
|
}
|
|
665
672
|
|
|
666
673
|
.jev-devtools.jev-drawer {
|
|
674
|
+
margin: 0;
|
|
675
|
+
padding: 0;
|
|
667
676
|
position: fixed;
|
|
668
677
|
z-index: 1000;
|
|
669
678
|
inset: auto 0 0;
|
|
@@ -756,6 +765,8 @@
|
|
|
756
765
|
gap: 9px;
|
|
757
766
|
padding: 10px 16px;
|
|
758
767
|
border: 1px solid #a24487;
|
|
768
|
+
border-radius: 8px;
|
|
769
|
+
cursor: pointer;
|
|
759
770
|
background: #211321;
|
|
760
771
|
color: #f1cbe6;
|
|
761
772
|
font:
|
|
@@ -768,12 +779,25 @@
|
|
|
768
779
|
font-size: 20px;
|
|
769
780
|
}
|
|
770
781
|
/* Baseline cards and authored left-to-right composition from the lab's Jev presets. */
|
|
782
|
+
.jev-devtools .detail-body.detail-body-flow {
|
|
783
|
+
display: flex;
|
|
784
|
+
padding: 0;
|
|
785
|
+
overflow: hidden;
|
|
786
|
+
}
|
|
787
|
+
.jev-devtools .lab-flow {
|
|
788
|
+
display: flex;
|
|
789
|
+
flex-direction: column;
|
|
790
|
+
flex: 1;
|
|
791
|
+
min-width: 0;
|
|
792
|
+
min-height: 0;
|
|
793
|
+
}
|
|
771
794
|
.jev-devtools .lab-flow-toolbar {
|
|
772
795
|
display: flex;
|
|
773
796
|
align-items: center;
|
|
774
797
|
justify-content: space-between;
|
|
775
798
|
gap: 12px;
|
|
776
|
-
|
|
799
|
+
flex-shrink: 0;
|
|
800
|
+
padding: 12px 16px;
|
|
777
801
|
flex-wrap: wrap;
|
|
778
802
|
}
|
|
779
803
|
.jev-devtools .lab-flow-toolbar label {
|
|
@@ -802,15 +826,40 @@
|
|
|
802
826
|
text-align: center;
|
|
803
827
|
}
|
|
804
828
|
.jev-devtools .lab-flow-viewport {
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
829
|
+
position: relative;
|
|
830
|
+
flex: 1;
|
|
831
|
+
min-height: 100px;
|
|
832
|
+
overflow: hidden;
|
|
833
|
+
background-color: #0d0a0f;
|
|
834
|
+
background-image: radial-gradient(#463449 0.8px, transparent 0.8px);
|
|
835
|
+
background-size: 20px 20px;
|
|
836
|
+
border-block: 1px solid #302634;
|
|
837
|
+
cursor: grab;
|
|
838
|
+
touch-action: none;
|
|
839
|
+
user-select: none;
|
|
840
|
+
overscroll-behavior: contain;
|
|
841
|
+
}
|
|
842
|
+
.jev-devtools .lab-flow-viewport[data-panning] {
|
|
843
|
+
cursor: grabbing;
|
|
844
|
+
}
|
|
845
|
+
.jev-devtools .lab-flow-viewport:focus-visible {
|
|
846
|
+
outline: 2px solid #e551ba;
|
|
847
|
+
outline-offset: -2px;
|
|
848
|
+
}
|
|
849
|
+
.jev-devtools .lab-flow-caption {
|
|
850
|
+
flex-shrink: 0;
|
|
851
|
+
padding: 8px 16px;
|
|
852
|
+
margin: 0;
|
|
853
|
+
}
|
|
854
|
+
.jev-devtools .lab-gesture-hint {
|
|
855
|
+
display: block;
|
|
856
|
+
color: #c6a7bd;
|
|
810
857
|
}
|
|
811
858
|
.jev-devtools .lab-flow-canvas {
|
|
812
|
-
position:
|
|
859
|
+
position: absolute;
|
|
860
|
+
inset: 0 auto auto 0;
|
|
813
861
|
transform-origin: top left;
|
|
862
|
+
will-change: transform;
|
|
814
863
|
font-family: system-ui, sans-serif;
|
|
815
864
|
color: #f5f3f6;
|
|
816
865
|
}
|
|
@@ -991,3 +1040,227 @@
|
|
|
991
1040
|
width: auto;
|
|
992
1041
|
max-width: none;
|
|
993
1042
|
}
|
|
1043
|
+
|
|
1044
|
+
.jev-devtools .detail-body-questions {
|
|
1045
|
+
display: flex;
|
|
1046
|
+
flex-direction: column;
|
|
1047
|
+
overflow: auto;
|
|
1048
|
+
}
|
|
1049
|
+
.jev-devtools .questions-list {
|
|
1050
|
+
display: flex;
|
|
1051
|
+
flex-direction: column;
|
|
1052
|
+
flex: 1;
|
|
1053
|
+
min-height: 180px;
|
|
1054
|
+
}
|
|
1055
|
+
.jev-devtools .questions-navigation {
|
|
1056
|
+
display: flex;
|
|
1057
|
+
align-items: end;
|
|
1058
|
+
gap: 8px;
|
|
1059
|
+
flex-wrap: wrap;
|
|
1060
|
+
flex-shrink: 0;
|
|
1061
|
+
}
|
|
1062
|
+
.jev-devtools .questions-navigation label {
|
|
1063
|
+
display: flex;
|
|
1064
|
+
align-items: center;
|
|
1065
|
+
gap: 8px;
|
|
1066
|
+
white-space: nowrap;
|
|
1067
|
+
flex: 1;
|
|
1068
|
+
min-width: 240px;
|
|
1069
|
+
max-width: 480px;
|
|
1070
|
+
font-size: 11px;
|
|
1071
|
+
color: #bc9db5;
|
|
1072
|
+
}
|
|
1073
|
+
.jev-devtools .questions-navigation select {
|
|
1074
|
+
display: block;
|
|
1075
|
+
min-width: 0;
|
|
1076
|
+
}
|
|
1077
|
+
.jev-devtools .questions-navigation > span {
|
|
1078
|
+
align-self: center;
|
|
1079
|
+
color: #a89bac;
|
|
1080
|
+
}
|
|
1081
|
+
.jev-devtools .questions-help {
|
|
1082
|
+
margin: 8px 0;
|
|
1083
|
+
flex-shrink: 0;
|
|
1084
|
+
}
|
|
1085
|
+
.jev-devtools .questions-viewport {
|
|
1086
|
+
flex: 1;
|
|
1087
|
+
min-height: 0;
|
|
1088
|
+
overflow: auto;
|
|
1089
|
+
overflow-anchor: none;
|
|
1090
|
+
scrollbar-gutter: stable;
|
|
1091
|
+
padding: 3px;
|
|
1092
|
+
}
|
|
1093
|
+
.jev-devtools .questions-spacer {
|
|
1094
|
+
position: relative;
|
|
1095
|
+
width: 100%;
|
|
1096
|
+
}
|
|
1097
|
+
.jev-devtools .questions-row {
|
|
1098
|
+
position: absolute;
|
|
1099
|
+
top: 0;
|
|
1100
|
+
left: 0;
|
|
1101
|
+
width: 100%;
|
|
1102
|
+
padding-bottom: 14px;
|
|
1103
|
+
}
|
|
1104
|
+
.jev-devtools .questions-row .standalone {
|
|
1105
|
+
margin-bottom: 0;
|
|
1106
|
+
}
|
|
1107
|
+
.jev-devtools .questions-viewport:focus-visible,
|
|
1108
|
+
.jev-devtools .questions-row article:focus-visible {
|
|
1109
|
+
outline: 2px solid #e551ba;
|
|
1110
|
+
outline-offset: -2px;
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/* Reserve measured card geometry while rapid scrolling uses a light preview. */
|
|
1114
|
+
.jev-devtools .questions-row[data-preview="true"] article {
|
|
1115
|
+
height: 100%;
|
|
1116
|
+
}
|
|
1117
|
+
.jev-devtools .question-scroll-preview p {
|
|
1118
|
+
margin-top: 16px;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/* The full distribution stays at reading size, outside the zoomable canvas. */
|
|
1122
|
+
.jev-devtools .lab-flow-viewport[hidden] {
|
|
1123
|
+
display: none;
|
|
1124
|
+
}
|
|
1125
|
+
.jev-devtools .lab-choice-browser {
|
|
1126
|
+
display: flex;
|
|
1127
|
+
flex-direction: column;
|
|
1128
|
+
flex: 1;
|
|
1129
|
+
min-height: 0;
|
|
1130
|
+
padding: 8px 16px;
|
|
1131
|
+
gap: 10px;
|
|
1132
|
+
}
|
|
1133
|
+
.jev-devtools .lab-choice-controls {
|
|
1134
|
+
display: flex;
|
|
1135
|
+
gap: 12px;
|
|
1136
|
+
align-items: end;
|
|
1137
|
+
flex-wrap: wrap;
|
|
1138
|
+
font-size: 12px;
|
|
1139
|
+
}
|
|
1140
|
+
.jev-devtools .lab-choice-controls label {
|
|
1141
|
+
display: grid;
|
|
1142
|
+
gap: 5px;
|
|
1143
|
+
}
|
|
1144
|
+
.jev-devtools .lab-choice-table-scroll {
|
|
1145
|
+
overflow: auto;
|
|
1146
|
+
min-height: 0;
|
|
1147
|
+
flex: 1;
|
|
1148
|
+
}
|
|
1149
|
+
.jev-devtools .lab-choice-browser table {
|
|
1150
|
+
width: 100%;
|
|
1151
|
+
border-collapse: collapse;
|
|
1152
|
+
font-size: 13px;
|
|
1153
|
+
}
|
|
1154
|
+
.jev-devtools .lab-choice-browser caption {
|
|
1155
|
+
text-align: left;
|
|
1156
|
+
padding: 8px 0;
|
|
1157
|
+
color: #aaa0af;
|
|
1158
|
+
}
|
|
1159
|
+
.jev-devtools .lab-choice-browser th,
|
|
1160
|
+
.jev-devtools .lab-choice-browser td {
|
|
1161
|
+
padding: 9px 12px;
|
|
1162
|
+
border-bottom: 1px solid #302634;
|
|
1163
|
+
text-align: left;
|
|
1164
|
+
}
|
|
1165
|
+
.jev-devtools .lab-choice-browser th {
|
|
1166
|
+
overflow-wrap: anywhere;
|
|
1167
|
+
max-width: 320px;
|
|
1168
|
+
}
|
|
1169
|
+
.jev-devtools .lab-choice-browser td {
|
|
1170
|
+
font-variant-numeric: tabular-nums;
|
|
1171
|
+
}
|
|
1172
|
+
.jev-devtools .lab-choice-browser code {
|
|
1173
|
+
white-space: normal;
|
|
1174
|
+
overflow-wrap: anywhere;
|
|
1175
|
+
}
|
|
1176
|
+
.jev-devtools .lab-choice-returned {
|
|
1177
|
+
display: block;
|
|
1178
|
+
font-size: 10px;
|
|
1179
|
+
color: #e551ba;
|
|
1180
|
+
}
|
|
1181
|
+
.jev-devtools .lab-choice-pagination {
|
|
1182
|
+
display: flex;
|
|
1183
|
+
align-items: center;
|
|
1184
|
+
gap: 12px;
|
|
1185
|
+
justify-content: space-between;
|
|
1186
|
+
font-size: 12px;
|
|
1187
|
+
}
|
|
1188
|
+
.jev-devtools .lab-choice-remainder button {
|
|
1189
|
+
margin-top: 7px;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
.jev-devtools .lab-flow-compact .lab-output {
|
|
1193
|
+
padding: 12px;
|
|
1194
|
+
height: 104px;
|
|
1195
|
+
}
|
|
1196
|
+
.jev-devtools .lab-flow-compact .lab-output .lab-eyebrow {
|
|
1197
|
+
margin-bottom: 6px;
|
|
1198
|
+
}
|
|
1199
|
+
.jev-devtools .lab-flow-compact .lab-output h3 {
|
|
1200
|
+
font-size: 16px;
|
|
1201
|
+
margin-bottom: 8px;
|
|
1202
|
+
}
|
|
1203
|
+
.jev-devtools .lab-flow-compact .lab-output p {
|
|
1204
|
+
font-size: 12px;
|
|
1205
|
+
margin: 4px 0;
|
|
1206
|
+
}
|
|
1207
|
+
.jev-devtools .lab-flow-compact .lab-result {
|
|
1208
|
+
display: -webkit-box;
|
|
1209
|
+
-webkit-line-clamp: 2;
|
|
1210
|
+
-webkit-box-orient: vertical;
|
|
1211
|
+
overflow: hidden;
|
|
1212
|
+
overflow-wrap: anywhere;
|
|
1213
|
+
}
|
|
1214
|
+
.jev-devtools .lab-choice-remainder {
|
|
1215
|
+
padding: 12px;
|
|
1216
|
+
}
|
|
1217
|
+
.jev-devtools .lab-choice-remainder .lab-small {
|
|
1218
|
+
margin: 8px 0 0;
|
|
1219
|
+
font-size: 11px;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
.jev-devtools .detail-body.detail-body-flow {
|
|
1223
|
+
flex-direction: column;
|
|
1224
|
+
}
|
|
1225
|
+
.jev-devtools .detail-body-flow > .warning,
|
|
1226
|
+
.jev-devtools .detail-body-flow > .error-banner {
|
|
1227
|
+
flex-shrink: 0;
|
|
1228
|
+
margin: 6px 16px;
|
|
1229
|
+
}
|
|
1230
|
+
.jev-devtools .lab-choice-browser {
|
|
1231
|
+
overflow: auto;
|
|
1232
|
+
}
|
|
1233
|
+
.jev-devtools .lab-choice-table-scroll {
|
|
1234
|
+
flex: 1 0 110px;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/* Only freshly revealed branches enter; pan/zoom remains on the canvas. */
|
|
1238
|
+
.jev-devtools .lab-card-enter {
|
|
1239
|
+
animation: jev-card-enter 280ms cubic-bezier(0.2, 0.8, 0.2, 1) backwards;
|
|
1240
|
+
}
|
|
1241
|
+
.jev-devtools .lab-branch-enter {
|
|
1242
|
+
stroke-dasharray: 1;
|
|
1243
|
+
animation: jev-branch-enter 280ms ease-out backwards;
|
|
1244
|
+
}
|
|
1245
|
+
@keyframes jev-card-enter {
|
|
1246
|
+
from {
|
|
1247
|
+
opacity: 0;
|
|
1248
|
+
transform: translateX(-16px) scale(0.96);
|
|
1249
|
+
}
|
|
1250
|
+
}
|
|
1251
|
+
@keyframes jev-branch-enter {
|
|
1252
|
+
from {
|
|
1253
|
+
opacity: 0;
|
|
1254
|
+
stroke-dashoffset: 1;
|
|
1255
|
+
}
|
|
1256
|
+
to {
|
|
1257
|
+
opacity: 1;
|
|
1258
|
+
stroke-dashoffset: 0;
|
|
1259
|
+
}
|
|
1260
|
+
}
|
|
1261
|
+
@media (prefers-reduced-motion: reduce) {
|
|
1262
|
+
.jev-devtools .lab-card-enter,
|
|
1263
|
+
.jev-devtools .lab-branch-enter {
|
|
1264
|
+
animation: none;
|
|
1265
|
+
}
|
|
1266
|
+
}
|
package/lib/src/protocol.d.ts
CHANGED
|
@@ -62,3 +62,16 @@ export type Snapshot = {
|
|
|
62
62
|
version: 1;
|
|
63
63
|
traces: Trace[];
|
|
64
64
|
};
|
|
65
|
+
export type StreamSnapshot = {
|
|
66
|
+
version: 2;
|
|
67
|
+
revision: number;
|
|
68
|
+
traces: Trace[];
|
|
69
|
+
};
|
|
70
|
+
export type TraceDelta = {
|
|
71
|
+
version: 2;
|
|
72
|
+
base: number;
|
|
73
|
+
revision: number;
|
|
74
|
+
upserts: Trace[];
|
|
75
|
+
/** Authoritative newest-first retained IDs. Omitted IDs are removed. */
|
|
76
|
+
order: string[];
|
|
77
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
const revision = (value) => typeof value === "number" && Number.isSafeInteger(value) && value >= 0;
|
|
2
|
+
function traces(value) {
|
|
3
|
+
if (!Array.isArray(value) || value.length > 200)
|
|
4
|
+
throw new Error("Invalid traces");
|
|
5
|
+
const ids = new Set();
|
|
6
|
+
for (const trace of value) {
|
|
7
|
+
if (!trace ||
|
|
8
|
+
trace.version !== 1 ||
|
|
9
|
+
typeof trace.id !== "string" ||
|
|
10
|
+
!trace.id ||
|
|
11
|
+
ids.has(trace.id))
|
|
12
|
+
throw new Error("Invalid trace ID");
|
|
13
|
+
ids.add(trace.id);
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
// Owned by one EventSource connection. Unchanged trace objects keep their
|
|
18
|
+
// identity; validate the complete update before replacing any current state.
|
|
19
|
+
export class TraceStore {
|
|
20
|
+
current = [];
|
|
21
|
+
revision;
|
|
22
|
+
apply(event, input) {
|
|
23
|
+
if (!input || typeof input !== "object")
|
|
24
|
+
throw new Error("Invalid update");
|
|
25
|
+
const data = input;
|
|
26
|
+
if (event === "snapshot") {
|
|
27
|
+
if (data.version !== 1 && data.version !== 2)
|
|
28
|
+
throw new Error("Unsupported stream");
|
|
29
|
+
if (data.version === 2 && !revision(data.revision))
|
|
30
|
+
throw new Error("Invalid revision");
|
|
31
|
+
const next = traces(data.traces);
|
|
32
|
+
this.current = next;
|
|
33
|
+
this.revision =
|
|
34
|
+
data.version === 2 ? data.revision : undefined;
|
|
35
|
+
return next;
|
|
36
|
+
}
|
|
37
|
+
if (data.version !== 2 ||
|
|
38
|
+
this.revision === undefined ||
|
|
39
|
+
data.base !== this.revision ||
|
|
40
|
+
!revision(data.revision) ||
|
|
41
|
+
data.revision <= this.revision)
|
|
42
|
+
throw new Error("Inspector stream needs a fresh snapshot");
|
|
43
|
+
if (!Array.isArray(data.order) ||
|
|
44
|
+
data.order.length > 200 ||
|
|
45
|
+
data.order.some((id) => typeof id !== "string") ||
|
|
46
|
+
new Set(data.order).size !== data.order.length)
|
|
47
|
+
throw new Error("Invalid retained order");
|
|
48
|
+
const order = data.order;
|
|
49
|
+
const retained = new Set(order);
|
|
50
|
+
const upserts = traces(data.upserts);
|
|
51
|
+
if (upserts.some((t) => !retained.has(t.id)))
|
|
52
|
+
throw new Error("Unexpected upsert");
|
|
53
|
+
const byId = new Map(this.current.map((t) => [t.id, t]));
|
|
54
|
+
for (const trace of upserts)
|
|
55
|
+
byId.set(trace.id, trace);
|
|
56
|
+
const next = order.map((id) => {
|
|
57
|
+
const trace = byId.get(id);
|
|
58
|
+
if (!trace)
|
|
59
|
+
throw new Error("Missing retained trace");
|
|
60
|
+
return trace;
|
|
61
|
+
});
|
|
62
|
+
this.current = next;
|
|
63
|
+
this.revision = data.revision;
|
|
64
|
+
return next;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { TraceStore } from "./trace-store.js";
|
|
2
|
+
export function connectTraceStream(url, onTraces, onConnection) {
|
|
3
|
+
let alive = true, events, retry, frame, fallback, pending;
|
|
4
|
+
const cancelPublish = () => {
|
|
5
|
+
if (frame !== undefined)
|
|
6
|
+
cancelAnimationFrame(frame);
|
|
7
|
+
clearTimeout(fallback);
|
|
8
|
+
frame = undefined;
|
|
9
|
+
fallback = undefined;
|
|
10
|
+
pending = undefined;
|
|
11
|
+
};
|
|
12
|
+
const publish = () => {
|
|
13
|
+
const next = pending;
|
|
14
|
+
cancelPublish();
|
|
15
|
+
if (alive && next)
|
|
16
|
+
onTraces(next);
|
|
17
|
+
};
|
|
18
|
+
const schedule = (next) => {
|
|
19
|
+
pending = next;
|
|
20
|
+
if (frame !== undefined)
|
|
21
|
+
return;
|
|
22
|
+
// Merge every protocol event, but render only the newest state per frame.
|
|
23
|
+
// Background tabs may suspend rAF; keep delivery bounded there as well.
|
|
24
|
+
frame = requestAnimationFrame(publish);
|
|
25
|
+
fallback = setTimeout(publish, 100);
|
|
26
|
+
};
|
|
27
|
+
const connect = () => {
|
|
28
|
+
if (!alive)
|
|
29
|
+
return;
|
|
30
|
+
// Native reconnects receive a full snapshot too. After an invalid delta,
|
|
31
|
+
// discard the cursor and reconnect rather than applying partial history.
|
|
32
|
+
const store = new TraceStore();
|
|
33
|
+
const source = new EventSource(url + "/events?protocol=2");
|
|
34
|
+
events = source;
|
|
35
|
+
const receive = (kind, event) => {
|
|
36
|
+
if (!alive || events !== source)
|
|
37
|
+
return;
|
|
38
|
+
try {
|
|
39
|
+
const next = store.apply(kind, JSON.parse(event.data));
|
|
40
|
+
schedule(next);
|
|
41
|
+
onConnection("connected");
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
cancelPublish();
|
|
45
|
+
source.close();
|
|
46
|
+
events = undefined;
|
|
47
|
+
onConnection("resynchronizing");
|
|
48
|
+
retry = setTimeout(connect, 1000);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
source.addEventListener("snapshot", (event) => receive("snapshot", event));
|
|
52
|
+
source.addEventListener("delta", (event) => receive("delta", event));
|
|
53
|
+
source.onerror = () => {
|
|
54
|
+
if (alive && events === source)
|
|
55
|
+
onConnection("reconnecting");
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
connect();
|
|
59
|
+
return () => {
|
|
60
|
+
alive = false;
|
|
61
|
+
clearTimeout(retry);
|
|
62
|
+
cancelPublish();
|
|
63
|
+
events?.close();
|
|
64
|
+
};
|
|
65
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Point } from "./diagram-camera.js";
|
|
2
|
+
/** Lab-style camera. Pointer frames update the transform without rerendering cards. */
|
|
3
|
+
export declare function useDiagramViewport(width: number, height: number, scene: string): {
|
|
4
|
+
viewport: import("react").RefObject<HTMLDivElement | null>;
|
|
5
|
+
canvas: import("react").RefObject<HTMLDivElement | null>;
|
|
6
|
+
zoom: number;
|
|
7
|
+
zoomBy: (factor: number, point?: Point) => void;
|
|
8
|
+
fit: () => void;
|
|
9
|
+
preserveCamera: () => void;
|
|
10
|
+
};
|