@reddb-io/ui 0.1.1

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.
Files changed (169) hide show
  1. package/LICENSE +17 -0
  2. package/bin/red-ui.js +259 -0
  3. package/build/CNAME +1 -0
  4. package/build/_app/env.js +1 -0
  5. package/build/_app/immutable/assets/0.C63jY5jX.css +1 -0
  6. package/build/_app/immutable/assets/10.Dblyk0Kr.css +1 -0
  7. package/build/_app/immutable/assets/EmptyState.RXcH7ryE.css +1 -0
  8. package/build/_app/immutable/assets/Workspace.BCzG1DsH.css +1 -0
  9. package/build/_app/immutable/assets/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
  10. package/build/_app/immutable/assets/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
  11. package/build/_app/immutable/assets/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
  12. package/build/_app/immutable/assets/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
  13. package/build/_app/immutable/assets/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
  14. package/build/_app/immutable/assets/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
  15. package/build/_app/immutable/assets/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
  16. package/build/_app/immutable/assets/jetbrains-mono-cyrillic-wght-normal.D73BlboJ.woff2 +0 -0
  17. package/build/_app/immutable/assets/jetbrains-mono-greek-wght-normal.Bw9x6K1M.woff2 +0 -0
  18. package/build/_app/immutable/assets/jetbrains-mono-latin-ext-wght-normal.DBQx-q_a.woff2 +0 -0
  19. package/build/_app/immutable/assets/jetbrains-mono-latin-wght-normal.B9CIFXIH.woff2 +0 -0
  20. package/build/_app/immutable/assets/jetbrains-mono-vietnamese-wght-normal.Bt-aOZkq.woff2 +0 -0
  21. package/build/_app/immutable/chunks/3j4cZ58b.js +1 -0
  22. package/build/_app/immutable/chunks/69_IOA4Y.js +1 -0
  23. package/build/_app/immutable/chunks/ALqaDrNQ.js +2 -0
  24. package/build/_app/immutable/chunks/BGcIe2ep.js +2 -0
  25. package/build/_app/immutable/chunks/BMHKbvs8.js +1 -0
  26. package/build/_app/immutable/chunks/BQHbwf54.js +916 -0
  27. package/build/_app/immutable/chunks/Bgso8cDH.js +311 -0
  28. package/build/_app/immutable/chunks/Bmgx1mfv.js +310 -0
  29. package/build/_app/immutable/chunks/BrvvOVaJ.js +1 -0
  30. package/build/_app/immutable/chunks/Bzak7iHL.js +1 -0
  31. package/build/_app/immutable/chunks/D9CTkMjK.js +1 -0
  32. package/build/_app/immutable/chunks/DIeogL5L.js +1 -0
  33. package/build/_app/immutable/chunks/DXY_Rxa7.js +113 -0
  34. package/build/_app/immutable/chunks/DhEqZVGG.js +1 -0
  35. package/build/_app/immutable/chunks/Ov8N-vxt.js +1 -0
  36. package/build/_app/immutable/chunks/oiO4RLqp.js +1 -0
  37. package/build/_app/immutable/entry/app.D_IVtpdv.js +2 -0
  38. package/build/_app/immutable/entry/start.3oC0UQ_Q.js +1 -0
  39. package/build/_app/immutable/nodes/0.CHPiFxDo.js +1 -0
  40. package/build/_app/immutable/nodes/1.BC_4YZ0h.js +1 -0
  41. package/build/_app/immutable/nodes/10.B7DMKSNc.js +60 -0
  42. package/build/_app/immutable/nodes/2.CAG5_cGP.js +1 -0
  43. package/build/_app/immutable/nodes/3.CAG5_cGP.js +1 -0
  44. package/build/_app/immutable/nodes/4.CAG5_cGP.js +1 -0
  45. package/build/_app/immutable/nodes/5.CAG5_cGP.js +1 -0
  46. package/build/_app/immutable/nodes/6.CAG5_cGP.js +1 -0
  47. package/build/_app/immutable/nodes/7.BZX9Iird.js +134 -0
  48. package/build/_app/immutable/nodes/8.CAG5_cGP.js +1 -0
  49. package/build/_app/immutable/nodes/9.CAG5_cGP.js +1 -0
  50. package/build/_app/version.json +1 -0
  51. package/build/favicon.png +0 -0
  52. package/build/index.html +59 -0
  53. package/dist/embed/RedUiEmbedRoot-BGoLxJf1.js +26562 -0
  54. package/dist/embed/RedUiEmbedRoot-BGoLxJf1.js.map +1 -0
  55. package/dist/embed/bundle-mjs-UuVRz3O4.js +3047 -0
  56. package/dist/embed/bundle-mjs-UuVRz3O4.js.map +1 -0
  57. package/dist/embed/core-QuRYvMt8.js +118 -0
  58. package/dist/embed/core-QuRYvMt8.js.map +1 -0
  59. package/dist/embed/index-B6ZQhCsu.js +577 -0
  60. package/dist/embed/index-B6ZQhCsu.js.map +1 -0
  61. package/dist/embed/index-client-ItCEU64C.js +3087 -0
  62. package/dist/embed/index-client-ItCEU64C.js.map +1 -0
  63. package/dist/embed/index-fad77a13.esm-BqPj0saH.js +1318 -0
  64. package/dist/embed/index-fad77a13.esm-BqPj0saH.js.map +1 -0
  65. package/dist/embed/index.js +11 -0
  66. package/dist/embed/index.js.map +1 -0
  67. package/dist/embed/sigma-node-border.esm-CGtlgqmo.js +432 -0
  68. package/dist/embed/sigma-node-border.esm-CGtlgqmo.js.map +1 -0
  69. package/dist/embed/sigma.esm-y0qgY-OC.js +2355 -0
  70. package/dist/embed/sigma.esm-y0qgY-OC.js.map +1 -0
  71. package/dist/embed/ui.css +1 -0
  72. package/package.json +101 -0
  73. package/src/app.css +238 -0
  74. package/src/lib/ActivityIndicator.svelte +100 -0
  75. package/src/lib/ClusterNode.svelte +119 -0
  76. package/src/lib/ClusterView.svelte +541 -0
  77. package/src/lib/CollectionHistory.svelte +266 -0
  78. package/src/lib/CollectionsView.svelte +14 -0
  79. package/src/lib/CommandPalette.svelte +237 -0
  80. package/src/lib/ConnectDropdown.svelte +222 -0
  81. package/src/lib/EmptyState.svelte +27 -0
  82. package/src/lib/LiveChanges.svelte +195 -0
  83. package/src/lib/MasterPasswordDialog.svelte +168 -0
  84. package/src/lib/PageHeader.svelte +32 -0
  85. package/src/lib/PendingChangesPanel.svelte +153 -0
  86. package/src/lib/QueryEditor.svelte +122 -0
  87. package/src/lib/ResultsPane.svelte +747 -0
  88. package/src/lib/SchemaTree.svelte +193 -0
  89. package/src/lib/SecurityView.svelte +255 -0
  90. package/src/lib/ShortcutOverlay.svelte +104 -0
  91. package/src/lib/Splash.svelte +46 -0
  92. package/src/lib/StatusBar.svelte +163 -0
  93. package/src/lib/SubNav.svelte +62 -0
  94. package/src/lib/Topbar.svelte +119 -0
  95. package/src/lib/Workspace.svelte +247 -0
  96. package/src/lib/activity.svelte.ts +91 -0
  97. package/src/lib/build-info.ts +41 -0
  98. package/src/lib/capability.ts +221 -0
  99. package/src/lib/cn.ts +6 -0
  100. package/src/lib/collection-catalog.ts +116 -0
  101. package/src/lib/collection-pages.ts +33 -0
  102. package/src/lib/connection-history.ts +301 -0
  103. package/src/lib/connections.svelte.ts +493 -0
  104. package/src/lib/embed/RedUiEmbedRoot.svelte +31 -0
  105. package/src/lib/embed/index.ts +234 -0
  106. package/src/lib/graph-loader/contract.ts +336 -0
  107. package/src/lib/graph-loader/drilldown.ts +334 -0
  108. package/src/lib/graph-loader/index.ts +13 -0
  109. package/src/lib/index.ts +114 -0
  110. package/src/lib/pending-changes.svelte.ts +149 -0
  111. package/src/lib/query-tabs.svelte.ts +130 -0
  112. package/src/lib/reddb/boot-params.ts +254 -0
  113. package/src/lib/reddb/cdc-stream-client.ts +234 -0
  114. package/src/lib/reddb/client.ts +698 -0
  115. package/src/lib/reddb/connection-bootstrap.ts +260 -0
  116. package/src/lib/reddb/connection-provider.contract.ts +72 -0
  117. package/src/lib/reddb/connection-provider.ts +72 -0
  118. package/src/lib/reddb/index.ts +14 -0
  119. package/src/lib/reddb/injected-client-provider.ts +76 -0
  120. package/src/lib/reddb/local-url-provider.ts +245 -0
  121. package/src/lib/reddb/secure-store.ts +41 -0
  122. package/src/lib/reddb/server-capabilities.ts +71 -0
  123. package/src/lib/reddb/tauri-encrypted-store.ts +64 -0
  124. package/src/lib/reddb/transports.ts +89 -0
  125. package/src/lib/reddb/types.ts +32 -0
  126. package/src/lib/reddb/uri.ts +54 -0
  127. package/src/lib/reddb/web-encrypted-store.ts +201 -0
  128. package/src/lib/renderers/DiffRenderer.svelte +184 -0
  129. package/src/lib/renderers/DocumentRenderer.svelte +134 -0
  130. package/src/lib/renderers/GraphRenderer.svelte +1328 -0
  131. package/src/lib/renderers/HypertableRenderer.svelte +253 -0
  132. package/src/lib/renderers/JsonRenderer.svelte +30 -0
  133. package/src/lib/renderers/KvRenderer.svelte +150 -0
  134. package/src/lib/renderers/QueueRenderer.svelte +286 -0
  135. package/src/lib/renderers/StatsRenderer.svelte +123 -0
  136. package/src/lib/renderers/TableRenderer.svelte +440 -0
  137. package/src/lib/renderers/VectorRenderer.svelte +388 -0
  138. package/src/lib/renderers/diff-render.ts +122 -0
  139. package/src/lib/renderers/diff.ts +9 -0
  140. package/src/lib/renderers/document-render.ts +64 -0
  141. package/src/lib/renderers/document.ts +9 -0
  142. package/src/lib/renderers/graph-render.ts +631 -0
  143. package/src/lib/renderers/graph-sigma.ts +395 -0
  144. package/src/lib/renderers/graph.ts +9 -0
  145. package/src/lib/renderers/hypertable-render.ts +320 -0
  146. package/src/lib/renderers/hypertable.ts +9 -0
  147. package/src/lib/renderers/index.ts +42 -0
  148. package/src/lib/renderers/json-render.ts +89 -0
  149. package/src/lib/renderers/json.ts +10 -0
  150. package/src/lib/renderers/kv-render.ts +193 -0
  151. package/src/lib/renderers/kv.ts +9 -0
  152. package/src/lib/renderers/queue-render.ts +197 -0
  153. package/src/lib/renderers/queue.ts +9 -0
  154. package/src/lib/renderers/registry.ts +68 -0
  155. package/src/lib/renderers/stats-render.ts +73 -0
  156. package/src/lib/renderers/stats.ts +9 -0
  157. package/src/lib/renderers/table-render.ts +156 -0
  158. package/src/lib/renderers/table.ts +11 -0
  159. package/src/lib/renderers/types.ts +33 -0
  160. package/src/lib/renderers/vector-render.ts +154 -0
  161. package/src/lib/renderers/vector.ts +9 -0
  162. package/src/lib/router.svelte.ts +163 -0
  163. package/src/lib/secureStore.svelte.ts +148 -0
  164. package/src/lib/shadow-dom-spike/ShadowDomSpikeCore.svelte +198 -0
  165. package/src/lib/shadow-dom-spike/mount-shadow-dom-spike.ts +94 -0
  166. package/src/lib/shortcuts.ts +22 -0
  167. package/src/lib/surface.ts +80 -0
  168. package/src/lib/tabs.svelte.ts +115 -0
  169. package/src/lib/theme.svelte.ts +63 -0
@@ -0,0 +1,153 @@
1
+ <script lang="ts">
2
+ import { pendingChanges, buildUpdateSql } from './pending-changes.svelte'
3
+ import { connection } from './connections.svelte'
4
+ import { X, AlertTriangle, Lock } from 'lucide-svelte'
5
+
6
+ // Mutations are committed against the server; block them while it reports
7
+ // read-only (#23). The reason explains *why* the control is disabled.
8
+ const readOnly = $derived(connection.readOnly)
9
+ const readOnlyReason = $derived(connection.readOnlyReason)
10
+
11
+ interface Props {
12
+ open: boolean
13
+ onClose: () => void
14
+ }
15
+
16
+ let { open, onClose }: Props = $props()
17
+
18
+ let committing = $state(false)
19
+ let lastError = $state<string | null>(null)
20
+
21
+ async function apply() {
22
+ if (committing) return
23
+ committing = true
24
+ lastError = null
25
+ try {
26
+ const r = await pendingChanges.commitAll()
27
+ if (r.failed.length === 0) {
28
+ onClose()
29
+ }
30
+ // On partial failure the panel stays open so the user can see the
31
+ // failures inline. We don't auto-close.
32
+ } catch (e) {
33
+ lastError = (e as Error).message
34
+ } finally {
35
+ committing = false
36
+ }
37
+ }
38
+
39
+ function discard() {
40
+ pendingChanges.discardAll()
41
+ onClose()
42
+ }
43
+ </script>
44
+
45
+ {#if open}
46
+ <div
47
+ class="fixed inset-0 z-50 grid place-items-center bg-bg-0/70 backdrop-blur-sm motion-reduce:backdrop-blur-none"
48
+ role="dialog"
49
+ aria-modal="true"
50
+ aria-label="Pending changes"
51
+ >
52
+ <div class="w-full max-w-2xl mx-4 bg-bg-1 border border-line-1 rounded-md shadow-lg overflow-hidden">
53
+ <div class="flex items-center justify-between px-4 py-3 border-b border-line-1">
54
+ <div>
55
+ <h2 class="type-h3 m-0">Pending changes</h2>
56
+ <p class="text-fg-3 text-[11px] font-mono m-0 mt-0.5">
57
+ {pendingChanges.count} staged · review before applying
58
+ </p>
59
+ </div>
60
+ <button
61
+ type="button"
62
+ class="p-1 text-fg-3 hover:text-fg-1"
63
+ aria-label="Close"
64
+ onclick={onClose}
65
+ >
66
+ <X class="size-4" />
67
+ </button>
68
+ </div>
69
+
70
+ <div class="max-h-[50vh] overflow-auto px-4 py-3">
71
+ {#if pendingChanges.count === 0}
72
+ <div class="text-fg-3 text-[12px] font-mono py-6 text-center">
73
+ No staged changes.
74
+ </div>
75
+ {:else}
76
+ <ul class="space-y-2 text-[12px] font-mono">
77
+ {#each pendingChanges.changes as change (change.id)}
78
+ <li class="border border-line-1 rounded px-2 py-1.5 bg-bg-2/30">
79
+ <div class="flex items-center justify-between gap-2 mb-1">
80
+ <span class="text-fg-2">
81
+ <span class="text-fg-1">{change.table}</span>
82
+ <span class="text-fg-3"> · rid </span>
83
+ <span class="text-fg-1 tabular-nums">{change.row}</span>
84
+ <span class="text-fg-3"> · </span>
85
+ <span class="text-fg-1">{change.col}</span>
86
+ </span>
87
+ <button
88
+ type="button"
89
+ class="text-fg-3 hover:text-fg-1 text-[11px]"
90
+ onclick={() => pendingChanges.unstage(change.id)}
91
+ >
92
+ Unstage
93
+ </button>
94
+ </div>
95
+ <div class="text-[11px] grid grid-cols-[auto_1fr] gap-x-2 gap-y-0.5">
96
+ <span class="text-fg-3">old</span>
97
+ <span class="text-fg-2 truncate">{String(change.oldValue ?? 'NULL')}</span>
98
+ <span class="text-fg-3">new</span>
99
+ <span class="text-accent truncate">{change.newValue}</span>
100
+ </div>
101
+ <div class="text-fg-3 text-[10.5px] mt-1 truncate">
102
+ {buildUpdateSql(change)}
103
+ </div>
104
+ {#if change.error}
105
+ <div class="text-warn text-[11px] mt-1 inline-flex items-center gap-1">
106
+ <AlertTriangle class="size-3" /> {change.error}
107
+ </div>
108
+ {/if}
109
+ </li>
110
+ {/each}
111
+ </ul>
112
+ {/if}
113
+ {#if lastError}
114
+ <div class="text-warn text-[11px] font-mono mt-2">{lastError}</div>
115
+ {/if}
116
+ </div>
117
+
118
+ {#if readOnly}
119
+ <div class="flex items-center gap-1.5 px-4 py-2 text-warn text-[11px] font-mono border-t border-line-1 bg-warn/5">
120
+ <Lock class="size-3 shrink-0" />
121
+ <span>{readOnlyReason ?? 'Server is read-only (file in use) — changes can’t be applied.'}</span>
122
+ </div>
123
+ {/if}
124
+
125
+ <div class="flex items-center justify-end gap-2 px-4 py-3 border-t border-line-1 bg-bg-1/60">
126
+ <button
127
+ type="button"
128
+ class="text-[12px] font-mono px-3 py-1 text-fg-3 hover:text-fg-1"
129
+ onclick={onClose}
130
+ >
131
+ Cancel
132
+ </button>
133
+ <button
134
+ type="button"
135
+ class="text-[12px] font-mono px-3 py-1 text-fg-1 border border-line-1 rounded hover:bg-bg-2"
136
+ onclick={discard}
137
+ disabled={pendingChanges.count === 0}
138
+ >
139
+ Discard all
140
+ </button>
141
+ <button
142
+ type="button"
143
+ class="text-[12px] font-mono px-3 py-1 bg-accent text-white rounded disabled:opacity-50"
144
+ onclick={apply}
145
+ disabled={pendingChanges.count === 0 || committing || readOnly}
146
+ title={readOnly ? (readOnlyReason ?? 'Server is read-only — mutations are disabled.') : undefined}
147
+ >
148
+ {committing ? 'Applying…' : 'Apply all'}
149
+ </button>
150
+ </div>
151
+ </div>
152
+ </div>
153
+ {/if}
@@ -0,0 +1,122 @@
1
+ <script lang="ts">
2
+ import { Play, Loader2 } from 'lucide-svelte'
3
+ import { connection } from '$lib/connections.svelte'
4
+ import { queryTabs, deriveQueryLabel } from '$lib/query-tabs.svelte'
5
+ import { tabs } from '$lib/tabs.svelte'
6
+ import { registry } from '$lib/renderers'
7
+ import type { Capability } from '$lib/renderers'
8
+ import { Kbd } from '@reddb-io/ui-kit'
9
+
10
+ interface Props {
11
+ tabId: string
12
+ fallbackLabel: string
13
+ }
14
+
15
+ let { tabId, fallbackLabel }: Props = $props()
16
+
17
+ // Ensure qs exists for this tab id.
18
+ $effect(() => {
19
+ queryTabs.ensure(tabId)
20
+ })
21
+
22
+ const qs = $derived(queryTabs.states[tabId])
23
+ const tab = $derived(tabs.tabs.find((t) => t.id === tabId))
24
+
25
+ let textareaEl: HTMLTextAreaElement | undefined = $state()
26
+
27
+ function setSql(value: string) {
28
+ queryTabs.setSql(tabId, value)
29
+ // Re-derive label live as the user types.
30
+ const label = deriveQueryLabel(value, fallbackLabel)
31
+ const tab = tabs.tabs.find((t) => t.id === tabId)
32
+ if (tab && tab.label !== label) tabs.rename(tabId, label)
33
+ }
34
+
35
+ async function run() {
36
+ await queryTabs.run(tabId)
37
+ }
38
+
39
+ function onKey(e: KeyboardEvent) {
40
+ if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
41
+ e.preventDefault()
42
+ void run()
43
+ }
44
+ }
45
+
46
+ function pickRenderer() {
47
+ if (!qs?.result) return null
48
+ return registry.pick(qs.result.capability as Capability | undefined, qs.result, tab?.overrideCapability)
49
+ }
50
+ </script>
51
+
52
+ <div class="flex h-full flex-col">
53
+ <div class="flex flex-col border-b border-line-1 bg-bg-0" style="height: 40%;">
54
+ <div class="flex items-center justify-between px-3 py-1.5 border-b border-line-1">
55
+ <div class="flex items-center gap-2 text-fg-3 type-label">
56
+ <span>SQL</span>
57
+ {#if connection.connected}
58
+ <span class="text-fg-3">·</span>
59
+ <span class="text-fg-2 font-mono text-[11px] truncate max-w-[280px]">{connection.active.url}</span>
60
+ {:else}
61
+ <span class="text-warn font-mono text-[11px]">disconnected</span>
62
+ {/if}
63
+ </div>
64
+ <div class="flex items-center gap-2">
65
+ <span class="text-fg-3 font-mono text-[11px] hidden sm:inline">Run with <Kbd>⌘</Kbd><Kbd>↵</Kbd></span>
66
+ <button
67
+ type="button"
68
+ class="inline-flex items-center gap-1 rounded border border-accent/60 bg-accent/10 px-2 py-1 text-[12px] font-mono text-accent hover:bg-accent/20 disabled:opacity-50 disabled:cursor-not-allowed"
69
+ onclick={run}
70
+ disabled={qs?.running || !connection.connected}
71
+ aria-label="Run query"
72
+ >
73
+ {#if qs?.running}
74
+ <Loader2 class="size-3.5 animate-spin" />
75
+ <span>Running…</span>
76
+ {:else}
77
+ <Play class="size-3.5" />
78
+ <span>Run</span>
79
+ {/if}
80
+ </button>
81
+ </div>
82
+ </div>
83
+ <textarea
84
+ bind:this={textareaEl}
85
+ class="flex-1 resize-none bg-bg-0 px-3 py-2 font-mono text-[13px] text-fg-1 outline-none placeholder:text-fg-3"
86
+ spellcheck="false"
87
+ autocapitalize="off"
88
+ placeholder={'SELECT * FROM users LIMIT 100'}
89
+ value={qs?.sql ?? ''}
90
+ oninput={(e) => setSql((e.currentTarget as HTMLTextAreaElement).value)}
91
+ onkeydown={onKey}
92
+ ></textarea>
93
+ </div>
94
+
95
+ <div class="flex-1 overflow-hidden bg-bg-0">
96
+ {#if qs?.error}
97
+ <div class="p-4 text-[12px] font-mono text-warn">
98
+ <div class="font-semibold mb-1">Query failed</div>
99
+ <pre class="whitespace-pre-wrap">{qs.error}</pre>
100
+ </div>
101
+ {:else if qs?.running && !qs.result}
102
+ <div class="h-full grid place-items-center text-fg-3 text-[12px] font-mono">
103
+ Running…
104
+ </div>
105
+ {:else if !qs?.result}
106
+ <div class="h-full grid place-items-center text-fg-3 text-[12px] font-mono">
107
+ Run a query to see results here.
108
+ </div>
109
+ {:else}
110
+ {#if qs.result.truncated}
111
+ <div class="px-3 py-1.5 border-b border-line-1 bg-bg-1 text-[11px] font-mono text-fg-3">
112
+ Streamed first {qs.result.record_count.toLocaleString()} rows — more remain on the server. Add a <span class="text-fg-2">LIMIT</span> to narrow.
113
+ </div>
114
+ {/if}
115
+ {@const renderer = pickRenderer()}
116
+ {#if renderer}
117
+ {@const Renderer = renderer.component}
118
+ <Renderer result={qs.result} showSystem={tab?.showSystemColumns ?? false} />
119
+ {/if}
120
+ {/if}
121
+ </div>
122
+ </div>