@sqlite-sync/devtools 0.9.2 → 0.9.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { useCallback, useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
1
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
2
+ import { createPortal } from "react-dom";
2
3
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
4
  //#region src/devtools-registry.ts
4
5
  const devtoolsRegistrySymbol = Symbol.for("@sqlite-sync/devtools");
@@ -44,10 +45,31 @@ function getOrCreateSQLiteSyncDevtoolsRegistry() {
44
45
  return registryGlobal[devtoolsRegistrySymbol];
45
46
  }
46
47
  //#endregion
48
+ //#region src/devtools.css?inline
49
+ var devtools_default = ":host {\n all: initial;\n z-index: 9998;\n --ss-bg: #0e1015;\n --ss-bg-panel: #161921;\n --ss-bg-card: #1c2030;\n --ss-bg-input: #111318;\n --ss-border: #2a2f3e;\n --ss-border-light: #343a4f;\n --ss-text: #e2e8f0;\n --ss-text-muted: #6b7280;\n --ss-text-dim: #9ca3af;\n --ss-teal: #2dd4bf;\n --ss-teal-dim: #2dd4bf1f;\n --ss-teal-glow: #2dd4bf0f;\n --ss-error: #f87171;\n --ss-error-bg: #f8717114;\n --ss-error-border: #f8717140;\n --ss-success: #34d399;\n --ss-success-bg: #34d39914;\n --ss-amber: #fbbf24;\n color: var(--ss-text);\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: .88rem;\n display: block;\n position: relative;\n}\n\n:host *, :host :before, :host :after {\n box-sizing: border-box;\n}\n\n.mono {\n font-family: ui-monospace, monospace;\n}\n\n.sm {\n font-size: .78rem;\n}\n\n.alignCenter {\n text-align: center;\n}\n\n.mlAuto {\n margin-left: auto;\n}\n\n.hiddenInput {\n display: none;\n}\n\n.floatingRoot {\n pointer-events: none;\n z-index: 9998;\n position: fixed;\n inset: 0;\n}\n\n.triggerButton {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-panel);\n color: var(--ss-text);\n box-shadow: 0 0 0 1px var(--ss-border),\n 0 4px 16px #00000080;\n cursor: grab;\n touch-action: none;\n user-select: none;\n pointer-events: auto;\n border-radius: 8px;\n align-items: center;\n gap: .3rem;\n padding: .3rem .45rem;\n display: inline-flex;\n position: absolute;\n bottom: 16px;\n right: 16px;\n}\n\n.triggerIcon {\n color: var(--ss-teal);\n font-size: .9rem;\n line-height: 1;\n}\n\n.triggerCount {\n background-color: var(--ss-teal-dim);\n min-width: 1.1rem;\n color: var(--ss-teal);\n text-align: center;\n border-radius: 4px;\n padding: .05rem .25rem;\n font-family: ui-monospace, monospace;\n font-size: .65rem;\n font-weight: 700;\n}\n\n.overlay {\n z-index: 9999;\n backdrop-filter: blur(8px);\n background-color: #000000b8;\n justify-content: center;\n align-items: center;\n padding: 1.5rem;\n display: flex;\n position: fixed;\n inset: 0;\n}\n\n.dialog {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg);\n width: min(72rem, 100%);\n height: min(90vh, 860px);\n box-shadow: 0 0 0 1px var(--ss-border),\n 0 40px 80px #000c,\n 0 0 60px #2dd4bf0a;\n color: var(--ss-text);\n border-radius: 16px;\n flex-direction: column;\n font-family: ui-sans-serif, system-ui, sans-serif;\n font-size: .88rem;\n display: flex;\n overflow: hidden;\n}\n\n.header {\n border-bottom: 1px solid var(--ss-border);\n background: var(--ss-bg-panel);\n flex-shrink: 0;\n justify-content: space-between;\n align-items: center;\n gap: 1rem;\n padding: .85rem 1.1rem;\n display: flex;\n}\n\n.headerLeft {\n align-items: center;\n gap: .75rem;\n display: flex;\n}\n\n.headerLogo {\n color: var(--ss-teal);\n font-size: 1.3rem;\n line-height: 1;\n}\n\n.eyebrow {\n letter-spacing: .1em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n font-family: ui-monospace, monospace;\n font-size: .65rem;\n font-weight: 600;\n}\n\n.title {\n color: var(--ss-text);\n letter-spacing: -.01em;\n margin: .05rem 0 0;\n font-size: .95rem;\n font-weight: 700;\n}\n\n.headerRight {\n align-items: center;\n gap: .6rem;\n display: flex;\n}\n\n.instancePickerLabel {\n align-items: center;\n gap: .5rem;\n display: flex;\n}\n\n.instancePickerText {\n color: var(--ss-text-muted);\n text-transform: uppercase;\n letter-spacing: .06em;\n font-size: .72rem;\n font-weight: 600;\n}\n\n.instancePickerSelect {\n border: 1px solid var(--ss-border);\n background-color: var(--ss-bg-card);\n color: var(--ss-text);\n cursor: pointer;\n border-radius: 7px;\n padding: .35rem .6rem;\n font-family: ui-monospace, monospace;\n font-size: .82rem;\n}\n\n.instanceCountBadge {\n color: var(--ss-teal);\n background-color: var(--ss-teal-dim);\n border-radius: 5px;\n padding: .2rem .5rem;\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n font-weight: 600;\n}\n\n.closeButton {\n border: 1px solid var(--ss-border);\n width: 28px;\n height: 28px;\n color: var(--ss-text-muted);\n cursor: pointer;\n background-color: #0000;\n border-radius: 7px;\n justify-content: center;\n align-items: center;\n font-size: .82rem;\n font-weight: 600;\n display: flex;\n}\n\n.hideLauncherButton {\n border: 1px solid var(--ss-border);\n color: var(--ss-text-dim);\n cursor: pointer;\n white-space: nowrap;\n background-color: #0000;\n border-radius: 7px;\n padding: .3rem .65rem;\n font-size: .75rem;\n font-weight: 600;\n}\n\n.emptyState {\n flex-direction: column;\n flex: 1;\n justify-content: center;\n align-items: center;\n gap: .75rem;\n padding: 4rem 2rem;\n display: flex;\n}\n\n.emptyStateIcon {\n color: var(--ss-text-muted);\n opacity: .4;\n font-size: 2.5rem;\n}\n\n.emptyStateText {\n color: var(--ss-text-dim);\n margin: 0;\n font-size: .95rem;\n font-weight: 600;\n}\n\n.emptyStateSubtext {\n color: var(--ss-text-muted);\n text-align: center;\n margin: 0;\n font-size: .82rem;\n}\n\n.inlineCode {\n background-color: var(--ss-bg-card);\n border: 1px solid var(--ss-border);\n color: var(--ss-teal);\n border-radius: 4px;\n padding: .1em .4em;\n font-family: ui-monospace, monospace;\n font-size: .88em;\n}\n\n.contentLayout {\n flex: 1;\n grid-template-columns: 200px minmax(0, 1fr);\n min-height: 0;\n display: grid;\n overflow: hidden;\n}\n\n.sidebar {\n border-right: 1px solid var(--ss-border);\n background: var(--ss-bg-panel);\n flex-direction: column;\n gap: 0;\n padding: .75rem;\n display: flex;\n overflow-y: auto;\n}\n\n.nav {\n flex-direction: column;\n gap: .25rem;\n margin-bottom: 1rem;\n display: flex;\n}\n\n.navIcon {\n opacity: .7;\n font-size: .7rem;\n}\n\n.sidebarInfo {\n background: var(--ss-bg-card);\n border: 1px solid var(--ss-border);\n border-radius: 8px;\n margin-top: auto;\n padding: .75rem;\n}\n\n.sidebarInfoLabel {\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n margin-bottom: .3rem;\n font-size: .65rem;\n font-weight: 600;\n}\n\n.sidebarInfoValue {\n color: var(--ss-text);\n word-break: break-word;\n font-family: ui-monospace, monospace;\n font-size: .88rem;\n font-weight: 600;\n}\n\n.sidebarInfoSub {\n color: var(--ss-text-muted);\n margin-top: .25rem;\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n}\n\n.mainPane {\n min-width: 0;\n padding: 1rem;\n overflow-y: auto;\n}\n\n.overviewLayout {\n flex-direction: column;\n gap: 1.25rem;\n display: flex;\n}\n\n.overviewCardsRow {\n grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));\n gap: .75rem;\n display: grid;\n}\n\n.overviewCard {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n border-radius: 10px;\n padding: .9rem 1rem;\n}\n\n.overviewCardLabel {\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n margin-bottom: .4rem;\n font-size: .68rem;\n font-weight: 600;\n}\n\n.overviewCardValue {\n color: var(--ss-text);\n word-break: break-word;\n font-size: 1rem;\n font-weight: 700;\n}\n\n.overviewCardSub {\n color: var(--ss-text-muted);\n margin-top: .25rem;\n font-family: ui-monospace, monospace;\n font-size: .68rem;\n}\n\n.overviewSection {\n flex-direction: column;\n gap: .5rem;\n display: flex;\n}\n\n.overviewSectionTitle {\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n font-size: .72rem;\n font-weight: 600;\n}\n\n.overviewSectionHeader {\n justify-content: space-between;\n align-items: center;\n gap: .75rem;\n display: flex;\n}\n\n.refreshButton {\n border: 1px solid var(--ss-border);\n color: var(--ss-text-dim);\n cursor: pointer;\n background-color: #0000;\n border-radius: 7px;\n flex-shrink: 0;\n padding: .35rem .7rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n}\n\n.overviewAccumulatorValue {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n color: var(--ss-text);\n word-break: break-all;\n border-radius: 8px;\n padding: .75rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n line-height: 1.5;\n}\n\n.overviewAccumulatorEmpty {\n color: var(--ss-text-muted);\n font-style: italic;\n}\n\n.overviewAccumulatorError {\n color: var(--ss-error);\n}\n\n.overviewEmpty {\n color: var(--ss-text-muted);\n font-size: .82rem;\n font-style: italic;\n}\n\n.eventCountGrid {\n grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));\n gap: .5rem;\n display: grid;\n}\n\n.eventCountCard {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n border-radius: 8px;\n padding: .75rem .85rem;\n}\n\n.eventCountValue {\n color: var(--ss-text);\n margin-bottom: .35rem;\n font-family: ui-monospace, monospace;\n font-size: 1.05rem;\n font-weight: 700;\n}\n\n.eventCountValue[data-tone=\"amber\"][data-emphasize=\"true\"] {\n color: var(--ss-amber);\n}\n\n.eventCountValue[data-tone=\"success\"][data-emphasize=\"true\"] {\n color: var(--ss-success);\n}\n\n.eventCountValue[data-tone=\"error\"][data-emphasize=\"true\"] {\n color: var(--ss-error);\n}\n\n.dataActions {\n gap: .4rem;\n display: flex;\n}\n\n.dataButton {\n border: 1px solid var(--ss-border);\n color: var(--ss-teal);\n cursor: pointer;\n white-space: nowrap;\n background-color: #0000;\n border-radius: 7px;\n padding: .4rem .8rem;\n font-size: .78rem;\n font-weight: 600;\n}\n\n.dataStatusSuccess {\n color: var(--ss-success);\n font-size: .75rem;\n}\n\n.dataStatusError {\n color: var(--ss-error);\n word-break: break-word;\n font-size: .75rem;\n}\n\n.dangerZone {\n border: 1px solid var(--ss-error-border);\n background: var(--ss-error-bg);\n border-radius: 10px;\n margin-top: auto;\n padding: .75rem 1rem;\n}\n\n.dangerZoneTitle {\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--ss-error);\n margin-bottom: .5rem;\n font-size: .65rem;\n font-weight: 700;\n}\n\n.dangerZoneRow {\n justify-content: space-between;\n align-items: center;\n gap: 1rem;\n display: flex;\n}\n\n.dangerZoneDesc {\n color: var(--ss-text-muted);\n font-size: .75rem;\n line-height: 1.5;\n}\n\n.dangerZoneActions {\n flex-shrink: 0;\n gap: .4rem;\n display: flex;\n}\n\n.resetButton {\n border: 1px solid var(--ss-error-border);\n color: var(--ss-error);\n cursor: pointer;\n white-space: nowrap;\n background-color: #0000;\n border-radius: 7px;\n flex-shrink: 0;\n padding: .35rem .75rem;\n font-size: .78rem;\n font-weight: 600;\n}\n\n.resetCancelButton {\n border: 1px solid var(--ss-border);\n color: var(--ss-text-dim);\n cursor: pointer;\n white-space: nowrap;\n background-color: #0000;\n border-radius: 7px;\n padding: .35rem .65rem;\n font-size: .78rem;\n font-weight: 600;\n}\n\n.resetConfirmButton {\n background-color: var(--ss-error);\n color: #0e1015;\n cursor: pointer;\n white-space: nowrap;\n border: none;\n border-radius: 7px;\n padding: .35rem .75rem;\n font-size: .78rem;\n font-weight: 700;\n}\n\n.queryRunnerLayout {\n flex-direction: column;\n gap: .75rem;\n height: 100%;\n display: flex;\n}\n\n.queryToolbar {\n flex-wrap: wrap;\n justify-content: space-between;\n align-items: center;\n gap: .75rem;\n display: flex;\n}\n\n.targetToggle {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n border-radius: 8px;\n gap: 0;\n display: flex;\n overflow: hidden;\n}\n\n.queryToolbarRight {\n align-items: center;\n gap: .6rem;\n display: flex;\n}\n\n.noInstanceWarning {\n color: var(--ss-amber);\n font-size: .75rem;\n font-weight: 500;\n}\n\n.shortcutHint {\n color: var(--ss-text-muted);\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n border-radius: 5px;\n padding: .2rem .5rem;\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n}\n\n.editorWrapper {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-input);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.textarea {\n resize: vertical;\n width: 100%;\n min-height: 9rem;\n color: var(--ss-text);\n box-sizing: border-box;\n background-color: #0000;\n border: none;\n outline: none;\n padding: .85rem 1rem;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: .875rem;\n line-height: 1.6;\n}\n\n.helperText {\n color: var(--ss-text-muted);\n font-size: .75rem;\n line-height: 1.5;\n}\n\n.resultPanel {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-panel);\n border-radius: 10px;\n flex-direction: column;\n gap: .6rem;\n display: flex;\n overflow: hidden;\n}\n\n.errorPanel {\n border: 1px solid var(--ss-error-border);\n background: var(--ss-error-bg);\n border-radius: 10px;\n flex-direction: column;\n gap: .6rem;\n padding: .85rem 1rem;\n display: flex;\n}\n\n.resultHeader {\n border-bottom: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n align-items: center;\n gap: .6rem;\n padding: .6rem .85rem;\n display: flex;\n}\n\n.successBadge {\n color: var(--ss-success);\n background-color: var(--ss-success-bg);\n border-radius: 5px;\n padding: .15rem .5rem;\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n font-weight: 700;\n}\n\n.errorBadge {\n color: var(--ss-error);\n background-color: var(--ss-error-bg);\n border-radius: 5px;\n padding: .15rem .5rem;\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n font-weight: 700;\n}\n\n.resultMeta {\n color: var(--ss-text-muted);\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n}\n\n.resultEmpty {\n color: var(--ss-text-muted);\n text-align: center;\n padding: 1.5rem;\n font-size: .82rem;\n font-style: italic;\n}\n\n.resultRaw {\n white-space: pre-wrap;\n word-break: break-word;\n color: var(--ss-text);\n margin: 0;\n padding: .85rem 1rem;\n font-family: ui-monospace, monospace;\n font-size: .8rem;\n line-height: 1.5;\n overflow-x: auto;\n}\n\n.errorMessage {\n color: var(--ss-error);\n white-space: pre-wrap;\n word-break: break-word;\n margin: 0;\n font-family: ui-monospace, monospace;\n font-size: .82rem;\n line-height: 1.6;\n}\n\n.tableWrapper {\n max-height: 320px;\n overflow: auto;\n}\n\n.table {\n border-collapse: collapse;\n width: 100%;\n font-family: ui-monospace, SFMono-Regular, Menlo, monospace;\n font-size: .8rem;\n}\n\n.table tbody tr:nth-child(2n) {\n background: var(--ss-teal-glow);\n}\n\n.th {\n text-align: left;\n letter-spacing: .05em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n border-bottom: 1px solid var(--ss-border);\n border-right: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n white-space: nowrap;\n padding: .5rem .85rem;\n font-size: .7rem;\n font-weight: 600;\n position: sticky;\n top: 0;\n}\n\n.thRowNum {\n text-align: right;\n letter-spacing: .05em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n border-bottom: 1px solid var(--ss-border);\n border-right: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n white-space: nowrap;\n opacity: .5;\n width: 2.5rem;\n padding: .5rem .85rem;\n font-size: .7rem;\n font-weight: 600;\n position: sticky;\n top: 0;\n}\n\n.td {\n border-bottom: 1px solid var(--ss-border);\n border-right: 1px solid var(--ss-border);\n color: var(--ss-text);\n white-space: nowrap;\n text-overflow: ellipsis;\n max-width: 300px;\n padding: .45rem .85rem;\n overflow: hidden;\n}\n\n.tdRowNum {\n border-bottom: 1px solid var(--ss-border);\n border-right: 1px solid var(--ss-border);\n color: var(--ss-text-muted);\n white-space: nowrap;\n text-overflow: ellipsis;\n opacity: .5;\n text-align: right;\n user-select: none;\n max-width: 300px;\n padding: .45rem .85rem;\n overflow: hidden;\n}\n\n.nullValue {\n color: var(--ss-text-muted);\n opacity: .6;\n font-style: italic;\n}\n\n.jsonValue {\n color: var(--ss-text-dim);\n}\n\n.tabButton {\n width: 100%;\n color: var(--ss-text-dim);\n text-align: left;\n cursor: pointer;\n background: none;\n border: none;\n border-left: 2px solid #0000;\n border-radius: 7px;\n align-items: center;\n gap: .5rem;\n padding: .6rem .75rem;\n font-size: .82rem;\n font-weight: 500;\n transition: background .15s;\n display: flex;\n}\n\n.tabButton[data-active=\"true\"] {\n background: var(--ss-teal-dim);\n color: var(--ss-teal);\n border-left: 2px solid var(--ss-teal);\n font-weight: 700;\n}\n\n.targetButton {\n border: none;\n border-right: 1px solid var(--ss-border);\n color: var(--ss-text-muted);\n cursor: pointer;\n white-space: nowrap;\n background: none;\n padding: .45rem .85rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n font-weight: 500;\n transition: background .1s;\n}\n\n.targetButton[data-active=\"true\"] {\n background: var(--ss-teal-dim);\n color: var(--ss-teal);\n font-weight: 700;\n}\n\n.runButton {\n background: var(--ss-bg-card);\n color: var(--ss-text-muted);\n cursor: not-allowed;\n opacity: 1;\n white-space: nowrap;\n border: none;\n border-radius: 7px;\n align-items: center;\n gap: .4rem;\n padding: .45rem .9rem;\n font-family: ui-monospace, monospace;\n font-size: .82rem;\n font-weight: 700;\n transition: background .15s;\n display: inline-flex;\n}\n\n.runButton[data-enabled=\"true\"] {\n background: var(--ss-teal);\n color: #0e1015;\n cursor: pointer;\n}\n\n.runButton[data-running=\"true\"] {\n opacity: .75;\n}\n\n.runningDot {\n background-color: currentColor;\n border-radius: 50%;\n width: 6px;\n height: 6px;\n}\n\n.schemaLayout {\n flex-direction: column;\n gap: 1.5rem;\n display: flex;\n}\n\n.schemaSection {\n flex-direction: column;\n gap: .65rem;\n display: flex;\n}\n\n.schemaSectionHeader {\n align-items: center;\n gap: .6rem;\n display: flex;\n}\n\n.schemaSectionTitle {\n letter-spacing: .08em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n font-size: .72rem;\n font-weight: 700;\n}\n\n.schemaBadge {\n color: var(--ss-teal);\n background-color: var(--ss-teal-dim);\n border-radius: 4px;\n padding: .1rem .4rem;\n font-family: ui-monospace, monospace;\n font-size: .68rem;\n font-weight: 700;\n}\n\n.schemaVersionChip {\n color: var(--ss-text-muted);\n background-color: var(--ss-bg-card);\n border: 1px solid var(--ss-border);\n border-radius: 4px;\n padding: .1rem .5rem;\n font-family: ui-monospace, monospace;\n font-size: .68rem;\n}\n\n.schemaTableGrid {\n border: 1px solid var(--ss-border);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.schemaColHeader {\n letter-spacing: .07em;\n text-transform: uppercase;\n color: var(--ss-text-muted);\n border-right: 1px solid var(--ss-border);\n padding: .5rem .85rem;\n font-size: .68rem;\n font-weight: 700;\n}\n\n.schemaStatusCell {\n justify-content: center;\n align-items: center;\n padding: .6rem .5rem;\n display: flex;\n}\n\n.schemaActiveTag {\n color: var(--ss-success);\n background-color: var(--ss-success-bg);\n border-radius: 4px;\n padding: .15rem .45rem;\n font-family: ui-monospace, monospace;\n font-size: .68rem;\n font-weight: 700;\n}\n\n.schemaEmpty {\n color: var(--ss-text-muted);\n font-size: .82rem;\n font-style: italic;\n}\n\n.schemaTableList {\n flex-direction: column;\n gap: .85rem;\n display: flex;\n}\n\n.schemaTableCard {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.schemaTableCardHeader {\n border-bottom: 1px solid var(--ss-border);\n flex-direction: column;\n gap: .45rem;\n padding: .75rem .85rem .7rem;\n display: flex;\n}\n\n.schemaTableCardTitleRow {\n align-items: center;\n gap: .5rem;\n display: flex;\n}\n\n.schemaTableCardName {\n color: var(--ss-text);\n font-family: ui-monospace, monospace;\n font-size: .92rem;\n font-weight: 700;\n}\n\n.schemaTableCardDescription {\n color: var(--ss-text-dim);\n margin: 0;\n font-size: .78rem;\n line-height: 1.45;\n}\n\n.schemaTableCardMeta {\n flex-wrap: wrap;\n gap: .35rem;\n display: flex;\n}\n\n.schemaPolicyChip {\n color: var(--ss-text-muted);\n background: var(--ss-bg-input);\n border: 1px solid var(--ss-border);\n border-radius: 4px;\n padding: .12rem .4rem;\n font-family: ui-monospace, monospace;\n font-size: .65rem;\n}\n\n.schemaPolicyChip[data-tone=\"warn\"] {\n color: var(--ss-amber);\n background: #fbbf2414;\n border-color: #fbbf2440;\n}\n\n.schemaColumnGrid {\n flex-direction: column;\n display: flex;\n overflow-x: auto;\n}\n\n.schemaColumnRow {\n border-bottom: 1px solid var(--ss-border);\n grid-template-columns: minmax(6.5rem, .7fr) minmax(8rem, 1fr) minmax(7.5rem, .9fr) minmax(0, 1.8fr);\n min-width: 36rem;\n display: grid;\n}\n\n.schemaColumnRow:last-child {\n border-bottom: none;\n}\n\n.schemaColumnRow[data-system=\"true\"] {\n opacity: .62;\n}\n\n.schemaColumnHeaderRow {\n background: var(--ss-bg-panel);\n}\n\n.schemaColumnHeaderRow .schemaColHeader:last-child {\n border-right: none;\n}\n\n.schemaColumnName, .schemaColumnType, .schemaColumnFlags, .schemaColumnDescription {\n border-right: 1px solid var(--ss-border);\n min-width: 0;\n padding: .5rem .85rem;\n font-size: .76rem;\n}\n\n.schemaColumnDescription {\n color: var(--ss-text-dim);\n overflow-wrap: anywhere;\n border-right: none;\n}\n\n.schemaColumnName {\n color: var(--ss-text);\n font-family: ui-monospace, monospace;\n font-weight: 600;\n}\n\n.schemaColumnType {\n color: var(--ss-teal);\n flex-direction: column;\n gap: .15rem;\n font-family: ui-monospace, monospace;\n display: flex;\n}\n\n.schemaColumnTypeDetail {\n color: var(--ss-text-muted);\n overflow-wrap: anywhere;\n font-size: .65rem;\n font-weight: 400;\n}\n\n.schemaColumnFlags {\n color: var(--ss-text-muted);\n overflow-wrap: anywhere;\n font-family: ui-monospace, monospace;\n}\n\n.liveQueryHeaderRow {\n background-color: var(--ss-bg-card);\n border-bottom: 1px solid var(--ss-border);\n grid-template-columns: minmax(0, 1fr) 220px 110px;\n gap: 0;\n display: grid;\n}\n\n.liveQueryRow {\n border-bottom: 1px solid var(--ss-border);\n grid-template-columns: minmax(0, 1fr) 220px 110px;\n gap: 0;\n display: grid;\n}\n\n.liveQuerySql {\n color: var(--ss-text);\n border-right: 1px solid var(--ss-border);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n padding: .6rem .85rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n}\n\n.liveQueryParameters {\n color: var(--ss-text-dim);\n border-right: 1px solid var(--ss-border);\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n padding: .6rem .85rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n}\n\n.liveQueryIdleTag {\n color: var(--ss-text-muted);\n background-color: var(--ss-bg-card);\n border: 1px solid var(--ss-border);\n border-radius: 4px;\n padding: .15rem .45rem;\n font-family: ui-monospace, monospace;\n font-size: .68rem;\n font-weight: 700;\n}\n\n.eventLogLayout {\n flex-direction: column;\n gap: .75rem;\n height: 100%;\n display: flex;\n}\n\n.eventLogToolbar {\n flex-wrap: wrap;\n flex-shrink: 0;\n align-items: center;\n gap: .5rem;\n display: flex;\n}\n\n.eventLogFilterSelect {\n border: 1px solid var(--ss-border);\n background-color: var(--ss-bg-card);\n color: var(--ss-text);\n cursor: pointer;\n border-radius: 7px;\n padding: .35rem .6rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n}\n\n.eventLogCount {\n color: var(--ss-text-muted);\n margin-left: auto;\n font-family: ui-monospace, monospace;\n font-size: .72rem;\n}\n\n.eventLogEmpty {\n text-align: center;\n color: var(--ss-text-muted);\n padding: 2rem;\n font-size: .82rem;\n font-style: italic;\n}\n\n.eventLogList {\n flex-direction: column;\n flex: 1;\n gap: .25rem;\n display: flex;\n overflow-y: auto;\n}\n\n.eventRow {\n border: 1px solid var(--ss-border);\n background: var(--ss-bg-card);\n border-radius: 8px;\n flex-shrink: 0;\n overflow: hidden;\n}\n\n.eventRow[data-expanded=\"true\"] {\n background: var(--ss-teal-glow);\n border: 1px solid #2dd4bf40;\n}\n\n.eventRowHeader {\n cursor: pointer;\n text-align: left;\n background: none;\n border: none;\n flex-wrap: wrap;\n align-items: center;\n gap: .5rem;\n width: 100%;\n padding: .5rem .75rem;\n display: flex;\n}\n\n.eventSyncId {\n color: var(--ss-text-muted);\n min-width: 3.5rem;\n font-family: ui-monospace, monospace;\n font-size: .7rem;\n}\n\n.eventDataset {\n color: var(--ss-text);\n font-family: ui-monospace, monospace;\n font-size: .8rem;\n font-weight: 600;\n}\n\n.eventItemId {\n color: var(--ss-text-muted);\n text-overflow: ellipsis;\n white-space: nowrap;\n flex: 1;\n min-width: 0;\n font-family: ui-monospace, monospace;\n font-size: .75rem;\n overflow: hidden;\n}\n\n.eventTimestamp {\n color: var(--ss-text-muted);\n margin-left: auto;\n font-family: ui-monospace, monospace;\n font-size: .7rem;\n}\n\n.eventChevron {\n color: var(--ss-text-muted);\n margin-left: .25rem;\n font-size: .6rem;\n}\n\n.eventPayload {\n border-top: 1px solid var(--ss-border);\n padding: .6rem .75rem;\n}\n\n.eventPayloadMeta {\n gap: .75rem;\n margin-bottom: .5rem;\n display: flex;\n}\n\n.eventMetaItem {\n color: var(--ss-text-muted);\n font-family: ui-monospace, monospace;\n font-size: .68rem;\n}\n\n.eventPayloadPre {\n color: var(--ss-text);\n white-space: pre-wrap;\n word-break: break-word;\n margin: 0;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n line-height: 1.5;\n}\n\n.loadMoreButton {\n border: 1px solid var(--ss-border);\n width: 100%;\n color: var(--ss-text-dim);\n cursor: pointer;\n background-color: #0000;\n border-radius: 8px;\n flex-shrink: 0;\n padding: .6rem;\n font-family: ui-monospace, monospace;\n font-size: .78rem;\n}\n\n.eventBadge {\n white-space: nowrap;\n border-radius: 4px;\n padding: .1rem .4rem;\n font-family: ui-monospace, monospace;\n font-size: .65rem;\n font-weight: 700;\n}\n\n.eventBadge[data-variant=\"create\"] {\n color: var(--ss-teal);\n background-color: var(--ss-teal-dim);\n}\n\n.eventBadge[data-variant=\"update\"] {\n color: #a78bfa;\n background-color: #a78bfa1f;\n}\n\n.eventBadge[data-variant=\"delete\"] {\n color: var(--ss-error);\n background-color: #f871711f;\n}\n\n.eventBadge[data-variant=\"own\"], .eventBadge[data-variant=\"applied\"] {\n color: var(--ss-success);\n background-color: var(--ss-success-bg);\n}\n\n.eventBadge[data-variant=\"remote\"], .eventBadge[data-variant=\"pending\"] {\n color: var(--ss-amber);\n background-color: #fbbf241a;\n}\n\n.eventBadge[data-variant=\"local\"] {\n color: var(--ss-text-dim);\n background-color: var(--ss-bg-card);\n border: 1px solid var(--ss-border);\n}\n\n.eventBadge[data-variant=\"failed\"] {\n color: var(--ss-error);\n background-color: var(--ss-error-bg);\n}\n\n.eventBadge[data-variant=\"skipped\"] {\n color: var(--ss-text-muted);\n background-color: var(--ss-bg-card);\n}\n";
50
+ //#endregion
51
+ //#region src/shadow-root.tsx
52
+ function ShadowRoot({ children, className }) {
53
+ const hostRef = useRef(null);
54
+ const [shadowRoot, setShadowRoot] = useState(null);
55
+ useLayoutEffect(() => {
56
+ const host = hostRef.current;
57
+ if (!host) return;
58
+ setShadowRoot(host.shadowRoot ?? host.attachShadow({ mode: "open" }));
59
+ }, []);
60
+ return /* @__PURE__ */ jsx("div", {
61
+ ref: hostRef,
62
+ className,
63
+ children: shadowRoot ? createPortal(/* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("style", { children: devtools_default }), children] }), shadowRoot) : null
64
+ });
65
+ }
66
+ //#endregion
47
67
  //#region src/devtools.tsx
48
68
  const TRIGGER_POSITION_STORAGE_KEY = "sqlite-sync-devtools.trigger-position";
69
+ const TRIGGER_HIDDEN_STORAGE_KEY = "sqlite-sync-devtools.trigger-hidden";
49
70
  const DRAG_THRESHOLD_PX = 4;
50
71
  const VIEWPORT_MARGIN_PX = 8;
72
+ const TOGGLE_LAUNCHER_HINT = typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform) ? "⌘⌥S" : "Ctrl+Alt+S";
51
73
  function readStoredTriggerPosition() {
52
74
  if (typeof window === "undefined") return null;
53
75
  try {
@@ -130,16 +152,70 @@ function useDraggableTrigger() {
130
152
  consumeDragClick
131
153
  };
132
154
  }
133
- function SQLiteSyncDevtools({ className }) {
155
+ function readStoredTriggerHidden() {
156
+ if (typeof window === "undefined") return false;
157
+ try {
158
+ return window.localStorage.getItem(TRIGGER_HIDDEN_STORAGE_KEY) === "1";
159
+ } catch {
160
+ return false;
161
+ }
162
+ }
163
+ function isToggleLauncherShortcut(event) {
164
+ if (event.shiftKey || !event.altKey || event.code !== "KeyS") return false;
165
+ return event.ctrlKey || event.metaKey;
166
+ }
167
+ function useLauncherVisibility({ hidden: hiddenProp, onHiddenChange }) {
168
+ const isControlled = hiddenProp !== void 0;
169
+ const [uncontrolledHidden, setUncontrolledHidden] = useState(readStoredTriggerHidden);
170
+ const hidden = isControlled ? hiddenProp : uncontrolledHidden;
171
+ const canToggle = !isControlled || onHiddenChange !== void 0;
172
+ const setHidden = useCallback((next) => {
173
+ if (isControlled) {
174
+ onHiddenChange?.(next);
175
+ return;
176
+ }
177
+ setUncontrolledHidden(next);
178
+ try {
179
+ window.localStorage.setItem(TRIGGER_HIDDEN_STORAGE_KEY, next ? "1" : "0");
180
+ } catch {}
181
+ }, [isControlled, onHiddenChange]);
182
+ useEffect(() => {
183
+ if (!canToggle) return;
184
+ const onKeyDown = (event) => {
185
+ if (!isToggleLauncherShortcut(event)) return;
186
+ event.preventDefault();
187
+ setHidden(!hidden);
188
+ };
189
+ window.addEventListener("keydown", onKeyDown);
190
+ return () => window.removeEventListener("keydown", onKeyDown);
191
+ }, [
192
+ canToggle,
193
+ hidden,
194
+ setHidden
195
+ ]);
196
+ return {
197
+ hidden,
198
+ setHidden,
199
+ canToggle
200
+ };
201
+ }
202
+ function SQLiteSyncDevtools({ className, hidden: hiddenProp, onHiddenChange }) {
134
203
  const registry = getOrCreateSQLiteSyncDevtoolsRegistry();
135
204
  const instances = useSyncExternalStore(registry.subscribe, registry.getSnapshot, getEmptySnapshot).instances;
136
205
  const { buttonRef, positionStyle, triggerHandlers, consumeDragClick } = useDraggableTrigger();
206
+ const { hidden, setHidden, canToggle } = useLauncherVisibility({
207
+ hidden: hiddenProp,
208
+ onHiddenChange
209
+ });
137
210
  const [isOpen, setIsOpen] = useState(false);
138
211
  const [activeTab, setActiveTab] = useState("overview");
139
212
  const [selectedInstanceId, setSelectedInstanceId] = useState("");
140
213
  const [queryTarget, setQueryTarget] = useState("memory");
141
214
  const [query, setQuery] = useState("");
142
215
  const [queryState, setQueryState] = useState({ status: "idle" });
216
+ useEffect(() => {
217
+ if (hidden) setIsOpen(false);
218
+ }, [hidden]);
143
219
  useEffect(() => {
144
220
  if (!isOpen) return;
145
221
  const onKeyDown = (event) => {
@@ -208,210 +284,315 @@ function SQLiteSyncDevtools({ className }) {
208
284
  });
209
285
  }
210
286
  };
211
- return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("div", {
212
- style: floatingRootStyles,
213
- children: /* @__PURE__ */ jsxs("button", {
214
- ref: buttonRef,
215
- type: "button",
216
- style: {
217
- ...triggerButtonStyles,
218
- ...positionStyle
219
- },
220
- onClick: () => {
221
- if (consumeDragClick()) return;
222
- setIsOpen(true);
223
- },
224
- title: "SQLite Sync Devtools",
225
- ...triggerHandlers,
226
- children: [/* @__PURE__ */ jsx("span", {
227
- style: triggerIconStyles,
228
- children: "◈"
229
- }), /* @__PURE__ */ jsx("span", {
230
- style: triggerCountStyles,
231
- children: instances.length
232
- })]
233
- })
234
- }), isOpen ? /* @__PURE__ */ jsx("div", {
235
- style: overlayStyles,
236
- onClick: () => setIsOpen(false),
237
- children: /* @__PURE__ */ jsxs("section", {
238
- className,
239
- role: "dialog",
240
- "aria-modal": "true",
241
- "aria-label": "SQLite Sync devtools",
242
- style: dialogStyles,
243
- onClick: (event) => event.stopPropagation(),
244
- children: [/* @__PURE__ */ jsxs("div", {
245
- style: headerStyles,
246
- children: [/* @__PURE__ */ jsxs("div", {
247
- style: headerLeftStyles,
248
- children: [/* @__PURE__ */ jsx("span", {
249
- style: headerLogoStyles,
250
- children: "◈"
251
- }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
252
- style: eyebrowStyles,
253
- children: "sqlite-sync"
254
- }), /* @__PURE__ */ jsx("h2", {
255
- style: titleStyles,
256
- children: "Devtools"
257
- })] })]
258
- }), /* @__PURE__ */ jsxs("div", {
259
- style: headerRightStyles,
260
- children: [
261
- instances.length > 0 && /* @__PURE__ */ jsxs("label", {
262
- style: instancePickerLabelStyles,
263
- children: [/* @__PURE__ */ jsx("span", {
264
- style: instancePickerTextStyles,
265
- children: "DB"
266
- }), /* @__PURE__ */ jsx("select", {
267
- value: selectedInstanceId,
268
- onChange: (event) => setSelectedInstanceId(event.target.value),
269
- style: instancePickerSelectStyles,
270
- children: instances.map((instance) => /* @__PURE__ */ jsx("option", {
271
- value: instance.instanceId,
272
- children: formatInstanceLabel(instance.dbId, instance.instanceId, dbIdCounts.get(instance.dbId) ?? 0)
273
- }, instance.instanceId))
274
- })]
275
- }),
276
- /* @__PURE__ */ jsxs("span", {
277
- style: instanceCountBadgeStyles,
278
- children: [
279
- instances.length,
280
- " instance",
281
- instances.length !== 1 ? "s" : ""
282
- ]
283
- }),
284
- /* @__PURE__ */ jsx("button", {
285
- type: "button",
286
- style: closeButtonStyles,
287
- onClick: () => setIsOpen(false),
288
- "aria-label": "Close",
289
- children: "✕"
290
- })
291
- ]
287
+ return /* @__PURE__ */ jsxs(ShadowRoot, {
288
+ className,
289
+ children: [hidden ? null : /* @__PURE__ */ jsx("div", {
290
+ className: "floatingRoot",
291
+ children: /* @__PURE__ */ jsxs("button", {
292
+ ref: buttonRef,
293
+ type: "button",
294
+ className: "triggerButton",
295
+ style: positionStyle,
296
+ onClick: () => {
297
+ if (consumeDragClick()) return;
298
+ setIsOpen(true);
299
+ },
300
+ title: `SQLite Sync Devtools (${TOGGLE_LAUNCHER_HINT} to hide)`,
301
+ ...triggerHandlers,
302
+ children: [/* @__PURE__ */ jsx("span", {
303
+ className: "triggerIcon",
304
+ children: "◈"
305
+ }), /* @__PURE__ */ jsx("span", {
306
+ className: "triggerCount",
307
+ children: instances.length
292
308
  })]
293
- }), instances.length === 0 ? /* @__PURE__ */ jsxs("div", {
294
- style: emptyStateStyles,
295
- children: [
296
- /* @__PURE__ */ jsx("div", {
297
- style: emptyStateIconStyles,
298
- children: "◈"
299
- }),
300
- /* @__PURE__ */ jsx("p", {
301
- style: emptyStateTextStyles,
302
- children: "No SQLite Sync database instances registered."
303
- }),
304
- /* @__PURE__ */ jsxs("p", {
305
- style: emptyStateSubtextStyles,
306
- children: [
307
- "Call ",
308
- /* @__PURE__ */ jsx("code", {
309
- style: inlineCodeStyles,
310
- children: "registerDevtools(db)"
311
- }),
312
- " in your app to get started."
313
- ]
314
- })
315
- ]
316
- }) : /* @__PURE__ */ jsxs("div", {
317
- style: contentLayoutStyles,
318
- children: [/* @__PURE__ */ jsxs("aside", {
319
- style: sidebarStyles,
320
- children: [/* @__PURE__ */ jsxs("nav", {
321
- style: navStyles,
309
+ })
310
+ }), isOpen ? /* @__PURE__ */ jsx("div", {
311
+ className: "overlay",
312
+ onClick: () => setIsOpen(false),
313
+ children: /* @__PURE__ */ jsxs("section", {
314
+ role: "dialog",
315
+ "aria-modal": "true",
316
+ "aria-label": "SQLite Sync devtools",
317
+ className: "dialog",
318
+ onClick: (event) => event.stopPropagation(),
319
+ children: [/* @__PURE__ */ jsxs("div", {
320
+ className: "header",
321
+ children: [/* @__PURE__ */ jsxs("div", {
322
+ className: "headerLeft",
323
+ children: [/* @__PURE__ */ jsx("span", {
324
+ className: "headerLogo",
325
+ children: "◈"
326
+ }), /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsx("div", {
327
+ className: "eyebrow",
328
+ children: "sqlite-sync"
329
+ }), /* @__PURE__ */ jsx("h2", {
330
+ className: "title",
331
+ children: "Devtools"
332
+ })] })]
333
+ }), /* @__PURE__ */ jsxs("div", {
334
+ className: "headerRight",
322
335
  children: [
323
- /* @__PURE__ */ jsxs("button", {
324
- type: "button",
325
- style: getTabButtonStyles(activeTab === "overview"),
326
- onClick: () => setActiveTab("overview"),
336
+ instances.length > 0 && /* @__PURE__ */ jsxs("label", {
337
+ className: "instancePickerLabel",
327
338
  children: [/* @__PURE__ */ jsx("span", {
328
- style: navIconStyles,
329
- children: "▦"
330
- }), "Overview"]
339
+ className: "instancePickerText",
340
+ children: "DB"
341
+ }), /* @__PURE__ */ jsx("select", {
342
+ value: selectedInstanceId,
343
+ onChange: (event) => setSelectedInstanceId(event.target.value),
344
+ className: "instancePickerSelect",
345
+ children: instances.map((instance) => /* @__PURE__ */ jsx("option", {
346
+ value: instance.instanceId,
347
+ children: formatInstanceLabel(instance.dbId, instance.instanceId, dbIdCounts.get(instance.dbId) ?? 0)
348
+ }, instance.instanceId))
349
+ })]
331
350
  }),
332
- /* @__PURE__ */ jsxs("button", {
333
- type: "button",
334
- style: getTabButtonStyles(activeTab === "schema"),
335
- onClick: () => setActiveTab("schema"),
336
- children: [/* @__PURE__ */ jsx("span", {
337
- style: navIconStyles,
338
- children: "⬡"
339
- }), "Schema"]
340
- }),
341
- /* @__PURE__ */ jsxs("button", {
342
- type: "button",
343
- style: getTabButtonStyles(activeTab === "live-queries"),
344
- onClick: () => setActiveTab("live-queries"),
345
- children: [/* @__PURE__ */ jsx("span", {
346
- style: navIconStyles,
347
- children: "◉"
348
- }), "Live Queries"]
351
+ /* @__PURE__ */ jsxs("span", {
352
+ className: "instanceCountBadge",
353
+ children: [
354
+ instances.length,
355
+ " instance",
356
+ instances.length !== 1 ? "s" : ""
357
+ ]
349
358
  }),
350
- /* @__PURE__ */ jsxs("button", {
359
+ canToggle && /* @__PURE__ */ jsx("button", {
351
360
  type: "button",
352
- style: getTabButtonStyles(activeTab === "event-log"),
353
- onClick: () => setActiveTab("event-log"),
354
- children: [/* @__PURE__ */ jsx("span", {
355
- style: navIconStyles,
356
- children: "≡"
357
- }), "Event Log"]
361
+ className: "hideLauncherButton",
362
+ onClick: () => setHidden(true),
363
+ title: `Hide launcher (${TOGGLE_LAUNCHER_HINT} to show again)`,
364
+ children: "Hide"
358
365
  }),
359
- /* @__PURE__ */ jsxs("button", {
366
+ /* @__PURE__ */ jsx("button", {
360
367
  type: "button",
361
- style: getTabButtonStyles(activeTab === "query-runner"),
362
- onClick: () => setActiveTab("query-runner"),
363
- children: [/* @__PURE__ */ jsx("span", {
364
- style: navIconStyles,
365
- children: "▶"
366
- }), "Query Runner"]
367
- })
368
- ]
369
- }), selectedInstance && /* @__PURE__ */ jsxs("div", {
370
- style: sidebarInfoStyles,
371
- children: [
372
- /* @__PURE__ */ jsx("div", {
373
- style: sidebarInfoLabelStyles,
374
- children: "Active instance"
375
- }),
376
- /* @__PURE__ */ jsx("div", {
377
- style: sidebarInfoValueStyles,
378
- children: formatInstanceLabel(selectedInstance.dbId, selectedInstance.instanceId, dbIdCounts.get(selectedInstance.dbId) ?? 0)
379
- }),
380
- /* @__PURE__ */ jsxs("div", {
381
- style: sidebarInfoSubStyles,
382
- children: [
383
- "id: ",
384
- selectedInstance.instanceId.slice(0, 12),
385
- "…"
386
- ]
368
+ className: "closeButton",
369
+ onClick: () => setIsOpen(false),
370
+ "aria-label": "Close",
371
+ children: "✕"
387
372
  })
388
373
  ]
389
374
  })]
390
- }), /* @__PURE__ */ jsx("div", {
391
- style: mainPaneStyles,
392
- children: activeTab === "overview" ? /* @__PURE__ */ jsx(OverviewTab, {
393
- selectedInstance,
394
- dbIdCounts
395
- }) : activeTab === "schema" ? /* @__PURE__ */ jsx(SchemaTab, { selectedInstance }) : activeTab === "live-queries" ? /* @__PURE__ */ jsx(LiveQueriesTab, { selectedInstance }) : activeTab === "event-log" ? /* @__PURE__ */ jsx(EventLogTab, { selectedInstance }) : /* @__PURE__ */ jsx(QueryRunnerTab, {
396
- selectedInstance,
397
- queryTarget,
398
- setQueryTarget,
399
- query,
400
- setQuery,
401
- queryState,
402
- canRunQuery,
403
- runQuery
404
- })
375
+ }), instances.length === 0 ? /* @__PURE__ */ jsxs("div", {
376
+ className: "emptyState",
377
+ children: [
378
+ /* @__PURE__ */ jsx("div", {
379
+ className: "emptyStateIcon",
380
+ children: "◈"
381
+ }),
382
+ /* @__PURE__ */ jsx("p", {
383
+ className: "emptyStateText",
384
+ children: "No SQLite Sync database instances registered."
385
+ }),
386
+ /* @__PURE__ */ jsxs("p", {
387
+ className: "emptyStateSubtext",
388
+ children: [
389
+ "Call ",
390
+ /* @__PURE__ */ jsx("code", {
391
+ className: "inlineCode",
392
+ children: "registerDevtools(db)"
393
+ }),
394
+ " in your app to get started."
395
+ ]
396
+ })
397
+ ]
398
+ }) : /* @__PURE__ */ jsxs("div", {
399
+ className: "contentLayout",
400
+ children: [/* @__PURE__ */ jsxs("aside", {
401
+ className: "sidebar",
402
+ children: [/* @__PURE__ */ jsxs("nav", {
403
+ className: "nav",
404
+ children: [
405
+ /* @__PURE__ */ jsxs("button", {
406
+ type: "button",
407
+ className: "tabButton",
408
+ "data-active": activeTab === "overview" ? "true" : void 0,
409
+ onClick: () => setActiveTab("overview"),
410
+ children: [/* @__PURE__ */ jsx("span", {
411
+ className: "navIcon",
412
+ children: "▦"
413
+ }), "Overview"]
414
+ }),
415
+ /* @__PURE__ */ jsxs("button", {
416
+ type: "button",
417
+ className: "tabButton",
418
+ "data-active": activeTab === "schema" ? "true" : void 0,
419
+ onClick: () => setActiveTab("schema"),
420
+ children: [/* @__PURE__ */ jsx("span", {
421
+ className: "navIcon",
422
+ children: "⬡"
423
+ }), "Schema"]
424
+ }),
425
+ /* @__PURE__ */ jsxs("button", {
426
+ type: "button",
427
+ className: "tabButton",
428
+ "data-active": activeTab === "live-queries" ? "true" : void 0,
429
+ onClick: () => setActiveTab("live-queries"),
430
+ children: [/* @__PURE__ */ jsx("span", {
431
+ className: "navIcon",
432
+ children: "◉"
433
+ }), "Live Queries"]
434
+ }),
435
+ /* @__PURE__ */ jsxs("button", {
436
+ type: "button",
437
+ className: "tabButton",
438
+ "data-active": activeTab === "event-log" ? "true" : void 0,
439
+ onClick: () => setActiveTab("event-log"),
440
+ children: [/* @__PURE__ */ jsx("span", {
441
+ className: "navIcon",
442
+ children: "≡"
443
+ }), "Event Log"]
444
+ }),
445
+ /* @__PURE__ */ jsxs("button", {
446
+ type: "button",
447
+ className: "tabButton",
448
+ "data-active": activeTab === "query-runner" ? "true" : void 0,
449
+ onClick: () => setActiveTab("query-runner"),
450
+ children: [/* @__PURE__ */ jsx("span", {
451
+ className: "navIcon",
452
+ children: "▶"
453
+ }), "Query Runner"]
454
+ })
455
+ ]
456
+ }), selectedInstance && /* @__PURE__ */ jsxs("div", {
457
+ className: "sidebarInfo",
458
+ children: [
459
+ /* @__PURE__ */ jsx("div", {
460
+ className: "sidebarInfoLabel",
461
+ children: "Active instance"
462
+ }),
463
+ /* @__PURE__ */ jsx("div", {
464
+ className: "sidebarInfoValue",
465
+ children: formatInstanceLabel(selectedInstance.dbId, selectedInstance.instanceId, dbIdCounts.get(selectedInstance.dbId) ?? 0)
466
+ }),
467
+ /* @__PURE__ */ jsxs("div", {
468
+ className: "sidebarInfoSub",
469
+ children: [
470
+ "id: ",
471
+ selectedInstance.instanceId.slice(0, 12),
472
+ "…"
473
+ ]
474
+ })
475
+ ]
476
+ })]
477
+ }), /* @__PURE__ */ jsx("div", {
478
+ className: "mainPane",
479
+ children: activeTab === "overview" ? /* @__PURE__ */ jsx(OverviewTab, {
480
+ selectedInstance,
481
+ dbIdCounts
482
+ }) : activeTab === "schema" ? /* @__PURE__ */ jsx(SchemaTab, { selectedInstance }) : activeTab === "live-queries" ? /* @__PURE__ */ jsx(LiveQueriesTab, { selectedInstance }) : activeTab === "event-log" ? /* @__PURE__ */ jsx(EventLogTab, { selectedInstance }) : /* @__PURE__ */ jsx(QueryRunnerTab, {
483
+ selectedInstance,
484
+ queryTarget,
485
+ setQueryTarget,
486
+ query,
487
+ setQuery,
488
+ queryState,
489
+ canRunQuery,
490
+ runQuery
491
+ })
492
+ })]
405
493
  })]
406
- })]
407
- })
408
- }) : null] });
494
+ })
495
+ }) : null]
496
+ });
409
497
  }
410
498
  const EVENT_HLC_ACCUMULATOR_QUERY = `SELECT value FROM "worker"."kv" WHERE key = 'crdt.consistency.event_hlc_sum.v2'`;
499
+ const EVENT_STATUS_COUNTS_QUERY = `SELECT status, COUNT(*) AS count FROM "worker"."crdt_events" GROUP BY status`;
500
+ const MEMORY_DB_SIZE_QUERY = "SELECT (SELECT page_count FROM pragma_page_count()) * (SELECT page_size FROM pragma_page_size()) AS bytes";
501
+ const WORKER_DB_SIZE_QUERY = `SELECT
502
+ (SELECT page_count FROM pragma_page_count('main')) * (SELECT page_size FROM pragma_page_size('main'))
503
+ + (SELECT page_count FROM pragma_page_count('worker')) * (SELECT page_size FROM pragma_page_size('worker'))
504
+ AS bytes`;
505
+ const EVENT_STATUS_META = {
506
+ pending: {
507
+ label: "Pending",
508
+ tone: "amber"
509
+ },
510
+ applied: {
511
+ label: "Applied",
512
+ tone: "success"
513
+ },
514
+ failed: {
515
+ label: "Failed",
516
+ tone: "error"
517
+ },
518
+ deduped: {
519
+ label: "Deduped",
520
+ tone: "muted"
521
+ }
522
+ };
523
+ function isEventStatus(value) {
524
+ return value === "pending" || value === "applied" || value === "failed" || value === "deduped";
525
+ }
526
+ function emptyEventStatusCounts() {
527
+ return {
528
+ total: 0,
529
+ pending: 0,
530
+ applied: 0,
531
+ failed: 0,
532
+ deduped: 0
533
+ };
534
+ }
535
+ function parseEventStatusCounts(rows) {
536
+ const counts = emptyEventStatusCounts();
537
+ for (const row of rows) {
538
+ if (typeof row !== "object" || row === null || !("status" in row) || !("count" in row)) continue;
539
+ const { status, count } = row;
540
+ if (!isEventStatus(status)) continue;
541
+ const n = typeof count === "number" ? count : Number(count);
542
+ if (!Number.isFinite(n)) continue;
543
+ counts[status] = n;
544
+ counts.total += n;
545
+ }
546
+ return counts;
547
+ }
548
+ function readCount(rows) {
549
+ const row = rows[0];
550
+ if (typeof row !== "object" || row === null || !("count" in row)) return 0;
551
+ const n = typeof row.count === "number" ? row.count : Number(row.count);
552
+ return Number.isFinite(n) ? n : 0;
553
+ }
554
+ function readBytes(rows) {
555
+ const row = rows[0];
556
+ if (typeof row !== "object" || row === null || !("bytes" in row)) return null;
557
+ const n = typeof row.bytes === "number" ? row.bytes : Number(row.bytes);
558
+ return Number.isFinite(n) ? n : null;
559
+ }
560
+ function formatByteSize(bytes) {
561
+ const exact = `${bytes.toLocaleString("en-US")} B`;
562
+ if (bytes < 1024) return {
563
+ label: exact,
564
+ exact
565
+ };
566
+ const units = [
567
+ "KB",
568
+ "MB",
569
+ "GB",
570
+ "TB"
571
+ ];
572
+ let value = bytes / 1024;
573
+ let unitIndex = 0;
574
+ while (value >= 1024 && unitIndex < units.length - 1) {
575
+ value /= 1024;
576
+ unitIndex += 1;
577
+ }
578
+ const decimals = value >= 100 ? 0 : value >= 10 ? 1 : 2;
579
+ return {
580
+ label: `${Number(value.toFixed(decimals))} ${units[unitIndex]}`,
581
+ exact
582
+ };
583
+ }
411
584
  function OverviewTab({ selectedInstance, dbIdCounts }) {
412
585
  const [eventHlcAccumulator, setEventHlcAccumulator] = useState(null);
413
586
  const [accumulatorError, setAccumulatorError] = useState(null);
414
587
  const [isAccumulatorLoading, setIsAccumulatorLoading] = useState(false);
588
+ const [eventCounts, setEventCounts] = useState(null);
589
+ const [eventCountsError, setEventCountsError] = useState(null);
590
+ const [isEventCountsLoading, setIsEventCountsLoading] = useState(false);
591
+ const [memoryBytes, setMemoryBytes] = useState(null);
592
+ const [memorySizeError, setMemorySizeError] = useState(null);
593
+ const [workerBytes, setWorkerBytes] = useState(null);
594
+ const [workerSizeError, setWorkerSizeError] = useState(null);
595
+ const [isSizeLoading, setIsSizeLoading] = useState(false);
415
596
  const refreshEventHlcAccumulator = useCallback(async () => {
416
597
  if (!selectedInstance) return;
417
598
  setIsAccumulatorLoading(true);
@@ -421,7 +602,8 @@ function OverviewTab({ selectedInstance, dbIdCounts }) {
421
602
  sql: EVENT_HLC_ACCUMULATOR_QUERY,
422
603
  parameters: []
423
604
  })).rows[0];
424
- setEventHlcAccumulator(row?.value ?? "");
605
+ const value = typeof row === "object" && row !== null && "value" in row && typeof row.value === "string" ? row.value : "";
606
+ setEventHlcAccumulator(value);
425
607
  } catch (error) {
426
608
  setAccumulatorError(error instanceof Error ? error.message : String(error));
427
609
  setEventHlcAccumulator(null);
@@ -429,135 +611,201 @@ function OverviewTab({ selectedInstance, dbIdCounts }) {
429
611
  setIsAccumulatorLoading(false);
430
612
  }
431
613
  }, [selectedInstance]);
614
+ const refreshEventCounts = useCallback(async () => {
615
+ if (!selectedInstance) return;
616
+ setIsEventCountsLoading(true);
617
+ setEventCountsError(null);
618
+ try {
619
+ const result = await selectedInstance.instance._internal.executeAsync({
620
+ sql: EVENT_STATUS_COUNTS_QUERY,
621
+ parameters: []
622
+ });
623
+ setEventCounts(parseEventStatusCounts(result.rows));
624
+ } catch (error) {
625
+ setEventCountsError(error instanceof Error ? error.message : String(error));
626
+ setEventCounts(null);
627
+ } finally {
628
+ setIsEventCountsLoading(false);
629
+ }
630
+ }, [selectedInstance]);
631
+ const refreshDbSizes = useCallback(async () => {
632
+ if (!selectedInstance) return;
633
+ setIsSizeLoading(true);
634
+ setMemorySizeError(null);
635
+ setWorkerSizeError(null);
636
+ const memoryResult = Promise.resolve().then(() => selectedInstance.instance.db.execute(MEMORY_DB_SIZE_QUERY)).then((result) => {
637
+ const bytes = readBytes(result.rows);
638
+ if (bytes === null) throw new Error("Memory DB did not return a size.");
639
+ return bytes;
640
+ });
641
+ const workerResult = selectedInstance.instance._internal.executeAsync({
642
+ sql: WORKER_DB_SIZE_QUERY,
643
+ parameters: []
644
+ }).then((result) => {
645
+ const bytes = readBytes(result.rows);
646
+ if (bytes === null) throw new Error("Worker DB did not return a size.");
647
+ return bytes;
648
+ });
649
+ const [memorySettled, workerSettled] = await Promise.allSettled([memoryResult, workerResult]);
650
+ if (memorySettled.status === "fulfilled") setMemoryBytes(memorySettled.value);
651
+ else {
652
+ setMemoryBytes(null);
653
+ setMemorySizeError(memorySettled.reason instanceof Error ? memorySettled.reason.message : String(memorySettled.reason));
654
+ }
655
+ if (workerSettled.status === "fulfilled") setWorkerBytes(workerSettled.value);
656
+ else {
657
+ setWorkerBytes(null);
658
+ setWorkerSizeError(workerSettled.reason instanceof Error ? workerSettled.reason.message : String(workerSettled.reason));
659
+ }
660
+ setIsSizeLoading(false);
661
+ }, [selectedInstance]);
432
662
  useEffect(() => {
433
663
  setEventHlcAccumulator(null);
434
664
  setAccumulatorError(null);
665
+ setEventCounts(null);
666
+ setEventCountsError(null);
667
+ setMemoryBytes(null);
668
+ setMemorySizeError(null);
669
+ setWorkerBytes(null);
670
+ setWorkerSizeError(null);
435
671
  refreshEventHlcAccumulator();
436
- }, [refreshEventHlcAccumulator]);
672
+ refreshEventCounts();
673
+ refreshDbSizes();
674
+ }, [
675
+ refreshEventHlcAccumulator,
676
+ refreshEventCounts,
677
+ refreshDbSizes
678
+ ]);
437
679
  if (!selectedInstance) return null;
438
680
  const label = formatInstanceLabel(selectedInstance.dbId, selectedInstance.instanceId, dbIdCounts.get(selectedInstance.dbId) ?? 0);
439
- const crdtTables = selectedInstance.instance._internal.crdtTableNames;
440
681
  return /* @__PURE__ */ jsxs("div", {
441
- style: overviewLayoutStyles,
682
+ className: "overviewLayout",
442
683
  children: [
443
684
  /* @__PURE__ */ jsxs("div", {
444
- style: overviewCardsRowStyles,
685
+ className: "overviewCardsRow",
445
686
  children: [
446
687
  /* @__PURE__ */ jsxs("div", {
447
- style: overviewCardStyles,
688
+ className: "overviewCard",
448
689
  children: [/* @__PURE__ */ jsx("div", {
449
- style: overviewCardLabelStyles,
690
+ className: "overviewCardLabel",
450
691
  children: "Database"
451
692
  }), /* @__PURE__ */ jsx("div", {
452
- style: overviewCardValueStyles,
693
+ className: "overviewCardValue",
453
694
  children: label
454
695
  })]
455
696
  }),
456
697
  /* @__PURE__ */ jsxs("div", {
457
- style: overviewCardStyles,
698
+ className: "overviewCard",
458
699
  children: [/* @__PURE__ */ jsx("div", {
459
- style: overviewCardLabelStyles,
700
+ className: "overviewCardLabel",
460
701
  children: "Instance ID"
461
702
  }), /* @__PURE__ */ jsxs("div", {
462
- style: {
463
- ...overviewCardValueStyles,
464
- fontFamily: "ui-monospace, monospace",
465
- fontSize: "0.78rem"
466
- },
703
+ className: "overviewCardValue mono sm",
467
704
  children: [selectedInstance.instanceId.slice(0, 16), "…"]
468
705
  })]
469
706
  }),
707
+ /* @__PURE__ */ jsx(DbSizeCard, {
708
+ label: "Memory DB",
709
+ bytes: memoryBytes,
710
+ error: memorySizeError,
711
+ loading: isSizeLoading
712
+ }),
713
+ /* @__PURE__ */ jsx(DbSizeCard, {
714
+ label: "Worker DB",
715
+ bytes: workerBytes,
716
+ error: workerSizeError,
717
+ loading: isSizeLoading
718
+ }),
470
719
  /* @__PURE__ */ jsxs("div", {
471
- style: overviewCardStyles,
720
+ className: "overviewCard",
472
721
  children: [/* @__PURE__ */ jsx("div", {
473
- style: overviewCardLabelStyles,
474
- children: "CRDT Tables"
722
+ className: "overviewCardLabel",
723
+ children: "CRDT Events"
475
724
  }), /* @__PURE__ */ jsx("div", {
476
- style: overviewCardValueStyles,
477
- children: crdtTables.length
725
+ className: "overviewCardValue",
726
+ children: eventCountsError ? "—" : isEventCountsLoading && eventCounts === null ? "…" : eventCounts?.total ?? 0
478
727
  })]
479
728
  })
480
729
  ]
481
730
  }),
482
731
  /* @__PURE__ */ jsxs("div", {
483
- style: overviewSectionStyles,
484
- children: [/* @__PURE__ */ jsx("div", {
485
- style: overviewSectionTitleStyles,
486
- children: "CRDT Tables"
487
- }), crdtTables.length === 0 ? /* @__PURE__ */ jsx("div", {
488
- style: overviewEmptyStyles,
489
- children: "No CRDT tables registered."
490
- }) : /* @__PURE__ */ jsx("div", {
491
- style: crdtTableListStyles,
492
- children: [...crdtTables].map((name) => /* @__PURE__ */ jsxs("div", {
493
- style: crdtTableRowStyles,
494
- children: [/* @__PURE__ */ jsx("span", {
495
- style: crdtTableIconStyles,
496
- children: "▦"
497
- }), /* @__PURE__ */ jsx("span", {
498
- style: crdtTableNameStyles,
499
- children: name
732
+ className: "overviewSection",
733
+ children: [
734
+ /* @__PURE__ */ jsxs("div", {
735
+ className: "overviewSectionHeader",
736
+ children: [/* @__PURE__ */ jsx("div", {
737
+ className: "overviewSectionTitle",
738
+ children: "CRDT events"
739
+ }), /* @__PURE__ */ jsxs("button", {
740
+ type: "button",
741
+ className: "refreshButton",
742
+ disabled: isEventCountsLoading || isSizeLoading,
743
+ title: "Refresh event counts and DB sizes",
744
+ onClick: () => {
745
+ refreshEventCounts();
746
+ refreshDbSizes();
747
+ },
748
+ children: [isEventCountsLoading || isSizeLoading ? "…" : "↻", " Refresh"]
500
749
  })]
501
- }, name))
502
- })]
750
+ }),
751
+ eventCountsError ? /* @__PURE__ */ jsx("div", {
752
+ className: "overviewAccumulatorError",
753
+ children: eventCountsError
754
+ }) : /* @__PURE__ */ jsx("div", {
755
+ className: "eventCountGrid",
756
+ children: [
757
+ "pending",
758
+ "applied",
759
+ "failed",
760
+ "deduped"
761
+ ].map((status) => {
762
+ const item = EVENT_STATUS_META[status];
763
+ const value = eventCounts?.[status] ?? 0;
764
+ return /* @__PURE__ */ jsxs("div", {
765
+ className: "eventCountCard",
766
+ children: [/* @__PURE__ */ jsx("div", {
767
+ className: "eventCountValue",
768
+ "data-tone": item.tone,
769
+ "data-emphasize": value > 0 ? "true" : void 0,
770
+ children: isEventCountsLoading && eventCounts === null ? "…" : value
771
+ }), /* @__PURE__ */ jsx("div", {
772
+ className: "overviewCardLabel",
773
+ children: item.label
774
+ })]
775
+ }, status);
776
+ })
777
+ }),
778
+ /* @__PURE__ */ jsx("div", {
779
+ className: "overviewEmpty",
780
+ children: "Counts are remaining worker log rows after GC, not lifetime history."
781
+ })
782
+ ]
503
783
  }),
504
784
  /* @__PURE__ */ jsxs("div", {
505
- style: overviewSectionStyles,
785
+ className: "overviewSection",
506
786
  children: [/* @__PURE__ */ jsxs("div", {
507
- style: overviewSectionHeaderStyles,
787
+ className: "overviewSectionHeader",
508
788
  children: [/* @__PURE__ */ jsx("div", {
509
- style: overviewSectionTitleStyles,
789
+ className: "overviewSectionTitle",
510
790
  children: "Event HLC accumulator"
511
791
  }), /* @__PURE__ */ jsxs("button", {
512
792
  type: "button",
513
- style: overviewRefreshButtonStyles,
793
+ className: "refreshButton",
514
794
  disabled: isAccumulatorLoading,
515
795
  onClick: () => void refreshEventHlcAccumulator(),
516
796
  children: [isAccumulatorLoading ? "…" : "↻", " Refresh"]
517
797
  })]
518
798
  }), /* @__PURE__ */ jsx("div", {
519
- style: overviewAccumulatorValueStyles,
799
+ className: "overviewAccumulatorValue",
520
800
  children: accumulatorError ? /* @__PURE__ */ jsx("span", {
521
- style: overviewAccumulatorErrorStyles,
801
+ className: "overviewAccumulatorError",
522
802
  children: accumulatorError
523
803
  }) : isAccumulatorLoading && eventHlcAccumulator === null ? "Loading…" : eventHlcAccumulator === "" ? /* @__PURE__ */ jsx("span", {
524
- style: overviewAccumulatorEmptyStyles,
804
+ className: "overviewAccumulatorEmpty",
525
805
  children: "(empty)"
526
806
  }) : eventHlcAccumulator
527
807
  })]
528
808
  }),
529
- /* @__PURE__ */ jsxs("div", {
530
- style: overviewSectionStyles,
531
- children: [
532
- /* @__PURE__ */ jsx("div", {
533
- style: overviewSectionTitleStyles,
534
- children: "Write Permissions"
535
- }),
536
- /* @__PURE__ */ jsxs("div", {
537
- style: permissionRowStyles,
538
- children: [/* @__PURE__ */ jsx("span", {
539
- style: permissionIconStyles,
540
- children: "✓"
541
- }), /* @__PURE__ */ jsx("span", {
542
- style: permissionTextStyles,
543
- children: "Memory DB — CRDT tables only"
544
- })]
545
- }),
546
- /* @__PURE__ */ jsxs("div", {
547
- style: permissionRowStyles,
548
- children: [/* @__PURE__ */ jsx("span", {
549
- style: {
550
- ...permissionIconStyles,
551
- color: "#f59e0b"
552
- },
553
- children: "⊘"
554
- }), /* @__PURE__ */ jsx("span", {
555
- style: permissionTextStyles,
556
- children: "Worker DB — read-only (SELECT, PRAGMA, EXPLAIN)"
557
- })]
558
- })
559
- ]
560
- }),
561
809
  /* @__PURE__ */ jsx(DataSection, {
562
810
  dbId: selectedInstance.dbId,
563
811
  instance: selectedInstance.instance
@@ -569,6 +817,38 @@ function OverviewTab({ selectedInstance, dbIdCounts }) {
569
817
  ]
570
818
  });
571
819
  }
820
+ function DbSizeCard({ label, bytes, error, loading }) {
821
+ let value = "…";
822
+ let sub;
823
+ let title;
824
+ if (error) {
825
+ value = "—";
826
+ title = error;
827
+ } else if (bytes !== null) {
828
+ const formatted = formatByteSize(bytes);
829
+ value = formatted.label;
830
+ title = formatted.exact;
831
+ sub = formatted.label === formatted.exact ? void 0 : formatted.exact;
832
+ } else if (!loading) value = "—";
833
+ return /* @__PURE__ */ jsxs("div", {
834
+ className: "overviewCard",
835
+ title,
836
+ children: [
837
+ /* @__PURE__ */ jsx("div", {
838
+ className: "overviewCardLabel",
839
+ children: label
840
+ }),
841
+ /* @__PURE__ */ jsx("div", {
842
+ className: "overviewCardValue mono",
843
+ children: value
844
+ }),
845
+ sub ? /* @__PURE__ */ jsx("div", {
846
+ className: "overviewCardSub",
847
+ children: sub
848
+ }) : null
849
+ ]
850
+ });
851
+ }
572
852
  function DataSection({ dbId, instance }) {
573
853
  const fileInputRef = useRef(null);
574
854
  const [isImporting, setIsImporting] = useState(false);
@@ -616,28 +896,28 @@ function DataSection({ dbId, instance }) {
616
896
  }
617
897
  };
618
898
  return /* @__PURE__ */ jsxs("div", {
619
- style: overviewSectionStyles,
899
+ className: "overviewSection",
620
900
  children: [
621
901
  /* @__PURE__ */ jsx("div", {
622
- style: overviewSectionTitleStyles,
902
+ className: "overviewSectionTitle",
623
903
  children: "Backup & Restore"
624
904
  }),
625
905
  /* @__PURE__ */ jsx("div", {
626
- style: dangerZoneDescStyles,
906
+ className: "dangerZoneDesc",
627
907
  children: "Export the active rows of every CRDT table as JSON, or import a dump to seed/restore. Import overwrites rows with matching ids and is propagated to the server."
628
908
  }),
629
909
  /* @__PURE__ */ jsxs("div", {
630
- style: dataActionsStyles,
910
+ className: "dataActions",
631
911
  children: [
632
912
  /* @__PURE__ */ jsx("button", {
633
913
  type: "button",
634
- style: dataButtonStyles,
914
+ className: "dataButton",
635
915
  onClick: handleExport,
636
916
  children: "↓ Export JSON"
637
917
  }),
638
918
  /* @__PURE__ */ jsx("button", {
639
919
  type: "button",
640
- style: dataButtonStyles,
920
+ className: "dataButton",
641
921
  disabled: isImporting,
642
922
  onClick: () => fileInputRef.current?.click(),
643
923
  children: isImporting ? "Importing…" : "↑ Import JSON"
@@ -646,7 +926,7 @@ function DataSection({ dbId, instance }) {
646
926
  ref: fileInputRef,
647
927
  type: "file",
648
928
  accept: "application/json,.json",
649
- style: { display: "none" },
929
+ className: "hiddenInput",
650
930
  onChange: (event) => {
651
931
  const file = event.target.files?.[0];
652
932
  event.target.value = "";
@@ -656,7 +936,7 @@ function DataSection({ dbId, instance }) {
656
936
  ]
657
937
  }),
658
938
  status && /* @__PURE__ */ jsx("div", {
659
- style: status.kind === "error" ? dataStatusErrorStyles : dataStatusSuccessStyles,
939
+ className: status.kind === "error" ? "dataStatusError" : "dataStatusSuccess",
660
940
  children: status.message
661
941
  })
662
942
  ]
@@ -670,164 +950,209 @@ function ResetSection({ dbId, instance }) {
670
950
  instance.requestReload({ clean: true });
671
951
  };
672
952
  return /* @__PURE__ */ jsxs("div", {
673
- style: dangerZoneStyles,
953
+ className: "dangerZone",
674
954
  children: [/* @__PURE__ */ jsx("div", {
675
- style: dangerZoneTitleStyles,
955
+ className: "dangerZoneTitle",
676
956
  children: "Danger Zone"
677
957
  }), /* @__PURE__ */ jsxs("div", {
678
- style: dangerZoneRowStyles,
958
+ className: "dangerZoneRow",
679
959
  children: [/* @__PURE__ */ jsxs("div", {
680
- style: dangerZoneDescStyles,
960
+ className: "dangerZoneDesc",
681
961
  children: [
682
962
  "Requests a clean reload, so ",
683
963
  /* @__PURE__ */ jsx("code", {
684
- style: inlineCodeStyles,
964
+ className: "inlineCode",
685
965
  children: dbId
686
966
  }),
687
967
  " is wiped on next load via",
688
968
  " ",
689
969
  /* @__PURE__ */ jsx("code", {
690
- style: inlineCodeStyles,
970
+ className: "inlineCode",
691
971
  children: "clearOnInit"
692
972
  }),
693
973
  ", then reloads all tabs."
694
974
  ]
695
975
  }), confirming ? /* @__PURE__ */ jsxs("div", {
696
- style: dangerZoneActionsStyles,
976
+ className: "dangerZoneActions",
697
977
  children: [/* @__PURE__ */ jsx("button", {
698
978
  type: "button",
699
- style: resetCancelButtonStyles,
979
+ className: "resetCancelButton",
700
980
  disabled: isResetting,
701
981
  onClick: () => setConfirming(false),
702
982
  children: "Cancel"
703
983
  }), /* @__PURE__ */ jsx("button", {
704
984
  type: "button",
705
- style: resetConfirmButtonStyles,
985
+ className: "resetConfirmButton",
706
986
  disabled: isResetting,
707
987
  onClick: handleReset,
708
988
  children: isResetting ? "Resetting…" : "Confirm reset"
709
989
  })]
710
990
  }) : /* @__PURE__ */ jsx("button", {
711
991
  type: "button",
712
- style: resetButtonStyles,
992
+ className: "resetButton",
713
993
  onClick: () => setConfirming(true),
714
994
  children: "Reset DB"
715
995
  })]
716
996
  })]
717
997
  });
718
998
  }
999
+ const SYSTEM_COLUMNS = /* @__PURE__ */ new Set(["id", "tombstone"]);
1000
+ function formatDefaultValue(value) {
1001
+ if (typeof value === "string") return JSON.stringify(value);
1002
+ if (typeof value === "boolean" || typeof value === "number") return String(value);
1003
+ if (value === null) return "null";
1004
+ return JSON.stringify(value);
1005
+ }
719
1006
  function SchemaTab({ selectedInstance }) {
720
1007
  if (!selectedInstance) return null;
721
- const { crdtTablesConfig, schemaVersion, migrationVersions } = selectedInstance.instance._internal;
722
- const latestVersion = migrationVersions.at(-1) ?? 0;
723
- return /* @__PURE__ */ jsxs("div", {
724
- style: schemaLayoutStyles,
725
- children: [/* @__PURE__ */ jsxs("div", {
726
- style: schemaSectionStyles,
727
- children: [/* @__PURE__ */ jsxs("div", {
728
- style: schemaSectionHeaderStyles,
729
- children: [/* @__PURE__ */ jsx("div", {
730
- style: schemaSectionTitleStyles,
731
- children: "CRDT Tables"
732
- }), /* @__PURE__ */ jsx("span", {
733
- style: schemaBadgeStyles,
734
- children: crdtTablesConfig.length
735
- })]
736
- }), /* @__PURE__ */ jsxs("div", {
737
- style: schemaTableGridStyles,
738
- children: [/* @__PURE__ */ jsxs("div", {
739
- style: schemaTableHeaderRowStyles,
740
- children: [
741
- /* @__PURE__ */ jsx("div", {
742
- style: schemaColHeaderStyles,
743
- children: "Base Table"
744
- }),
745
- /* @__PURE__ */ jsx("div", {
746
- style: schemaColHeaderStyles,
747
- children: "CRDT Table"
748
- }),
749
- /* @__PURE__ */ jsx("div", {
750
- style: {
751
- ...schemaColHeaderStyles,
752
- textAlign: "center"
753
- },
754
- children: "Status"
755
- })
756
- ]
757
- }), crdtTablesConfig.map((table) => /* @__PURE__ */ jsxs("div", {
758
- style: schemaTableRowStyles,
759
- children: [
760
- /* @__PURE__ */ jsxs("div", {
761
- style: schemaTableNameStyles,
762
- children: [/* @__PURE__ */ jsx("span", {
763
- style: schemaTableIconStyles,
764
- children: "▦"
765
- }), table.baseTableName]
766
- }),
767
- /* @__PURE__ */ jsx("div", {
768
- style: schemaCrdtNameStyles,
769
- children: table.crdtTableName
770
- }),
771
- /* @__PURE__ */ jsx("div", {
772
- style: schemaStatusCellStyles,
773
- children: /* @__PURE__ */ jsx("span", {
774
- style: schemaActiveTagStyles,
775
- children: "active"
776
- })
777
- })
778
- ]
779
- }, table.crdtTableName))]
780
- })]
781
- }), /* @__PURE__ */ jsxs("div", {
782
- style: schemaSectionStyles,
1008
+ const { tables, schemaVersion } = selectedInstance.instance._internal;
1009
+ const tableEntries = Object.entries(tables);
1010
+ return /* @__PURE__ */ jsx("div", {
1011
+ className: "schemaLayout",
1012
+ children: /* @__PURE__ */ jsxs("div", {
1013
+ className: "schemaSection",
783
1014
  children: [/* @__PURE__ */ jsxs("div", {
784
- style: schemaSectionHeaderStyles,
785
- children: [/* @__PURE__ */ jsx("div", {
786
- style: schemaSectionTitleStyles,
787
- children: "Migrations"
788
- }), /* @__PURE__ */ jsxs("div", {
789
- style: schemaMigrationsMetaStyles,
790
- children: [/* @__PURE__ */ jsxs("span", {
791
- style: schemaBadgeStyles,
792
- children: [migrationVersions.length, " total"]
793
- }), /* @__PURE__ */ jsxs("span", {
794
- style: schemaVersionChipStyles,
795
- children: [
796
- "current: v",
797
- schemaVersion,
798
- " / latest: v",
799
- latestVersion
800
- ]
801
- })]
802
- })]
803
- }), migrationVersions.length === 0 ? /* @__PURE__ */ jsx("div", {
804
- style: schemaEmptyStyles,
805
- children: "No migrations defined."
1015
+ className: "schemaSectionHeader",
1016
+ children: [
1017
+ /* @__PURE__ */ jsx("div", {
1018
+ className: "schemaSectionTitle",
1019
+ children: "Tables"
1020
+ }),
1021
+ /* @__PURE__ */ jsx("span", {
1022
+ className: "schemaBadge",
1023
+ children: tableEntries.length
1024
+ }),
1025
+ /* @__PURE__ */ jsxs("span", {
1026
+ className: "schemaVersionChip mlAuto",
1027
+ title: "Latest applied migration version",
1028
+ children: ["applied v", schemaVersion]
1029
+ })
1030
+ ]
1031
+ }), tableEntries.length === 0 ? /* @__PURE__ */ jsx("div", {
1032
+ className: "schemaEmpty",
1033
+ children: "No tables declared on this schema."
806
1034
  }) : /* @__PURE__ */ jsx("div", {
807
- style: migrationListStyles,
808
- children: migrationVersions.map((version) => {
809
- const applied = version <= schemaVersion;
810
- const isCurrent = version === schemaVersion;
1035
+ className: "schemaTableList",
1036
+ children: tableEntries.map(([crdtTableName, table]) => {
1037
+ const baseTableName = table.baseName ?? `_${crdtTableName}`;
1038
+ const columns = Object.entries(table.columns);
811
1039
  return /* @__PURE__ */ jsxs("div", {
812
- style: getMigrationRowStyles(applied),
813
- children: [
814
- /* @__PURE__ */ jsxs("div", {
815
- style: migrationVersionStyles,
816
- children: ["v", version]
817
- }),
818
- /* @__PURE__ */ jsx("div", {
819
- style: migrationBarTrackStyles,
820
- children: /* @__PURE__ */ jsx("div", { style: getMigrationBarFillStyles(applied) })
821
- }),
822
- /* @__PURE__ */ jsx("div", {
823
- style: getMigrationTagStyles(applied, isCurrent),
824
- children: isCurrent ? "current" : applied ? "applied" : "pending"
825
- })
826
- ]
827
- }, version);
1040
+ className: "schemaTableCard",
1041
+ children: [/* @__PURE__ */ jsxs("div", {
1042
+ className: "schemaTableCardHeader",
1043
+ children: [
1044
+ /* @__PURE__ */ jsxs("div", {
1045
+ className: "schemaTableCardTitleRow",
1046
+ children: [/* @__PURE__ */ jsx("div", {
1047
+ className: "schemaTableCardName",
1048
+ children: crdtTableName
1049
+ }), /* @__PURE__ */ jsx("span", {
1050
+ className: "schemaBadge",
1051
+ children: columns.length
1052
+ })]
1053
+ }),
1054
+ table.description ? /* @__PURE__ */ jsx("p", {
1055
+ className: "schemaTableCardDescription",
1056
+ children: table.description
1057
+ }) : null,
1058
+ /* @__PURE__ */ jsxs("div", {
1059
+ className: "schemaTableCardMeta",
1060
+ children: [
1061
+ /* @__PURE__ */ jsx("span", {
1062
+ className: "schemaPolicyChip",
1063
+ title: "Base table",
1064
+ children: baseTableName
1065
+ }),
1066
+ /* @__PURE__ */ jsxs("span", {
1067
+ className: "schemaPolicyChip",
1068
+ "data-tone": table.writeOrigin === "server" ? "warn" : void 0,
1069
+ title: "Who may write CRDT events",
1070
+ children: ["writes ", table.writeOrigin]
1071
+ }),
1072
+ /* @__PURE__ */ jsxs("span", {
1073
+ className: "schemaPolicyChip",
1074
+ "data-tone": table.aiAccess === "read-write" ? void 0 : "warn",
1075
+ title: "AI agent access",
1076
+ children: ["ai ", table.aiAccess]
1077
+ }),
1078
+ /* @__PURE__ */ jsxs("span", {
1079
+ className: "schemaPolicyChip",
1080
+ "data-tone": table.exportImport === "ignore" ? "warn" : void 0,
1081
+ title: "Export / import",
1082
+ children: ["export ", table.exportImport]
1083
+ })
1084
+ ]
1085
+ })
1086
+ ]
1087
+ }), /* @__PURE__ */ jsxs("div", {
1088
+ className: "schemaColumnGrid",
1089
+ children: [/* @__PURE__ */ jsxs("div", {
1090
+ className: "schemaColumnRow schemaColumnHeaderRow",
1091
+ children: [
1092
+ /* @__PURE__ */ jsx("div", {
1093
+ className: "schemaColHeader",
1094
+ children: "Column"
1095
+ }),
1096
+ /* @__PURE__ */ jsx("div", {
1097
+ className: "schemaColHeader",
1098
+ children: "Type"
1099
+ }),
1100
+ /* @__PURE__ */ jsx("div", {
1101
+ className: "schemaColHeader",
1102
+ children: "Flags"
1103
+ }),
1104
+ /* @__PURE__ */ jsx("div", {
1105
+ className: "schemaColHeader",
1106
+ children: "Description"
1107
+ })
1108
+ ]
1109
+ }), columns.map(([name, meta]) => /* @__PURE__ */ jsx(SchemaColumnRow, {
1110
+ name,
1111
+ meta
1112
+ }, name))]
1113
+ })]
1114
+ }, crdtTableName);
828
1115
  })
829
1116
  })]
830
- })]
1117
+ })
1118
+ });
1119
+ }
1120
+ function SchemaColumnRow({ name, meta }) {
1121
+ const flags = [];
1122
+ if (name === "id") flags.push("pk");
1123
+ if (meta.nullable) flags.push("nullable");
1124
+ if (meta.hasDefault) flags.push(`default ${formatDefaultValue(meta.defaultValue)}`);
1125
+ return /* @__PURE__ */ jsxs("div", {
1126
+ className: "schemaColumnRow",
1127
+ "data-system": SYSTEM_COLUMNS.has(name) ? "true" : void 0,
1128
+ children: [
1129
+ /* @__PURE__ */ jsx("div", {
1130
+ className: "schemaColumnName",
1131
+ children: name
1132
+ }),
1133
+ /* @__PURE__ */ jsxs("div", {
1134
+ className: "schemaColumnType",
1135
+ children: [
1136
+ /* @__PURE__ */ jsx("span", { children: meta.kind }),
1137
+ meta.kind === "boolean" ? /* @__PURE__ */ jsx("span", {
1138
+ className: "schemaColumnTypeDetail",
1139
+ children: "integer 0/1"
1140
+ }) : null,
1141
+ meta.kind === "enum" && meta.enumValues && meta.enumValues.length > 0 ? /* @__PURE__ */ jsx("span", {
1142
+ className: "schemaColumnTypeDetail",
1143
+ children: meta.enumValues.map((value) => `"${value}"`).join(" | ")
1144
+ }) : null
1145
+ ]
1146
+ }),
1147
+ /* @__PURE__ */ jsx("div", {
1148
+ className: "schemaColumnFlags",
1149
+ children: flags.length > 0 ? flags.join(" · ") : "—"
1150
+ }),
1151
+ /* @__PURE__ */ jsx("div", {
1152
+ className: "schemaColumnDescription",
1153
+ children: meta.description ?? ""
1154
+ })
1155
+ ]
831
1156
  });
832
1157
  }
833
1158
  function LiveQueriesTab({ selectedInstance }) {
@@ -844,22 +1169,22 @@ function LiveQueriesTab({ selectedInstance }) {
844
1169
  }, [refresh]);
845
1170
  const totalSubscribers = queries.reduce((total, query) => total + query.subscriberCount, 0);
846
1171
  return /* @__PURE__ */ jsx("div", {
847
- style: schemaLayoutStyles,
1172
+ className: "schemaLayout",
848
1173
  children: /* @__PURE__ */ jsxs("div", {
849
- style: schemaSectionStyles,
1174
+ className: "schemaSection",
850
1175
  children: [/* @__PURE__ */ jsxs("div", {
851
- style: schemaSectionHeaderStyles,
1176
+ className: "schemaSectionHeader",
852
1177
  children: [
853
1178
  /* @__PURE__ */ jsx("div", {
854
- style: schemaSectionTitleStyles,
1179
+ className: "schemaSectionTitle",
855
1180
  children: "Shared Live Queries"
856
1181
  }),
857
1182
  /* @__PURE__ */ jsx("span", {
858
- style: schemaBadgeStyles,
1183
+ className: "schemaBadge",
859
1184
  children: queries.length
860
1185
  }),
861
1186
  /* @__PURE__ */ jsxs("span", {
862
- style: schemaVersionChipStyles,
1187
+ className: "schemaVersionChip",
863
1188
  children: [
864
1189
  totalSubscribers,
865
1190
  " subscriber",
@@ -868,63 +1193,57 @@ function LiveQueriesTab({ selectedInstance }) {
868
1193
  }),
869
1194
  /* @__PURE__ */ jsx("button", {
870
1195
  type: "button",
871
- style: {
872
- ...eventLogRefreshButtonStyles,
873
- marginLeft: "auto"
874
- },
1196
+ className: "refreshButton mlAuto",
875
1197
  onClick: refresh,
876
1198
  disabled: !selectedInstance,
877
1199
  children: "↻ Refresh"
878
1200
  })
879
1201
  ]
880
1202
  }), queries.length === 0 ? /* @__PURE__ */ jsxs("div", {
881
- style: schemaEmptyStyles,
1203
+ className: "schemaEmpty",
882
1204
  children: [
883
1205
  "No active live queries. Queries created via ",
884
1206
  /* @__PURE__ */ jsx("code", {
885
- style: inlineCodeStyles,
1207
+ className: "inlineCode",
886
1208
  children: "useDbQuery"
887
1209
  }),
888
1210
  " appear here while components are subscribed."
889
1211
  ]
890
1212
  }) : /* @__PURE__ */ jsxs("div", {
891
- style: schemaTableGridStyles,
1213
+ className: "schemaTableGrid",
892
1214
  children: [/* @__PURE__ */ jsxs("div", {
893
- style: liveQueryHeaderRowStyles,
1215
+ className: "liveQueryHeaderRow",
894
1216
  children: [
895
1217
  /* @__PURE__ */ jsx("div", {
896
- style: schemaColHeaderStyles,
1218
+ className: "schemaColHeader",
897
1219
  children: "SQL"
898
1220
  }),
899
1221
  /* @__PURE__ */ jsx("div", {
900
- style: schemaColHeaderStyles,
1222
+ className: "schemaColHeader",
901
1223
  children: "Parameters"
902
1224
  }),
903
1225
  /* @__PURE__ */ jsx("div", {
904
- style: {
905
- ...schemaColHeaderStyles,
906
- textAlign: "center"
907
- },
1226
+ className: "schemaColHeader alignCenter",
908
1227
  children: "Subscribers"
909
1228
  })
910
1229
  ]
911
1230
  }), queries.map((query) => {
912
1231
  const parameters = formatLiveQueryParameters(query.parameters);
913
1232
  return /* @__PURE__ */ jsxs("div", {
914
- style: liveQueryRowStyles,
1233
+ className: "liveQueryRow",
915
1234
  children: [
916
1235
  /* @__PURE__ */ jsx("div", {
917
- style: liveQuerySqlStyles,
1236
+ className: "liveQuerySql",
918
1237
  children: query.sql
919
1238
  }),
920
1239
  /* @__PURE__ */ jsx("div", {
921
- style: liveQueryParametersStyles,
1240
+ className: "liveQueryParameters",
922
1241
  children: parameters
923
1242
  }),
924
1243
  /* @__PURE__ */ jsx("div", {
925
- style: schemaStatusCellStyles,
1244
+ className: "schemaStatusCell",
926
1245
  children: /* @__PURE__ */ jsx("span", {
927
- style: query.subscriberCount > 0 ? schemaActiveTagStyles : liveQueryIdleTagStyles,
1246
+ className: query.subscriberCount > 0 ? "schemaActiveTag" : "liveQueryIdleTag",
928
1247
  children: query.subscriberCount
929
1248
  })
930
1249
  })
@@ -942,14 +1261,27 @@ function formatLiveQueryParameters(parameters) {
942
1261
  return String(parameters);
943
1262
  }
944
1263
  }
1264
+ function isPersistedCrdtEvent(value) {
1265
+ if (typeof value !== "object" || value === null) return false;
1266
+ return "sync_id" in value && typeof value.sync_id === "number" && "status" in value && typeof value.status === "string" && "type" in value && typeof value.type === "string" && "origin" in value && typeof value.origin === "string" && "dataset" in value && typeof value.dataset === "string" && "item_id" in value && typeof value.item_id === "string" && "payload" in value && typeof value.payload === "string" && "timestamp" in value && typeof value.timestamp === "string";
1267
+ }
945
1268
  const PAGE_SIZE = 50;
946
- function buildEventLogQuery(filters, afterSyncId) {
1269
+ function sqlStringLiteral(value) {
1270
+ return `'${value.replaceAll("'", "''")}'`;
1271
+ }
1272
+ function eventLogWhereClause(filters, afterSyncId) {
947
1273
  const conditions = [];
948
- if (filters.dataset) conditions.push(`dataset = '${filters.dataset}'`);
949
- if (filters.origin) conditions.push(`origin = '${filters.origin}'`);
950
- if (filters.status) conditions.push(`status = '${filters.status}'`);
1274
+ if (filters.dataset) conditions.push(`dataset = ${sqlStringLiteral(filters.dataset)}`);
1275
+ if (filters.origin) conditions.push(`origin = ${sqlStringLiteral(filters.origin)}`);
1276
+ if (filters.status) conditions.push(`status = ${sqlStringLiteral(filters.status)}`);
951
1277
  if (afterSyncId !== null) conditions.push(`sync_id < ${afterSyncId}`);
952
- return `SELECT * FROM "worker"."crdt_events" ${conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : ""} ORDER BY sync_id DESC LIMIT 51`;
1278
+ return conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
1279
+ }
1280
+ function buildEventLogQuery(filters, afterSyncId) {
1281
+ return `SELECT * FROM "worker"."crdt_events" ${eventLogWhereClause(filters, afterSyncId)} ORDER BY sync_id DESC LIMIT 51`;
1282
+ }
1283
+ function buildEventLogCountQuery(filters) {
1284
+ return `SELECT COUNT(*) AS count FROM "worker"."crdt_events" ${eventLogWhereClause(filters, null)}`;
953
1285
  }
954
1286
  function EventLogTab({ selectedInstance }) {
955
1287
  const baseTableNames = useMemo(() => selectedInstance?.instance._internal.crdtTablesConfig.map((table) => table.baseTableName) ?? [], [selectedInstance]);
@@ -959,12 +1291,13 @@ function EventLogTab({ selectedInstance }) {
959
1291
  status: ""
960
1292
  });
961
1293
  const [events, setEvents] = useState([]);
1294
+ const [totalCount, setTotalCount] = useState(null);
962
1295
  const [hasMore, setHasMore] = useState(false);
963
1296
  const [isLoading, setIsLoading] = useState(false);
964
1297
  const [expandedId, setExpandedId] = useState(null);
965
1298
  const afterSyncIdRef = useRef(null);
966
1299
  const isLoadingRef = useRef(false);
967
- const executeQuery = useCallback(async (sql) => {
1300
+ const executeRows = useCallback(async (sql) => {
968
1301
  if (!selectedInstance) return [];
969
1302
  return (await selectedInstance.instance._internal.executeAsync({
970
1303
  sql,
@@ -977,20 +1310,25 @@ function EventLogTab({ selectedInstance }) {
977
1310
  setIsLoading(true);
978
1311
  try {
979
1312
  const sql = buildEventLogQuery(currentFilters, reset ? null : afterSyncIdRef.current);
980
- const rows = await executeQuery(sql);
981
- const page = rows.slice(0, PAGE_SIZE);
1313
+ const rowsPromise = executeRows(sql);
1314
+ const countPromise = reset ? executeRows(buildEventLogCountQuery(currentFilters)) : Promise.resolve(null);
1315
+ const [rows, countRows] = await Promise.all([rowsPromise, countPromise]);
1316
+ const page = rows.filter(isPersistedCrdtEvent).slice(0, PAGE_SIZE);
982
1317
  setHasMore(rows.length > PAGE_SIZE);
983
- if (reset) setEvents(page);
984
- else setEvents((prev) => [...prev, ...page]);
1318
+ if (reset) {
1319
+ setEvents(page);
1320
+ setTotalCount(countRows ? readCount(countRows) : null);
1321
+ } else setEvents((prev) => [...prev, ...page]);
985
1322
  afterSyncIdRef.current = page.at(-1)?.sync_id ?? null;
986
1323
  } finally {
987
1324
  isLoadingRef.current = false;
988
1325
  setIsLoading(false);
989
1326
  }
990
- }, [selectedInstance, executeQuery]);
1327
+ }, [selectedInstance, executeRows]);
991
1328
  useEffect(() => {
992
1329
  afterSyncIdRef.current = null;
993
1330
  setEvents([]);
1331
+ setTotalCount(null);
994
1332
  load(true, filters);
995
1333
  }, [filters, load]);
996
1334
  const applyFilters = (next) => {
@@ -998,9 +1336,9 @@ function EventLogTab({ selectedInstance }) {
998
1336
  };
999
1337
  if (!selectedInstance) return null;
1000
1338
  return /* @__PURE__ */ jsxs("div", {
1001
- style: eventLogLayoutStyles,
1339
+ className: "eventLogLayout",
1002
1340
  children: [/* @__PURE__ */ jsxs("div", {
1003
- style: eventLogToolbarStyles,
1341
+ className: "eventLogToolbar",
1004
1342
  children: [
1005
1343
  /* @__PURE__ */ jsxs("select", {
1006
1344
  value: filters.dataset,
@@ -1008,7 +1346,7 @@ function EventLogTab({ selectedInstance }) {
1008
1346
  ...filters,
1009
1347
  dataset: e.target.value
1010
1348
  }),
1011
- style: eventLogFilterSelectStyles,
1349
+ className: "eventLogFilterSelect",
1012
1350
  children: [/* @__PURE__ */ jsx("option", {
1013
1351
  value: "",
1014
1352
  children: "All datasets"
@@ -1023,7 +1361,7 @@ function EventLogTab({ selectedInstance }) {
1023
1361
  ...filters,
1024
1362
  origin: e.target.value
1025
1363
  }),
1026
- style: eventLogFilterSelectStyles,
1364
+ className: "eventLogFilterSelect",
1027
1365
  children: [
1028
1366
  /* @__PURE__ */ jsx("option", {
1029
1367
  value: "",
@@ -1049,7 +1387,7 @@ function EventLogTab({ selectedInstance }) {
1049
1387
  ...filters,
1050
1388
  status: e.target.value
1051
1389
  }),
1052
- style: eventLogFilterSelectStyles,
1390
+ className: "eventLogFilterSelect",
1053
1391
  children: [
1054
1392
  /* @__PURE__ */ jsx("option", {
1055
1393
  value: "",
@@ -1075,85 +1413,82 @@ function EventLogTab({ selectedInstance }) {
1075
1413
  }),
1076
1414
  /* @__PURE__ */ jsxs("button", {
1077
1415
  type: "button",
1078
- style: eventLogRefreshButtonStyles,
1416
+ className: "refreshButton",
1079
1417
  disabled: isLoading,
1080
1418
  onClick: () => void load(true, filters),
1081
1419
  children: [isLoading ? "…" : "↻", " Refresh"]
1082
1420
  }),
1083
- events.length > 0 && /* @__PURE__ */ jsxs("span", {
1084
- style: eventLogCountStyles,
1085
- children: [
1086
- events.length,
1087
- hasMore ? "+" : "",
1088
- " events"
1089
- ]
1421
+ totalCount !== null && /* @__PURE__ */ jsx("span", {
1422
+ className: "eventLogCount",
1423
+ children: events.length === totalCount ? `${totalCount} event${totalCount === 1 ? "" : "s"}` : `${events.length} of ${totalCount}`
1090
1424
  })
1091
1425
  ]
1092
1426
  }), events.length === 0 && !isLoading ? /* @__PURE__ */ jsx("div", {
1093
- style: eventLogEmptyStyles,
1427
+ className: "eventLogEmpty",
1094
1428
  children: "No events match the current filters."
1095
1429
  }) : /* @__PURE__ */ jsxs("div", {
1096
- style: eventLogListStyles,
1430
+ className: "eventLogList",
1097
1431
  children: [events.map((event) => {
1098
1432
  const isExpanded = expandedId === event.sync_id;
1099
1433
  return /* @__PURE__ */ jsxs("div", {
1100
- style: getEventRowStyles(isExpanded),
1434
+ className: "eventRow",
1435
+ "data-expanded": isExpanded ? "true" : void 0,
1101
1436
  children: [/* @__PURE__ */ jsxs("button", {
1102
1437
  type: "button",
1103
- style: eventRowHeaderStyles,
1438
+ className: "eventRowHeader",
1104
1439
  onClick: () => setExpandedId(isExpanded ? null : event.sync_id),
1105
1440
  children: [
1106
1441
  /* @__PURE__ */ jsxs("span", {
1107
- style: eventSyncIdStyles,
1442
+ className: "eventSyncId",
1108
1443
  children: ["#", event.sync_id]
1109
1444
  }),
1110
1445
  /* @__PURE__ */ jsx(EventTypeBadge, { type: event.type }),
1111
1446
  /* @__PURE__ */ jsx(EventOriginBadge, { origin: event.origin }),
1112
1447
  /* @__PURE__ */ jsx(EventStatusBadge, { status: event.status }),
1113
1448
  /* @__PURE__ */ jsx("span", {
1114
- style: eventDatasetStyles,
1449
+ className: "eventDataset",
1115
1450
  children: event.dataset
1116
1451
  }),
1117
1452
  /* @__PURE__ */ jsx("span", {
1118
- style: eventItemIdStyles,
1453
+ className: "eventItemId",
1119
1454
  children: event.item_id
1120
1455
  }),
1121
1456
  /* @__PURE__ */ jsx("span", {
1122
- style: eventTimestampStyles,
1457
+ className: "eventTimestamp",
1123
1458
  children: formatHlcTimestamp(event.timestamp)
1124
1459
  }),
1125
1460
  /* @__PURE__ */ jsx("span", {
1126
- style: eventChevronStyles,
1461
+ className: "eventChevron",
1127
1462
  children: isExpanded ? "▲" : "▼"
1128
1463
  })
1129
1464
  ]
1130
1465
  }), isExpanded && /* @__PURE__ */ jsxs("div", {
1131
- style: eventPayloadStyles,
1466
+ className: "eventPayload",
1132
1467
  children: [/* @__PURE__ */ jsxs("div", {
1133
- style: eventPayloadMetaStyles,
1468
+ className: "eventPayloadMeta",
1134
1469
  children: [
1135
1470
  /* @__PURE__ */ jsxs("span", {
1136
- style: eventMetaItemStyles,
1471
+ className: "eventMetaItem",
1137
1472
  children: ["schema v", event.schema_version]
1138
1473
  }),
1139
1474
  /* @__PURE__ */ jsxs("span", {
1140
- style: eventMetaItemStyles,
1475
+ className: "eventMetaItem",
1141
1476
  children: ["node: ", event.source_node_id || "—"]
1142
1477
  }),
1143
1478
  /* @__PURE__ */ jsxs("span", {
1144
- style: eventMetaItemStyles,
1479
+ className: "eventMetaItem",
1145
1480
  children: ["ts: ", event.timestamp]
1146
1481
  })
1147
1482
  ]
1148
1483
  }), /* @__PURE__ */ jsx("pre", {
1149
- style: eventPayloadPreStyles,
1484
+ className: "eventPayloadPre",
1150
1485
  children: formatPayload(event.payload)
1151
1486
  })]
1152
1487
  })]
1153
1488
  }, event.sync_id);
1154
1489
  }), hasMore && /* @__PURE__ */ jsx("button", {
1155
1490
  type: "button",
1156
- style: loadMoreButtonStyles,
1491
+ className: "loadMoreButton",
1157
1492
  disabled: isLoading,
1158
1493
  onClick: () => void load(false, filters),
1159
1494
  children: isLoading ? "Loading…" : `Load ${PAGE_SIZE} more`
@@ -1162,30 +1497,33 @@ function EventLogTab({ selectedInstance }) {
1162
1497
  });
1163
1498
  }
1164
1499
  function EventTypeBadge({ type }) {
1165
- const { style, label } = type === "item-created" ? {
1166
- style: eventTypeCreateStyles,
1500
+ const { variant, label } = type === "item-created" ? {
1501
+ variant: "create",
1167
1502
  label: "create"
1168
1503
  } : type === "item-deleted" ? {
1169
- style: eventTypeDeleteStyles,
1504
+ variant: "delete",
1170
1505
  label: "delete"
1171
1506
  } : {
1172
- style: eventTypeUpdateStyles,
1507
+ variant: "update",
1173
1508
  label: "update"
1174
1509
  };
1175
1510
  return /* @__PURE__ */ jsx("span", {
1176
- style,
1511
+ className: "eventBadge",
1512
+ "data-variant": variant,
1177
1513
  children: label
1178
1514
  });
1179
1515
  }
1180
1516
  function EventOriginBadge({ origin }) {
1181
1517
  return /* @__PURE__ */ jsx("span", {
1182
- style: origin === "own" ? eventOriginOwnStyles : origin === "remote" ? eventOriginRemoteStyles : eventOriginLocalStyles,
1518
+ className: "eventBadge",
1519
+ "data-variant": origin === "own" ? "own" : origin === "remote" ? "remote" : "local",
1183
1520
  children: origin
1184
1521
  });
1185
1522
  }
1186
1523
  function EventStatusBadge({ status }) {
1187
1524
  return /* @__PURE__ */ jsx("span", {
1188
- style: status === "applied" ? eventStatusAppliedStyles : status === "pending" ? eventStatusPendingStyles : status === "failed" ? eventStatusFailedStyles : eventStatusSkippedStyles,
1525
+ className: "eventBadge",
1526
+ "data-variant": status === "applied" ? "applied" : status === "pending" ? "pending" : status === "failed" ? "failed" : "skipped",
1189
1527
  children: status
1190
1528
  });
1191
1529
  }
@@ -1203,46 +1541,50 @@ function formatPayload(raw) {
1203
1541
  }
1204
1542
  function QueryRunnerTab({ selectedInstance, queryTarget, setQueryTarget, query, setQuery, queryState, canRunQuery, runQuery }) {
1205
1543
  return /* @__PURE__ */ jsxs("div", {
1206
- style: queryRunnerLayoutStyles,
1544
+ className: "queryRunnerLayout",
1207
1545
  children: [
1208
1546
  /* @__PURE__ */ jsxs("div", {
1209
- style: queryToolbarStyles,
1547
+ className: "queryToolbar",
1210
1548
  children: [/* @__PURE__ */ jsxs("div", {
1211
- style: targetToggleStyles,
1549
+ className: "targetToggle",
1212
1550
  children: [/* @__PURE__ */ jsx("button", {
1213
1551
  type: "button",
1214
- style: getTargetButtonStyles(queryTarget === "memory"),
1552
+ className: "targetButton",
1553
+ "data-active": queryTarget === "memory" ? "true" : void 0,
1215
1554
  onClick: () => setQueryTarget("memory"),
1216
1555
  children: "Memory DB"
1217
1556
  }), /* @__PURE__ */ jsx("button", {
1218
1557
  type: "button",
1219
- style: getTargetButtonStyles(queryTarget === "worker"),
1558
+ className: "targetButton",
1559
+ "data-active": queryTarget === "worker" ? "true" : void 0,
1220
1560
  onClick: () => setQueryTarget("worker"),
1221
1561
  children: "Worker DB"
1222
1562
  })]
1223
1563
  }), /* @__PURE__ */ jsxs("div", {
1224
- style: queryToolbarRightStyles,
1564
+ className: "queryToolbarRight",
1225
1565
  children: [
1226
1566
  !selectedInstance && /* @__PURE__ */ jsx("span", {
1227
- style: noInstanceWarningStyles,
1567
+ className: "noInstanceWarning",
1228
1568
  children: "No instance selected"
1229
1569
  }),
1230
1570
  /* @__PURE__ */ jsx("span", {
1231
- style: shortcutHintStyles,
1571
+ className: "shortcutHint",
1232
1572
  children: "⌘↵ to run"
1233
1573
  }),
1234
1574
  /* @__PURE__ */ jsx("button", {
1235
1575
  type: "button",
1236
- style: runButtonStyles(canRunQuery, queryState.status === "running"),
1576
+ className: "runButton",
1577
+ "data-enabled": canRunQuery ? "true" : void 0,
1578
+ "data-running": queryState.status === "running" ? "true" : void 0,
1237
1579
  disabled: !canRunQuery,
1238
1580
  onClick: () => void runQuery(),
1239
- children: queryState.status === "running" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", { style: runningDotStyles }), "Running…"] }) : /* @__PURE__ */ jsx(Fragment, { children: "▶ Run" })
1581
+ children: queryState.status === "running" ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("span", { className: "runningDot" }), "Running…"] }) : /* @__PURE__ */ jsx(Fragment, { children: "▶ Run" })
1240
1582
  })
1241
1583
  ]
1242
1584
  })]
1243
1585
  }),
1244
1586
  /* @__PURE__ */ jsx("div", {
1245
- style: editorWrapperStyles,
1587
+ className: "editorWrapper",
1246
1588
  children: /* @__PURE__ */ jsx("textarea", {
1247
1589
  value: query,
1248
1590
  onChange: (event) => setQuery(event.target.value),
@@ -1252,24 +1594,24 @@ function QueryRunnerTab({ selectedInstance, queryTarget, setQueryTarget, query,
1252
1594
  runQuery();
1253
1595
  }
1254
1596
  },
1255
- style: textareaStyles,
1597
+ className: "textarea",
1256
1598
  placeholder: "SELECT * FROM your_table LIMIT 100",
1257
1599
  spellCheck: false
1258
1600
  })
1259
1601
  }),
1260
1602
  /* @__PURE__ */ jsx("div", {
1261
- style: helperTextStyles,
1603
+ className: "helperText",
1262
1604
  children: queryTarget === "worker" ? "Worker DB is read-only — only SELECT, PRAGMA, and EXPLAIN are allowed." : "Memory DB allows writes only to CRDT tables."
1263
1605
  }),
1264
1606
  queryState.status === "error" && /* @__PURE__ */ jsxs("div", {
1265
- style: errorPanelStyles,
1607
+ className: "errorPanel",
1266
1608
  children: [/* @__PURE__ */ jsxs("div", {
1267
- style: resultHeaderStyles,
1609
+ className: "resultHeader",
1268
1610
  children: [/* @__PURE__ */ jsx("span", {
1269
- style: errorBadgeStyles,
1611
+ className: "errorBadge",
1270
1612
  children: "Error"
1271
1613
  }), /* @__PURE__ */ jsxs("span", {
1272
- style: resultMetaStyles,
1614
+ className: "resultMeta",
1273
1615
  children: [
1274
1616
  queryState.error.target,
1275
1617
  " · ",
@@ -1277,23 +1619,23 @@ function QueryRunnerTab({ selectedInstance, queryTarget, setQueryTarget, query,
1277
1619
  ]
1278
1620
  })]
1279
1621
  }), /* @__PURE__ */ jsx("pre", {
1280
- style: errorMessageStyles,
1622
+ className: "errorMessage",
1281
1623
  children: queryState.error.message
1282
1624
  })]
1283
1625
  }),
1284
1626
  queryState.status === "success" && /* @__PURE__ */ jsxs("div", {
1285
- style: resultPanelStyles,
1627
+ className: "resultPanel",
1286
1628
  children: [/* @__PURE__ */ jsxs("div", {
1287
- style: resultHeaderStyles,
1629
+ className: "resultHeader",
1288
1630
  children: [/* @__PURE__ */ jsxs("span", {
1289
- style: successBadgeStyles,
1631
+ className: "successBadge",
1290
1632
  children: [
1291
1633
  queryState.output.rowCount,
1292
1634
  " row",
1293
1635
  queryState.output.rowCount !== 1 ? "s" : ""
1294
1636
  ]
1295
1637
  }), /* @__PURE__ */ jsxs("span", {
1296
- style: resultMetaStyles,
1638
+ className: "resultMeta",
1297
1639
  children: [
1298
1640
  queryState.output.target,
1299
1641
  " · ",
@@ -1308,44 +1650,41 @@ function QueryRunnerTab({ selectedInstance, queryTarget, setQueryTarget, query,
1308
1650
  }
1309
1651
  function ResultTable({ rows }) {
1310
1652
  if (rows.length === 0) return /* @__PURE__ */ jsx("div", {
1311
- style: resultEmptyStyles,
1653
+ className: "resultEmpty",
1312
1654
  children: "Query returned 0 rows."
1313
1655
  });
1314
1656
  const firstRow = rows[0];
1315
1657
  if (typeof firstRow !== "object" || firstRow === null) return /* @__PURE__ */ jsx("pre", {
1316
- style: resultRawStyles,
1658
+ className: "resultRaw",
1317
1659
  children: JSON.stringify(rows, null, 2)
1318
1660
  });
1319
1661
  const columns = Object.keys(firstRow);
1320
1662
  return /* @__PURE__ */ jsx("div", {
1321
- style: tableWrapperStyles,
1663
+ className: "tableWrapper",
1322
1664
  children: /* @__PURE__ */ jsxs("table", {
1323
- style: tableStyles,
1665
+ className: "table",
1324
1666
  children: [/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("th", {
1325
- style: thRowNumStyles,
1667
+ className: "thRowNum",
1326
1668
  children: "#"
1327
1669
  }), columns.map((col) => /* @__PURE__ */ jsx("th", {
1328
- style: thStyles,
1670
+ className: "th",
1329
1671
  children: col
1330
- }, col))] }) }), /* @__PURE__ */ jsx("tbody", { children: rows.map((row, rowIdx) => /* @__PURE__ */ jsxs("tr", {
1331
- style: rowIdx % 2 === 0 ? trStyles : trAltStyles,
1332
- children: [/* @__PURE__ */ jsx("td", {
1333
- style: tdRowNumStyles,
1334
- children: rowIdx + 1
1335
- }), columns.map((col) => {
1336
- const val = row[col];
1337
- return /* @__PURE__ */ jsx("td", {
1338
- style: tdStyles,
1339
- children: val === null ? /* @__PURE__ */ jsx("span", {
1340
- style: nullValueStyles,
1341
- children: "NULL"
1342
- }) : typeof val === "object" ? /* @__PURE__ */ jsx("span", {
1343
- style: jsonValueStyles,
1344
- children: JSON.stringify(val)
1345
- }) : String(val)
1346
- }, col);
1347
- })]
1348
- }, rowIdx)) })]
1672
+ }, col))] }) }), /* @__PURE__ */ jsx("tbody", { children: rows.map((row, rowIdx) => /* @__PURE__ */ jsxs("tr", { children: [/* @__PURE__ */ jsx("td", {
1673
+ className: "tdRowNum",
1674
+ children: rowIdx + 1
1675
+ }), columns.map((col) => {
1676
+ const val = row[col];
1677
+ return /* @__PURE__ */ jsx("td", {
1678
+ className: "td",
1679
+ children: val === null ? /* @__PURE__ */ jsx("span", {
1680
+ className: "nullValue",
1681
+ children: "NULL"
1682
+ }) : typeof val === "object" ? /* @__PURE__ */ jsx("span", {
1683
+ className: "jsonValue",
1684
+ children: JSON.stringify(val)
1685
+ }) : String(val)
1686
+ }, col);
1687
+ })] }, rowIdx)) })]
1349
1688
  })
1350
1689
  });
1351
1690
  }
@@ -1370,1164 +1709,6 @@ function getEmptySnapshot() {
1370
1709
  return emptySnapshot;
1371
1710
  }
1372
1711
  const emptySnapshot = { instances: [] };
1373
- const C = {
1374
- bg: "#0e1015",
1375
- bgPanel: "#161921",
1376
- bgCard: "#1c2030",
1377
- bgInput: "#111318",
1378
- border: "#2a2f3e",
1379
- borderLight: "#343a4f",
1380
- text: "#e2e8f0",
1381
- textMuted: "#6b7280",
1382
- textDim: "#9ca3af",
1383
- teal: "#2dd4bf",
1384
- tealDim: "rgba(45,212,191,0.12)",
1385
- tealGlow: "rgba(45,212,191,0.06)",
1386
- error: "#f87171",
1387
- errorBg: "rgba(248,113,113,0.08)",
1388
- errorBorder: "rgba(248,113,113,0.25)",
1389
- success: "#34d399",
1390
- successBg: "rgba(52,211,153,0.08)",
1391
- amber: "#fbbf24"
1392
- };
1393
- const floatingRootStyles = {
1394
- position: "fixed",
1395
- inset: 0,
1396
- pointerEvents: "none",
1397
- zIndex: 9998
1398
- };
1399
- const triggerButtonStyles = {
1400
- position: "absolute",
1401
- right: "16px",
1402
- bottom: "16px",
1403
- display: "inline-flex",
1404
- alignItems: "center",
1405
- gap: "0.3rem",
1406
- border: `1px solid ${C.border}`,
1407
- borderRadius: "8px",
1408
- padding: "0.3rem 0.45rem",
1409
- background: C.bgPanel,
1410
- color: C.text,
1411
- boxShadow: `0 0 0 1px ${C.border}, 0 4px 16px rgba(0,0,0,0.5)`,
1412
- cursor: "grab",
1413
- touchAction: "none",
1414
- userSelect: "none",
1415
- pointerEvents: "auto"
1416
- };
1417
- const triggerIconStyles = {
1418
- color: C.teal,
1419
- fontSize: "0.9rem",
1420
- lineHeight: 1
1421
- };
1422
- const triggerCountStyles = {
1423
- minWidth: "1.1rem",
1424
- borderRadius: "4px",
1425
- padding: "0.05rem 0.25rem",
1426
- backgroundColor: C.tealDim,
1427
- color: C.teal,
1428
- fontSize: "0.65rem",
1429
- fontWeight: 700,
1430
- textAlign: "center",
1431
- fontFamily: "ui-monospace, monospace"
1432
- };
1433
- const overlayStyles = {
1434
- position: "fixed",
1435
- inset: 0,
1436
- zIndex: 9999,
1437
- display: "flex",
1438
- alignItems: "center",
1439
- justifyContent: "center",
1440
- padding: "1.5rem",
1441
- backgroundColor: "rgba(0,0,0,0.72)",
1442
- backdropFilter: "blur(8px)"
1443
- };
1444
- const dialogStyles = {
1445
- width: "min(72rem, 100%)",
1446
- height: "min(90vh, 860px)",
1447
- display: "flex",
1448
- flexDirection: "column",
1449
- border: `1px solid ${C.border}`,
1450
- borderRadius: "16px",
1451
- background: C.bg,
1452
- boxShadow: `0 0 0 1px ${C.border}, 0 40px 80px rgba(0,0,0,0.8), 0 0 60px rgba(45,212,191,0.04)`,
1453
- overflow: "hidden",
1454
- fontFamily: "ui-sans-serif, system-ui, sans-serif",
1455
- color: C.text,
1456
- fontSize: "0.88rem"
1457
- };
1458
- const headerStyles = {
1459
- display: "flex",
1460
- justifyContent: "space-between",
1461
- alignItems: "center",
1462
- gap: "1rem",
1463
- padding: "0.85rem 1.1rem",
1464
- borderBottom: `1px solid ${C.border}`,
1465
- background: C.bgPanel,
1466
- flexShrink: 0
1467
- };
1468
- const headerLeftStyles = {
1469
- display: "flex",
1470
- alignItems: "center",
1471
- gap: "0.75rem"
1472
- };
1473
- const headerLogoStyles = {
1474
- fontSize: "1.3rem",
1475
- color: C.teal,
1476
- lineHeight: 1
1477
- };
1478
- const eyebrowStyles = {
1479
- fontSize: "0.65rem",
1480
- fontWeight: 600,
1481
- letterSpacing: "0.1em",
1482
- textTransform: "uppercase",
1483
- color: C.textMuted,
1484
- fontFamily: "ui-monospace, monospace"
1485
- };
1486
- const titleStyles = {
1487
- margin: "0.05rem 0 0",
1488
- fontSize: "0.95rem",
1489
- fontWeight: 700,
1490
- color: C.text,
1491
- letterSpacing: "-0.01em"
1492
- };
1493
- const headerRightStyles = {
1494
- display: "flex",
1495
- alignItems: "center",
1496
- gap: "0.6rem"
1497
- };
1498
- const instancePickerLabelStyles = {
1499
- display: "flex",
1500
- alignItems: "center",
1501
- gap: "0.5rem"
1502
- };
1503
- const instancePickerTextStyles = {
1504
- fontSize: "0.72rem",
1505
- fontWeight: 600,
1506
- color: C.textMuted,
1507
- textTransform: "uppercase",
1508
- letterSpacing: "0.06em"
1509
- };
1510
- const instancePickerSelectStyles = {
1511
- borderRadius: "7px",
1512
- border: `1px solid ${C.border}`,
1513
- padding: "0.35rem 0.6rem",
1514
- backgroundColor: C.bgCard,
1515
- color: C.text,
1516
- fontSize: "0.82rem",
1517
- fontFamily: "ui-monospace, monospace",
1518
- cursor: "pointer"
1519
- };
1520
- const instanceCountBadgeStyles = {
1521
- fontSize: "0.72rem",
1522
- fontWeight: 600,
1523
- color: C.teal,
1524
- backgroundColor: C.tealDim,
1525
- borderRadius: "5px",
1526
- padding: "0.2rem 0.5rem",
1527
- fontFamily: "ui-monospace, monospace"
1528
- };
1529
- const closeButtonStyles = {
1530
- border: `1px solid ${C.border}`,
1531
- borderRadius: "7px",
1532
- width: "28px",
1533
- height: "28px",
1534
- display: "flex",
1535
- alignItems: "center",
1536
- justifyContent: "center",
1537
- backgroundColor: "transparent",
1538
- color: C.textMuted,
1539
- fontSize: "0.82rem",
1540
- fontWeight: 600,
1541
- cursor: "pointer"
1542
- };
1543
- const emptyStateStyles = {
1544
- display: "flex",
1545
- flexDirection: "column",
1546
- alignItems: "center",
1547
- justifyContent: "center",
1548
- padding: "4rem 2rem",
1549
- gap: "0.75rem",
1550
- flex: 1
1551
- };
1552
- const emptyStateIconStyles = {
1553
- fontSize: "2.5rem",
1554
- color: C.textMuted,
1555
- opacity: .4
1556
- };
1557
- const emptyStateTextStyles = {
1558
- margin: 0,
1559
- fontSize: "0.95rem",
1560
- fontWeight: 600,
1561
- color: C.textDim
1562
- };
1563
- const emptyStateSubtextStyles = {
1564
- margin: 0,
1565
- fontSize: "0.82rem",
1566
- color: C.textMuted,
1567
- textAlign: "center"
1568
- };
1569
- const inlineCodeStyles = {
1570
- fontFamily: "ui-monospace, monospace",
1571
- backgroundColor: C.bgCard,
1572
- border: `1px solid ${C.border}`,
1573
- borderRadius: "4px",
1574
- padding: "0.1em 0.4em",
1575
- fontSize: "0.88em",
1576
- color: C.teal
1577
- };
1578
- const contentLayoutStyles = {
1579
- display: "grid",
1580
- gridTemplateColumns: "200px minmax(0, 1fr)",
1581
- flex: 1,
1582
- minHeight: 0,
1583
- overflow: "hidden"
1584
- };
1585
- const sidebarStyles = {
1586
- display: "flex",
1587
- flexDirection: "column",
1588
- gap: "0",
1589
- padding: "0.75rem",
1590
- borderRight: `1px solid ${C.border}`,
1591
- background: C.bgPanel,
1592
- overflowY: "auto"
1593
- };
1594
- const navStyles = {
1595
- display: "flex",
1596
- flexDirection: "column",
1597
- gap: "0.25rem",
1598
- marginBottom: "1rem"
1599
- };
1600
- const navIconStyles = {
1601
- fontSize: "0.7rem",
1602
- opacity: .7
1603
- };
1604
- const sidebarInfoStyles = {
1605
- marginTop: "auto",
1606
- padding: "0.75rem",
1607
- borderRadius: "8px",
1608
- background: C.bgCard,
1609
- border: `1px solid ${C.border}`
1610
- };
1611
- const sidebarInfoLabelStyles = {
1612
- fontSize: "0.65rem",
1613
- fontWeight: 600,
1614
- letterSpacing: "0.08em",
1615
- textTransform: "uppercase",
1616
- color: C.textMuted,
1617
- marginBottom: "0.3rem"
1618
- };
1619
- const sidebarInfoValueStyles = {
1620
- fontSize: "0.88rem",
1621
- fontWeight: 600,
1622
- color: C.text,
1623
- wordBreak: "break-word",
1624
- fontFamily: "ui-monospace, monospace"
1625
- };
1626
- const sidebarInfoSubStyles = {
1627
- fontSize: "0.72rem",
1628
- color: C.textMuted,
1629
- fontFamily: "ui-monospace, monospace",
1630
- marginTop: "0.25rem"
1631
- };
1632
- const mainPaneStyles = {
1633
- minWidth: 0,
1634
- overflowY: "auto",
1635
- padding: "1rem"
1636
- };
1637
- const overviewLayoutStyles = {
1638
- display: "flex",
1639
- flexDirection: "column",
1640
- gap: "1.25rem"
1641
- };
1642
- const overviewCardsRowStyles = {
1643
- display: "grid",
1644
- gridTemplateColumns: "repeat(auto-fit, minmax(11rem, 1fr))",
1645
- gap: "0.75rem"
1646
- };
1647
- const overviewCardStyles = {
1648
- padding: "0.9rem 1rem",
1649
- borderRadius: "10px",
1650
- border: `1px solid ${C.border}`,
1651
- background: C.bgCard
1652
- };
1653
- const overviewCardLabelStyles = {
1654
- fontSize: "0.68rem",
1655
- fontWeight: 600,
1656
- letterSpacing: "0.08em",
1657
- textTransform: "uppercase",
1658
- color: C.textMuted,
1659
- marginBottom: "0.4rem"
1660
- };
1661
- const overviewCardValueStyles = {
1662
- fontSize: "1rem",
1663
- fontWeight: 700,
1664
- color: C.text,
1665
- wordBreak: "break-word"
1666
- };
1667
- const overviewSectionStyles = {
1668
- display: "flex",
1669
- flexDirection: "column",
1670
- gap: "0.5rem"
1671
- };
1672
- const overviewSectionTitleStyles = {
1673
- fontSize: "0.72rem",
1674
- fontWeight: 600,
1675
- letterSpacing: "0.08em",
1676
- textTransform: "uppercase",
1677
- color: C.textMuted
1678
- };
1679
- const overviewSectionHeaderStyles = {
1680
- display: "flex",
1681
- alignItems: "center",
1682
- justifyContent: "space-between",
1683
- gap: "0.75rem"
1684
- };
1685
- const overviewRefreshButtonStyles = {
1686
- border: `1px solid ${C.border}`,
1687
- borderRadius: "7px",
1688
- padding: "0.35rem 0.7rem",
1689
- backgroundColor: "transparent",
1690
- color: C.textDim,
1691
- fontSize: "0.78rem",
1692
- fontFamily: "ui-monospace, monospace",
1693
- cursor: "pointer",
1694
- flexShrink: 0
1695
- };
1696
- const overviewAccumulatorValueStyles = {
1697
- padding: "0.75rem",
1698
- borderRadius: "8px",
1699
- border: `1px solid ${C.border}`,
1700
- background: C.bgCard,
1701
- fontFamily: "ui-monospace, monospace",
1702
- fontSize: "0.78rem",
1703
- color: C.text,
1704
- lineHeight: 1.5,
1705
- wordBreak: "break-all"
1706
- };
1707
- const overviewAccumulatorEmptyStyles = {
1708
- color: C.textMuted,
1709
- fontStyle: "italic"
1710
- };
1711
- const overviewAccumulatorErrorStyles = { color: C.error };
1712
- const overviewEmptyStyles = {
1713
- fontSize: "0.82rem",
1714
- color: C.textMuted,
1715
- fontStyle: "italic"
1716
- };
1717
- const crdtTableListStyles = {
1718
- display: "flex",
1719
- flexDirection: "column",
1720
- gap: "0.25rem"
1721
- };
1722
- const crdtTableRowStyles = {
1723
- display: "flex",
1724
- alignItems: "center",
1725
- gap: "0.6rem",
1726
- padding: "0.5rem 0.75rem",
1727
- borderRadius: "7px",
1728
- background: C.bgCard,
1729
- border: `1px solid ${C.border}`
1730
- };
1731
- const crdtTableIconStyles = {
1732
- color: C.teal,
1733
- fontSize: "0.72rem"
1734
- };
1735
- const crdtTableNameStyles = {
1736
- fontFamily: "ui-monospace, monospace",
1737
- fontSize: "0.85rem",
1738
- color: C.text
1739
- };
1740
- const permissionRowStyles = {
1741
- display: "flex",
1742
- alignItems: "center",
1743
- gap: "0.6rem",
1744
- fontSize: "0.82rem",
1745
- color: C.textDim
1746
- };
1747
- const permissionIconStyles = {
1748
- color: C.success,
1749
- fontWeight: 700,
1750
- fontSize: "0.85rem",
1751
- lineHeight: 1
1752
- };
1753
- const permissionTextStyles = { color: C.textDim };
1754
- const dataActionsStyles = {
1755
- display: "flex",
1756
- gap: "0.4rem"
1757
- };
1758
- const dataButtonStyles = {
1759
- border: `1px solid ${C.border}`,
1760
- borderRadius: "7px",
1761
- padding: "0.4rem 0.8rem",
1762
- backgroundColor: "transparent",
1763
- color: C.teal,
1764
- fontSize: "0.78rem",
1765
- fontWeight: 600,
1766
- cursor: "pointer",
1767
- whiteSpace: "nowrap"
1768
- };
1769
- const dataStatusSuccessStyles = {
1770
- fontSize: "0.75rem",
1771
- color: C.success
1772
- };
1773
- const dataStatusErrorStyles = {
1774
- fontSize: "0.75rem",
1775
- color: C.error,
1776
- wordBreak: "break-word"
1777
- };
1778
- const dangerZoneStyles = {
1779
- marginTop: "auto",
1780
- borderRadius: "10px",
1781
- border: `1px solid ${C.errorBorder}`,
1782
- padding: "0.75rem 1rem",
1783
- background: C.errorBg
1784
- };
1785
- const dangerZoneTitleStyles = {
1786
- fontSize: "0.65rem",
1787
- fontWeight: 700,
1788
- letterSpacing: "0.08em",
1789
- textTransform: "uppercase",
1790
- color: C.error,
1791
- marginBottom: "0.5rem"
1792
- };
1793
- const dangerZoneRowStyles = {
1794
- display: "flex",
1795
- alignItems: "center",
1796
- justifyContent: "space-between",
1797
- gap: "1rem"
1798
- };
1799
- const dangerZoneDescStyles = {
1800
- fontSize: "0.75rem",
1801
- color: C.textMuted,
1802
- lineHeight: 1.5
1803
- };
1804
- const dangerZoneActionsStyles = {
1805
- display: "flex",
1806
- gap: "0.4rem",
1807
- flexShrink: 0
1808
- };
1809
- const resetButtonStyles = {
1810
- flexShrink: 0,
1811
- border: `1px solid ${C.errorBorder}`,
1812
- borderRadius: "7px",
1813
- padding: "0.35rem 0.75rem",
1814
- backgroundColor: "transparent",
1815
- color: C.error,
1816
- fontSize: "0.78rem",
1817
- fontWeight: 600,
1818
- cursor: "pointer",
1819
- whiteSpace: "nowrap"
1820
- };
1821
- const resetCancelButtonStyles = {
1822
- border: `1px solid ${C.border}`,
1823
- borderRadius: "7px",
1824
- padding: "0.35rem 0.65rem",
1825
- backgroundColor: "transparent",
1826
- color: C.textDim,
1827
- fontSize: "0.78rem",
1828
- fontWeight: 600,
1829
- cursor: "pointer",
1830
- whiteSpace: "nowrap"
1831
- };
1832
- const resetConfirmButtonStyles = {
1833
- border: "none",
1834
- borderRadius: "7px",
1835
- padding: "0.35rem 0.75rem",
1836
- backgroundColor: C.error,
1837
- color: "#0e1015",
1838
- fontSize: "0.78rem",
1839
- fontWeight: 700,
1840
- cursor: "pointer",
1841
- whiteSpace: "nowrap"
1842
- };
1843
- const queryRunnerLayoutStyles = {
1844
- display: "flex",
1845
- flexDirection: "column",
1846
- gap: "0.75rem",
1847
- height: "100%"
1848
- };
1849
- const queryToolbarStyles = {
1850
- display: "flex",
1851
- alignItems: "center",
1852
- justifyContent: "space-between",
1853
- gap: "0.75rem",
1854
- flexWrap: "wrap"
1855
- };
1856
- const targetToggleStyles = {
1857
- display: "flex",
1858
- gap: "0",
1859
- borderRadius: "8px",
1860
- border: `1px solid ${C.border}`,
1861
- overflow: "hidden",
1862
- background: C.bgCard
1863
- };
1864
- const queryToolbarRightStyles = {
1865
- display: "flex",
1866
- alignItems: "center",
1867
- gap: "0.6rem"
1868
- };
1869
- const noInstanceWarningStyles = {
1870
- fontSize: "0.75rem",
1871
- color: C.amber,
1872
- fontWeight: 500
1873
- };
1874
- const shortcutHintStyles = {
1875
- fontSize: "0.72rem",
1876
- color: C.textMuted,
1877
- fontFamily: "ui-monospace, monospace",
1878
- padding: "0.2rem 0.5rem",
1879
- borderRadius: "5px",
1880
- border: `1px solid ${C.border}`,
1881
- background: C.bgCard
1882
- };
1883
- const editorWrapperStyles = {
1884
- borderRadius: "10px",
1885
- border: `1px solid ${C.border}`,
1886
- overflow: "hidden",
1887
- background: C.bgInput
1888
- };
1889
- const textareaStyles = {
1890
- width: "100%",
1891
- minHeight: "9rem",
1892
- resize: "vertical",
1893
- border: "none",
1894
- padding: "0.85rem 1rem",
1895
- backgroundColor: "transparent",
1896
- color: C.text,
1897
- fontSize: "0.875rem",
1898
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace",
1899
- lineHeight: 1.6,
1900
- outline: "none",
1901
- boxSizing: "border-box"
1902
- };
1903
- const helperTextStyles = {
1904
- fontSize: "0.75rem",
1905
- lineHeight: 1.5,
1906
- color: C.textMuted
1907
- };
1908
- const resultPanelStyles = {
1909
- display: "flex",
1910
- flexDirection: "column",
1911
- gap: "0.6rem",
1912
- borderRadius: "10px",
1913
- border: `1px solid ${C.border}`,
1914
- overflow: "hidden",
1915
- background: C.bgPanel
1916
- };
1917
- const errorPanelStyles = {
1918
- display: "flex",
1919
- flexDirection: "column",
1920
- gap: "0.6rem",
1921
- padding: "0.85rem 1rem",
1922
- borderRadius: "10px",
1923
- border: `1px solid ${C.errorBorder}`,
1924
- background: C.errorBg
1925
- };
1926
- const resultHeaderStyles = {
1927
- display: "flex",
1928
- alignItems: "center",
1929
- gap: "0.6rem",
1930
- padding: "0.6rem 0.85rem",
1931
- borderBottom: `1px solid ${C.border}`,
1932
- background: C.bgCard
1933
- };
1934
- const successBadgeStyles = {
1935
- fontSize: "0.72rem",
1936
- fontWeight: 700,
1937
- color: C.success,
1938
- backgroundColor: C.successBg,
1939
- borderRadius: "5px",
1940
- padding: "0.15rem 0.5rem",
1941
- fontFamily: "ui-monospace, monospace"
1942
- };
1943
- const errorBadgeStyles = {
1944
- fontSize: "0.72rem",
1945
- fontWeight: 700,
1946
- color: C.error,
1947
- backgroundColor: C.errorBg,
1948
- borderRadius: "5px",
1949
- padding: "0.15rem 0.5rem",
1950
- fontFamily: "ui-monospace, monospace"
1951
- };
1952
- const resultMetaStyles = {
1953
- fontSize: "0.72rem",
1954
- color: C.textMuted,
1955
- fontFamily: "ui-monospace, monospace"
1956
- };
1957
- const resultEmptyStyles = {
1958
- padding: "1.5rem",
1959
- fontSize: "0.82rem",
1960
- color: C.textMuted,
1961
- fontStyle: "italic",
1962
- textAlign: "center"
1963
- };
1964
- const resultRawStyles = {
1965
- margin: 0,
1966
- padding: "0.85rem 1rem",
1967
- overflowX: "auto",
1968
- whiteSpace: "pre-wrap",
1969
- wordBreak: "break-word",
1970
- fontSize: "0.8rem",
1971
- lineHeight: 1.5,
1972
- color: C.text,
1973
- fontFamily: "ui-monospace, monospace"
1974
- };
1975
- const errorMessageStyles = {
1976
- margin: 0,
1977
- fontSize: "0.82rem",
1978
- lineHeight: 1.6,
1979
- color: C.error,
1980
- fontFamily: "ui-monospace, monospace",
1981
- whiteSpace: "pre-wrap",
1982
- wordBreak: "break-word"
1983
- };
1984
- const tableWrapperStyles = {
1985
- overflowX: "auto",
1986
- maxHeight: "320px",
1987
- overflowY: "auto"
1988
- };
1989
- const tableStyles = {
1990
- width: "100%",
1991
- borderCollapse: "collapse",
1992
- fontSize: "0.8rem",
1993
- fontFamily: "ui-monospace, SFMono-Regular, Menlo, monospace"
1994
- };
1995
- const thStyles = {
1996
- padding: "0.5rem 0.85rem",
1997
- textAlign: "left",
1998
- fontWeight: 600,
1999
- fontSize: "0.7rem",
2000
- letterSpacing: "0.05em",
2001
- textTransform: "uppercase",
2002
- color: C.textMuted,
2003
- borderBottom: `1px solid ${C.border}`,
2004
- borderRight: `1px solid ${C.border}`,
2005
- background: C.bgCard,
2006
- whiteSpace: "nowrap",
2007
- position: "sticky",
2008
- top: 0
2009
- };
2010
- const thRowNumStyles = {
2011
- ...thStyles,
2012
- color: C.textMuted,
2013
- opacity: .5,
2014
- width: "2.5rem",
2015
- textAlign: "right"
2016
- };
2017
- const trStyles = { background: "transparent" };
2018
- const trAltStyles = { background: C.tealGlow };
2019
- const tdStyles = {
2020
- padding: "0.45rem 0.85rem",
2021
- borderBottom: `1px solid ${C.border}`,
2022
- borderRight: `1px solid ${C.border}`,
2023
- color: C.text,
2024
- whiteSpace: "nowrap",
2025
- maxWidth: "300px",
2026
- overflow: "hidden",
2027
- textOverflow: "ellipsis"
2028
- };
2029
- const tdRowNumStyles = {
2030
- ...tdStyles,
2031
- color: C.textMuted,
2032
- opacity: .5,
2033
- textAlign: "right",
2034
- userSelect: "none"
2035
- };
2036
- const nullValueStyles = {
2037
- color: C.textMuted,
2038
- fontStyle: "italic",
2039
- opacity: .6
2040
- };
2041
- const jsonValueStyles = { color: C.textDim };
2042
- function getTabButtonStyles(isActive) {
2043
- return {
2044
- display: "flex",
2045
- alignItems: "center",
2046
- gap: "0.5rem",
2047
- width: "100%",
2048
- border: "none",
2049
- borderRadius: "7px",
2050
- padding: "0.6rem 0.75rem",
2051
- background: isActive ? C.tealDim : "transparent",
2052
- color: isActive ? C.teal : C.textDim,
2053
- fontSize: "0.82rem",
2054
- fontWeight: isActive ? 700 : 500,
2055
- textAlign: "left",
2056
- cursor: "pointer",
2057
- transition: "background 0.15s",
2058
- borderLeft: isActive ? `2px solid ${C.teal}` : "2px solid transparent"
2059
- };
2060
- }
2061
- function getTargetButtonStyles(isActive) {
2062
- return {
2063
- border: "none",
2064
- borderRight: `1px solid ${C.border}`,
2065
- padding: "0.45rem 0.85rem",
2066
- background: isActive ? C.tealDim : "transparent",
2067
- color: isActive ? C.teal : C.textMuted,
2068
- fontSize: "0.78rem",
2069
- fontWeight: isActive ? 700 : 500,
2070
- cursor: "pointer",
2071
- fontFamily: "ui-monospace, monospace",
2072
- transition: "background 0.1s",
2073
- whiteSpace: "nowrap"
2074
- };
2075
- }
2076
- function runButtonStyles(enabled, running) {
2077
- return {
2078
- display: "inline-flex",
2079
- alignItems: "center",
2080
- gap: "0.4rem",
2081
- border: "none",
2082
- borderRadius: "7px",
2083
- padding: "0.45rem 0.9rem",
2084
- background: enabled ? C.teal : C.bgCard,
2085
- color: enabled ? "#0e1015" : C.textMuted,
2086
- fontSize: "0.82rem",
2087
- fontWeight: 700,
2088
- fontFamily: "ui-monospace, monospace",
2089
- cursor: enabled ? "pointer" : "not-allowed",
2090
- opacity: running ? .75 : 1,
2091
- transition: "background 0.15s",
2092
- whiteSpace: "nowrap"
2093
- };
2094
- }
2095
- const runningDotStyles = {
2096
- width: "6px",
2097
- height: "6px",
2098
- borderRadius: "50%",
2099
- backgroundColor: "currentColor"
2100
- };
2101
- const schemaLayoutStyles = {
2102
- display: "flex",
2103
- flexDirection: "column",
2104
- gap: "1.5rem"
2105
- };
2106
- const schemaSectionStyles = {
2107
- display: "flex",
2108
- flexDirection: "column",
2109
- gap: "0.65rem"
2110
- };
2111
- const schemaSectionHeaderStyles = {
2112
- display: "flex",
2113
- alignItems: "center",
2114
- gap: "0.6rem"
2115
- };
2116
- const schemaSectionTitleStyles = {
2117
- fontSize: "0.72rem",
2118
- fontWeight: 700,
2119
- letterSpacing: "0.08em",
2120
- textTransform: "uppercase",
2121
- color: C.textMuted
2122
- };
2123
- const schemaBadgeStyles = {
2124
- fontSize: "0.68rem",
2125
- fontWeight: 700,
2126
- color: C.teal,
2127
- backgroundColor: C.tealDim,
2128
- borderRadius: "4px",
2129
- padding: "0.1rem 0.4rem",
2130
- fontFamily: "ui-monospace, monospace"
2131
- };
2132
- const schemaMigrationsMetaStyles = {
2133
- display: "flex",
2134
- alignItems: "center",
2135
- gap: "0.5rem",
2136
- marginLeft: "auto"
2137
- };
2138
- const schemaVersionChipStyles = {
2139
- fontSize: "0.68rem",
2140
- fontFamily: "ui-monospace, monospace",
2141
- color: C.textMuted,
2142
- backgroundColor: C.bgCard,
2143
- border: `1px solid ${C.border}`,
2144
- borderRadius: "4px",
2145
- padding: "0.1rem 0.5rem"
2146
- };
2147
- const schemaTableGridStyles = {
2148
- borderRadius: "10px",
2149
- border: `1px solid ${C.border}`,
2150
- overflow: "hidden"
2151
- };
2152
- const schemaTableHeaderRowStyles = {
2153
- display: "grid",
2154
- gridTemplateColumns: "1fr 1fr 90px",
2155
- gap: 0,
2156
- backgroundColor: C.bgCard,
2157
- borderBottom: `1px solid ${C.border}`
2158
- };
2159
- const schemaColHeaderStyles = {
2160
- padding: "0.5rem 0.85rem",
2161
- fontSize: "0.68rem",
2162
- fontWeight: 700,
2163
- letterSpacing: "0.07em",
2164
- textTransform: "uppercase",
2165
- color: C.textMuted,
2166
- borderRight: `1px solid ${C.border}`
2167
- };
2168
- const schemaTableRowStyles = {
2169
- display: "grid",
2170
- gridTemplateColumns: "1fr 1fr 90px",
2171
- gap: 0,
2172
- borderBottom: `1px solid ${C.border}`
2173
- };
2174
- const schemaTableNameStyles = {
2175
- display: "flex",
2176
- alignItems: "center",
2177
- gap: "0.5rem",
2178
- padding: "0.6rem 0.85rem",
2179
- fontSize: "0.82rem",
2180
- fontFamily: "ui-monospace, monospace",
2181
- color: C.text,
2182
- borderRight: `1px solid ${C.border}`
2183
- };
2184
- const schemaTableIconStyles = {
2185
- color: C.teal,
2186
- fontSize: "0.68rem",
2187
- opacity: .8
2188
- };
2189
- const schemaCrdtNameStyles = {
2190
- display: "flex",
2191
- alignItems: "center",
2192
- padding: "0.6rem 0.85rem",
2193
- fontSize: "0.82rem",
2194
- fontFamily: "ui-monospace, monospace",
2195
- color: C.textDim,
2196
- borderRight: `1px solid ${C.border}`
2197
- };
2198
- const schemaStatusCellStyles = {
2199
- display: "flex",
2200
- alignItems: "center",
2201
- justifyContent: "center",
2202
- padding: "0.6rem 0.5rem"
2203
- };
2204
- const schemaActiveTagStyles = {
2205
- fontSize: "0.68rem",
2206
- fontWeight: 700,
2207
- color: C.success,
2208
- backgroundColor: C.successBg,
2209
- borderRadius: "4px",
2210
- padding: "0.15rem 0.45rem",
2211
- fontFamily: "ui-monospace, monospace"
2212
- };
2213
- const schemaEmptyStyles = {
2214
- fontSize: "0.82rem",
2215
- color: C.textMuted,
2216
- fontStyle: "italic"
2217
- };
2218
- const migrationListStyles = {
2219
- display: "flex",
2220
- flexDirection: "column",
2221
- gap: "0.35rem"
2222
- };
2223
- function getMigrationRowStyles(applied) {
2224
- return {
2225
- display: "grid",
2226
- gridTemplateColumns: "3.5rem 1fr 70px",
2227
- alignItems: "center",
2228
- gap: "0.75rem",
2229
- padding: "0.55rem 0.85rem",
2230
- borderRadius: "8px",
2231
- background: applied ? C.bgCard : "transparent",
2232
- border: `1px solid ${applied ? C.border : C.borderLight}`,
2233
- opacity: applied ? 1 : .55
2234
- };
2235
- }
2236
- const migrationVersionStyles = {
2237
- fontSize: "0.78rem",
2238
- fontWeight: 700,
2239
- fontFamily: "ui-monospace, monospace",
2240
- color: C.textDim
2241
- };
2242
- const migrationBarTrackStyles = {
2243
- height: "4px",
2244
- borderRadius: "2px",
2245
- backgroundColor: C.border,
2246
- overflow: "hidden"
2247
- };
2248
- function getMigrationBarFillStyles(applied) {
2249
- return {
2250
- height: "100%",
2251
- width: applied ? "100%" : "0%",
2252
- borderRadius: "2px",
2253
- backgroundColor: C.teal,
2254
- transition: "width 0.3s ease"
2255
- };
2256
- }
2257
- function getMigrationTagStyles(applied, isCurrent) {
2258
- if (isCurrent) return {
2259
- fontSize: "0.68rem",
2260
- fontWeight: 700,
2261
- color: C.teal,
2262
- backgroundColor: C.tealDim,
2263
- borderRadius: "4px",
2264
- padding: "0.15rem 0.45rem",
2265
- fontFamily: "ui-monospace, monospace",
2266
- textAlign: "center"
2267
- };
2268
- if (applied) return {
2269
- fontSize: "0.68rem",
2270
- fontWeight: 600,
2271
- color: C.success,
2272
- backgroundColor: C.successBg,
2273
- borderRadius: "4px",
2274
- padding: "0.15rem 0.45rem",
2275
- fontFamily: "ui-monospace, monospace",
2276
- textAlign: "center"
2277
- };
2278
- return {
2279
- fontSize: "0.68rem",
2280
- fontWeight: 600,
2281
- color: C.amber,
2282
- backgroundColor: "rgba(251,191,36,0.08)",
2283
- borderRadius: "4px",
2284
- padding: "0.15rem 0.45rem",
2285
- fontFamily: "ui-monospace, monospace",
2286
- textAlign: "center"
2287
- };
2288
- }
2289
- const liveQueryHeaderRowStyles = {
2290
- display: "grid",
2291
- gridTemplateColumns: "minmax(0, 1fr) 220px 110px",
2292
- gap: 0,
2293
- backgroundColor: C.bgCard,
2294
- borderBottom: `1px solid ${C.border}`
2295
- };
2296
- const liveQueryRowStyles = {
2297
- display: "grid",
2298
- gridTemplateColumns: "minmax(0, 1fr) 220px 110px",
2299
- gap: 0,
2300
- borderBottom: `1px solid ${C.border}`
2301
- };
2302
- const liveQuerySqlStyles = {
2303
- padding: "0.6rem 0.85rem",
2304
- fontSize: "0.78rem",
2305
- fontFamily: "ui-monospace, monospace",
2306
- color: C.text,
2307
- borderRight: `1px solid ${C.border}`,
2308
- whiteSpace: "pre-wrap",
2309
- overflowWrap: "anywhere"
2310
- };
2311
- const liveQueryParametersStyles = {
2312
- padding: "0.6rem 0.85rem",
2313
- fontSize: "0.78rem",
2314
- fontFamily: "ui-monospace, monospace",
2315
- color: C.textDim,
2316
- borderRight: `1px solid ${C.border}`,
2317
- whiteSpace: "pre-wrap",
2318
- overflowWrap: "anywhere"
2319
- };
2320
- const liveQueryIdleTagStyles = {
2321
- fontSize: "0.68rem",
2322
- fontWeight: 700,
2323
- color: C.textMuted,
2324
- backgroundColor: C.bgCard,
2325
- border: `1px solid ${C.border}`,
2326
- borderRadius: "4px",
2327
- padding: "0.15rem 0.45rem",
2328
- fontFamily: "ui-monospace, monospace"
2329
- };
2330
- const eventLogLayoutStyles = {
2331
- display: "flex",
2332
- flexDirection: "column",
2333
- gap: "0.75rem",
2334
- height: "100%"
2335
- };
2336
- const eventLogToolbarStyles = {
2337
- display: "flex",
2338
- alignItems: "center",
2339
- gap: "0.5rem",
2340
- flexWrap: "wrap",
2341
- flexShrink: 0
2342
- };
2343
- const eventLogFilterSelectStyles = {
2344
- borderRadius: "7px",
2345
- border: `1px solid ${C.border}`,
2346
- padding: "0.35rem 0.6rem",
2347
- backgroundColor: C.bgCard,
2348
- color: C.text,
2349
- fontSize: "0.78rem",
2350
- fontFamily: "ui-monospace, monospace",
2351
- cursor: "pointer"
2352
- };
2353
- const eventLogRefreshButtonStyles = {
2354
- border: `1px solid ${C.border}`,
2355
- borderRadius: "7px",
2356
- padding: "0.35rem 0.7rem",
2357
- backgroundColor: "transparent",
2358
- color: C.textDim,
2359
- fontSize: "0.78rem",
2360
- fontFamily: "ui-monospace, monospace",
2361
- cursor: "pointer"
2362
- };
2363
- const eventLogCountStyles = {
2364
- marginLeft: "auto",
2365
- fontSize: "0.72rem",
2366
- fontFamily: "ui-monospace, monospace",
2367
- color: C.textMuted
2368
- };
2369
- const eventLogEmptyStyles = {
2370
- padding: "2rem",
2371
- textAlign: "center",
2372
- fontSize: "0.82rem",
2373
- color: C.textMuted,
2374
- fontStyle: "italic"
2375
- };
2376
- const eventLogListStyles = {
2377
- display: "flex",
2378
- flexDirection: "column",
2379
- gap: "0.25rem",
2380
- overflowY: "auto",
2381
- flex: 1
2382
- };
2383
- function getEventRowStyles(isExpanded) {
2384
- return {
2385
- borderRadius: "8px",
2386
- border: `1px solid ${isExpanded ? `${C.teal}40` : C.border}`,
2387
- background: isExpanded ? C.tealGlow : C.bgCard,
2388
- overflow: "hidden",
2389
- flexShrink: 0
2390
- };
2391
- }
2392
- const eventRowHeaderStyles = {
2393
- display: "flex",
2394
- alignItems: "center",
2395
- gap: "0.5rem",
2396
- width: "100%",
2397
- padding: "0.5rem 0.75rem",
2398
- background: "transparent",
2399
- border: "none",
2400
- cursor: "pointer",
2401
- textAlign: "left",
2402
- flexWrap: "wrap"
2403
- };
2404
- const eventSyncIdStyles = {
2405
- fontSize: "0.7rem",
2406
- fontFamily: "ui-monospace, monospace",
2407
- color: C.textMuted,
2408
- minWidth: "3.5rem"
2409
- };
2410
- const eventDatasetStyles = {
2411
- fontSize: "0.8rem",
2412
- fontFamily: "ui-monospace, monospace",
2413
- color: C.text,
2414
- fontWeight: 600
2415
- };
2416
- const eventItemIdStyles = {
2417
- fontSize: "0.75rem",
2418
- fontFamily: "ui-monospace, monospace",
2419
- color: C.textMuted,
2420
- flex: 1,
2421
- overflow: "hidden",
2422
- textOverflow: "ellipsis",
2423
- whiteSpace: "nowrap",
2424
- minWidth: 0
2425
- };
2426
- const eventTimestampStyles = {
2427
- fontSize: "0.7rem",
2428
- fontFamily: "ui-monospace, monospace",
2429
- color: C.textMuted,
2430
- marginLeft: "auto"
2431
- };
2432
- const eventChevronStyles = {
2433
- fontSize: "0.6rem",
2434
- color: C.textMuted,
2435
- marginLeft: "0.25rem"
2436
- };
2437
- const eventPayloadStyles = {
2438
- borderTop: `1px solid ${C.border}`,
2439
- padding: "0.6rem 0.75rem"
2440
- };
2441
- const eventPayloadMetaStyles = {
2442
- display: "flex",
2443
- gap: "0.75rem",
2444
- marginBottom: "0.5rem"
2445
- };
2446
- const eventMetaItemStyles = {
2447
- fontSize: "0.68rem",
2448
- fontFamily: "ui-monospace, monospace",
2449
- color: C.textMuted
2450
- };
2451
- const eventPayloadPreStyles = {
2452
- margin: 0,
2453
- fontSize: "0.78rem",
2454
- fontFamily: "ui-monospace, monospace",
2455
- color: C.text,
2456
- lineHeight: 1.5,
2457
- whiteSpace: "pre-wrap",
2458
- wordBreak: "break-word"
2459
- };
2460
- const loadMoreButtonStyles = {
2461
- width: "100%",
2462
- border: `1px solid ${C.border}`,
2463
- borderRadius: "8px",
2464
- padding: "0.6rem",
2465
- backgroundColor: "transparent",
2466
- color: C.textDim,
2467
- fontSize: "0.78rem",
2468
- fontFamily: "ui-monospace, monospace",
2469
- cursor: "pointer",
2470
- flexShrink: 0
2471
- };
2472
- const eventTypeBadgeBase = {
2473
- fontSize: "0.65rem",
2474
- fontWeight: 700,
2475
- borderRadius: "4px",
2476
- padding: "0.1rem 0.4rem",
2477
- fontFamily: "ui-monospace, monospace",
2478
- whiteSpace: "nowrap"
2479
- };
2480
- const eventTypeCreateStyles = {
2481
- ...eventTypeBadgeBase,
2482
- color: C.teal,
2483
- backgroundColor: C.tealDim
2484
- };
2485
- const eventTypeUpdateStyles = {
2486
- ...eventTypeBadgeBase,
2487
- color: "#a78bfa",
2488
- backgroundColor: "rgba(167,139,250,0.12)"
2489
- };
2490
- const eventTypeDeleteStyles = {
2491
- ...eventTypeBadgeBase,
2492
- color: C.error,
2493
- backgroundColor: "rgba(248,113,113,0.12)"
2494
- };
2495
- const eventOriginOwnStyles = {
2496
- ...eventTypeBadgeBase,
2497
- color: C.success,
2498
- backgroundColor: C.successBg
2499
- };
2500
- const eventOriginRemoteStyles = {
2501
- ...eventTypeBadgeBase,
2502
- color: C.amber,
2503
- backgroundColor: "rgba(251,191,36,0.1)"
2504
- };
2505
- const eventOriginLocalStyles = {
2506
- ...eventTypeBadgeBase,
2507
- color: C.textDim,
2508
- backgroundColor: C.bgCard,
2509
- border: `1px solid ${C.border}`
2510
- };
2511
- const eventStatusAppliedStyles = {
2512
- ...eventTypeBadgeBase,
2513
- color: C.success,
2514
- backgroundColor: C.successBg
2515
- };
2516
- const eventStatusPendingStyles = {
2517
- ...eventTypeBadgeBase,
2518
- color: C.amber,
2519
- backgroundColor: "rgba(251,191,36,0.1)"
2520
- };
2521
- const eventStatusFailedStyles = {
2522
- ...eventTypeBadgeBase,
2523
- color: C.error,
2524
- backgroundColor: C.errorBg
2525
- };
2526
- const eventStatusSkippedStyles = {
2527
- ...eventTypeBadgeBase,
2528
- color: C.textMuted,
2529
- backgroundColor: C.bgCard
2530
- };
2531
1712
  //#endregion
2532
1713
  export { SQLiteSyncDevtools };
2533
1714