setSearch(e.target.value)}
- style={{ padding: 6, width: 320 }}
- />
-
+
+
+
+
+
+
+
- {list.isLoading &&
loading…
}
- {list.isError &&
{String(list.error)}
}
-
- {list.data && (
-
-
-
- | name |
- rows |
- fragments |
- pending |
- last updated |
- last compaction |
- action |
-
-
-
- {list.data.experiments.map((exp) => (
-
- ))}
-
-
- )}
+
+
+
+ setSearch(e.target.value)}
+ />
+
+
+
{fmtInt(total)} total
+
-
-
-
- page {page + 1} / {maxPage + 1}
-
-
+
+ {list.isError &&
{String(list.error)}
}
+ {!list.isError && (
+
+
+
+ | Experiment |
+ Rows |
+ Fragments |
+ Pending |
+ Updated |
+ Compacted |
+ Action |
+
+
+
+ {list.isLoading &&
+ Array.from({ length: 6 }).map((_, i) => (
+
+ {Array.from({ length: 7 }).map((__, j) => (
+ |
+
+ |
+ ))}
+
+ ))}
+ {!list.isLoading && rows.map((exp) =>
)}
+
+
+ )}
+ {!list.isLoading && !list.isError && rows.length === 0 && (
+
+ No experiments{search ? ` matching “${search}”` : ""}.
+
+ )}
+
+ {maxPage > 0 && (
+
+
+ page {page + 1} / {maxPage + 1}
+
+
+
+
+ )}
+
+ {selected &&
}
);
}
diff --git a/crates/lance-context-master/ui/src/main.tsx b/crates/lance-context-master/ui/src/main.tsx
index e479872d..8965f80d 100644
--- a/crates/lance-context-master/ui/src/main.tsx
+++ b/crates/lance-context-master/ui/src/main.tsx
@@ -1,6 +1,7 @@
import ReactDOM from "react-dom/client";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { App } from "./App";
+import "./styles.css";
const queryClient = new QueryClient({
defaultOptions: {
diff --git a/crates/lance-context-master/ui/src/styles.css b/crates/lance-context-master/ui/src/styles.css
new file mode 100644
index 00000000..09ba6236
--- /dev/null
+++ b/crates/lance-context-master/ui/src/styles.css
@@ -0,0 +1,643 @@
+/* ============================================================================
+ lance-context master — design system
+ A restrained, data-dense control-plane UI in the register of a frontier AI
+ lab: near-black canvas, hairline borders, monospace numerics, a single
+ electric accent, and typography that gets out of the way of the data.
+ ========================================================================== */
+
+@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap");
+
+:root {
+ /* Surfaces — layered near-blacks with a faint blue cast. */
+ --bg: #08090c;
+ --bg-elev: #0e1015;
+ --bg-elev-2: #14161d;
+ --bg-hover: #171a22;
+ --panel: #0c0e13;
+
+ /* Hairlines. */
+ --border: #1c1f27;
+ --border-strong: #2a2e39;
+
+ /* Text. */
+ --text: #e8eaed;
+ --text-muted: #9aa0aa;
+ --text-dim: #5f656f;
+
+ /* Accent — electric indigo/cyan, used sparingly for signal. */
+ --accent: #6d7cff;
+ --accent-hover: #8590ff;
+ --accent-dim: rgba(109, 124, 255, 0.12);
+ --accent-border: rgba(109, 124, 255, 0.35);
+
+ /* Status. */
+ --ok: #3fb950;
+ --ok-dim: rgba(63, 185, 80, 0.12);
+ --warn: #d29922;
+ --warn-dim: rgba(210, 153, 34, 0.12);
+ --err: #f85149;
+ --err-dim: rgba(248, 81, 73, 0.12);
+ --run: #58a6ff;
+ --run-dim: rgba(88, 166, 255, 0.12);
+
+ --radius: 8px;
+ --radius-sm: 6px;
+ --mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;
+ --sans: "Inter", system-ui, -apple-system, sans-serif;
+
+ --shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 8px 24px rgba(0, 0, 0, 0.35);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html,
+body,
+#root {
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ background:
+ radial-gradient(1200px 600px at 80% -10%, rgba(109, 124, 255, 0.06), transparent 60%),
+ var(--bg);
+ color: var(--text);
+ font-family: var(--sans);
+ font-size: 14px;
+ line-height: 1.5;
+ -webkit-font-smoothing: antialiased;
+ text-rendering: optimizeLegibility;
+}
+
+::selection {
+ background: var(--accent-dim);
+}
+
+/* Scrollbar. */
+::-webkit-scrollbar {
+ width: 10px;
+ height: 10px;
+}
+::-webkit-scrollbar-thumb {
+ background: var(--border-strong);
+ border-radius: 8px;
+ border: 2px solid var(--bg);
+}
+::-webkit-scrollbar-thumb:hover {
+ background: #363b47;
+}
+
+/* ---- Layout ------------------------------------------------------------- */
+
+.app {
+ max-width: 1240px;
+ margin: 0 auto;
+ padding: 0 28px 80px;
+}
+
+.topbar {
+ position: sticky;
+ top: 0;
+ z-index: 20;
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 18px 0 16px;
+ margin-bottom: 24px;
+ background: linear-gradient(var(--bg) 70%, transparent);
+ border-bottom: 1px solid var(--border);
+}
+
+.brand {
+ display: flex;
+ align-items: center;
+ gap: 11px;
+}
+
+.brand__mark {
+ width: 26px;
+ height: 26px;
+ border-radius: 7px;
+ background: linear-gradient(135deg, var(--accent), #4a56d6);
+ box-shadow: 0 0 0 1px var(--accent-border), 0 4px 14px rgba(109, 124, 255, 0.35);
+ position: relative;
+ flex: none;
+}
+.brand__mark::after {
+ content: "";
+ position: absolute;
+ inset: 7px;
+ border-radius: 3px;
+ border: 1.5px solid rgba(255, 255, 255, 0.9);
+ border-bottom-color: transparent;
+ border-right-color: transparent;
+}
+
+.brand__name {
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ font-size: 15px;
+}
+.brand__sub {
+ color: var(--text-dim);
+ font-family: var(--mono);
+ font-size: 11px;
+ letter-spacing: 0.04em;
+ text-transform: uppercase;
+ padding: 2px 7px;
+ border: 1px solid var(--border);
+ border-radius: 5px;
+}
+
+.topbar__spacer {
+ flex: 1;
+}
+
+.live {
+ display: flex;
+ align-items: center;
+ gap: 7px;
+ color: var(--text-muted);
+ font-size: 12px;
+ font-family: var(--mono);
+}
+.live__dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: var(--ok);
+ box-shadow: 0 0 0 3px var(--ok-dim);
+ animation: pulse 2s ease-in-out infinite;
+}
+@keyframes pulse {
+ 50% {
+ opacity: 0.45;
+ }
+}
+
+/* ---- Stat strip --------------------------------------------------------- */
+
+.stats {
+ display: grid;
+ grid-template-columns: repeat(4, 1fr);
+ gap: 12px;
+ margin-bottom: 22px;
+}
+
+.stat {
+ background: linear-gradient(180deg, var(--bg-elev), var(--panel));
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ padding: 14px 16px;
+}
+.stat__label {
+ color: var(--text-dim);
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ margin-bottom: 8px;
+}
+.stat__value {
+ font-family: var(--mono);
+ font-size: 22px;
+ font-weight: 500;
+ letter-spacing: -0.02em;
+}
+.stat__value .unit {
+ color: var(--text-dim);
+ font-size: 13px;
+ margin-left: 4px;
+}
+
+/* ---- Toolbar ------------------------------------------------------------ */
+
+.toolbar {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ margin-bottom: 14px;
+}
+
+.search {
+ position: relative;
+ flex: 1;
+ max-width: 420px;
+}
+.search svg {
+ position: absolute;
+ left: 12px;
+ top: 50%;
+ transform: translateY(-50%);
+ color: var(--text-dim);
+ pointer-events: none;
+}
+.search input {
+ width: 100%;
+ padding: 9px 12px 9px 36px;
+ background: var(--bg-elev);
+ border: 1px solid var(--border);
+ border-radius: var(--radius-sm);
+ color: var(--text);
+ font-family: var(--sans);
+ font-size: 13px;
+ transition: border-color 0.15s, box-shadow 0.15s;
+}
+.search input::placeholder {
+ color: var(--text-dim);
+}
+.search input:focus {
+ outline: none;
+ border-color: var(--accent-border);
+ box-shadow: 0 0 0 3px var(--accent-dim);
+}
+
+.toolbar__count {
+ color: var(--text-muted);
+ font-size: 12px;
+ font-family: var(--mono);
+}
+
+/* ---- Table -------------------------------------------------------------- */
+
+.table-wrap {
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ overflow: hidden;
+ background: var(--panel);
+ box-shadow: var(--shadow);
+}
+
+table.grid {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 13px;
+}
+
+table.grid thead th {
+ text-align: left;
+ padding: 11px 16px;
+ background: var(--bg-elev);
+ border-bottom: 1px solid var(--border);
+ color: var(--text-dim);
+ font-weight: 500;
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ white-space: nowrap;
+}
+table.grid th.num,
+table.grid td.num {
+ text-align: right;
+ font-family: var(--mono);
+}
+
+table.grid tbody td {
+ padding: 12px 16px;
+ border-bottom: 1px solid var(--border);
+ vertical-align: middle;
+ white-space: nowrap;
+}
+table.grid tbody tr:last-child td {
+ border-bottom: none;
+}
+table.grid tbody tr {
+ transition: background 0.12s;
+}
+table.grid tbody tr:hover {
+ background: var(--bg-hover);
+}
+
+.name-cell {
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--text);
+ font-weight: 500;
+ cursor: pointer;
+ background: none;
+ border: none;
+ padding: 0;
+ font-family: var(--sans);
+ font-size: 13px;
+}
+.name-cell:hover {
+ color: var(--accent-hover);
+}
+.name-cell::before {
+ content: "";
+ width: 6px;
+ height: 6px;
+ border-radius: 2px;
+ background: var(--accent);
+ opacity: 0.55;
+ flex: none;
+}
+
+td .muted {
+ color: var(--text-dim);
+ font-family: var(--mono);
+ font-size: 12px;
+}
+
+/* Fragment health tint: hot fragments read amber. */
+.frag--hot {
+ color: var(--warn);
+}
+
+/* ---- Buttons ------------------------------------------------------------ */
+
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 6px 12px;
+ border-radius: var(--radius-sm);
+ border: 1px solid var(--border-strong);
+ background: var(--bg-elev-2);
+ color: var(--text);
+ font-family: var(--sans);
+ font-size: 12px;
+ font-weight: 500;
+ cursor: pointer;
+ transition: background 0.13s, border-color 0.13s, transform 0.05s;
+ white-space: nowrap;
+}
+.btn:hover:not(:disabled) {
+ background: var(--bg-hover);
+ border-color: #363b47;
+}
+.btn:active:not(:disabled) {
+ transform: translateY(1px);
+}
+.btn:disabled {
+ opacity: 0.45;
+ cursor: not-allowed;
+}
+.btn--accent {
+ background: var(--accent-dim);
+ border-color: var(--accent-border);
+ color: var(--accent-hover);
+}
+.btn--accent:hover:not(:disabled) {
+ background: rgba(109, 124, 255, 0.18);
+ border-color: var(--accent);
+}
+.btn--ghost {
+ background: transparent;
+ border-color: var(--border);
+}
+
+/* ---- Status pills ------------------------------------------------------- */
+
+.pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 3px 9px;
+ border-radius: 999px;
+ font-family: var(--mono);
+ font-size: 11px;
+ font-weight: 500;
+ letter-spacing: 0.01em;
+ border: 1px solid transparent;
+}
+.pill__dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: currentColor;
+}
+.pill--queued {
+ color: var(--text-muted);
+ background: var(--bg-elev-2);
+ border-color: var(--border-strong);
+}
+.pill--running {
+ color: var(--run);
+ background: var(--run-dim);
+ border-color: rgba(88, 166, 255, 0.3);
+}
+.pill--running .pill__dot {
+ animation: pulse 1.1s ease-in-out infinite;
+}
+.pill--done {
+ color: var(--ok);
+ background: var(--ok-dim);
+ border-color: rgba(63, 185, 80, 0.3);
+}
+.pill--failed {
+ color: var(--err);
+ background: var(--err-dim);
+ border-color: rgba(248, 81, 73, 0.3);
+}
+
+/* ---- Pagination --------------------------------------------------------- */
+
+.pager {
+ display: flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 14px;
+ margin-top: 16px;
+}
+.pager__info {
+ color: var(--text-muted);
+ font-family: var(--mono);
+ font-size: 12px;
+}
+
+/* ---- Detail drawer ------------------------------------------------------ */
+
+.scrim {
+ position: fixed;
+ inset: 0;
+ background: rgba(4, 5, 8, 0.6);
+ backdrop-filter: blur(2px);
+ z-index: 40;
+ animation: fade 0.16s ease;
+}
+@keyframes fade {
+ from {
+ opacity: 0;
+ }
+}
+
+.drawer {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ width: min(520px, 92vw);
+ background: var(--panel);
+ border-left: 1px solid var(--border-strong);
+ z-index: 50;
+ box-shadow: -20px 0 60px rgba(0, 0, 0, 0.5);
+ display: flex;
+ flex-direction: column;
+ animation: slide 0.22s cubic-bezier(0.16, 1, 0.3, 1);
+}
+@keyframes slide {
+ from {
+ transform: translateX(24px);
+ opacity: 0;
+ }
+}
+
+.drawer__head {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+ padding: 20px 22px 16px;
+ border-bottom: 1px solid var(--border);
+}
+.drawer__title {
+ font-size: 16px;
+ font-weight: 600;
+ letter-spacing: -0.01em;
+ word-break: break-all;
+ display: flex;
+ align-items: center;
+ gap: 9px;
+}
+.drawer__title .glyph {
+ width: 8px;
+ height: 8px;
+ border-radius: 2px;
+ background: var(--accent);
+ flex: none;
+}
+.drawer__uri {
+ color: var(--text-dim);
+ font-family: var(--mono);
+ font-size: 11px;
+ margin-top: 4px;
+ word-break: break-all;
+}
+.iconbtn {
+ background: none;
+ border: 1px solid var(--border);
+ color: var(--text-muted);
+ border-radius: var(--radius-sm);
+ width: 30px;
+ height: 30px;
+ display: grid;
+ place-items: center;
+ cursor: pointer;
+ flex: none;
+ transition: background 0.13s, color 0.13s;
+}
+.iconbtn:hover {
+ background: var(--bg-hover);
+ color: var(--text);
+}
+
+.drawer__body {
+ padding: 20px 22px;
+ overflow-y: auto;
+ flex: 1;
+}
+
+.metric-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 1px;
+ background: var(--border);
+ border: 1px solid var(--border);
+ border-radius: var(--radius);
+ overflow: hidden;
+ margin-bottom: 20px;
+}
+.metric {
+ background: var(--bg-elev);
+ padding: 14px 16px;
+}
+.metric__label {
+ color: var(--text-dim);
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.05em;
+ margin-bottom: 6px;
+}
+.metric__value {
+ font-family: var(--mono);
+ font-size: 16px;
+ font-weight: 500;
+ word-break: break-all;
+}
+.metric--wide {
+ grid-column: 1 / -1;
+}
+.metric--wide .metric__value {
+ font-size: 12px;
+ color: var(--text-muted);
+}
+
+.drawer__actions {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 16px 22px;
+ border-top: 1px solid var(--border);
+}
+
+.section-label {
+ color: var(--text-dim);
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ margin: 4px 0 10px;
+}
+
+/* ---- States ------------------------------------------------------------- */
+
+.empty,
+.loading,
+.error {
+ padding: 56px 20px;
+ text-align: center;
+ color: var(--text-muted);
+ font-size: 13px;
+}
+.error {
+ color: var(--err);
+ font-family: var(--mono);
+ font-size: 12px;
+}
+.spinner {
+ display: inline-block;
+ width: 15px;
+ height: 15px;
+ border: 2px solid var(--border-strong);
+ border-top-color: var(--accent);
+ border-radius: 50%;
+ animation: spin 0.7s linear infinite;
+ vertical-align: -3px;
+ margin-right: 8px;
+}
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+.skeleton {
+ height: 13px;
+ border-radius: 4px;
+ background: linear-gradient(90deg, var(--bg-elev) 25%, var(--bg-elev-2) 50%, var(--bg-elev) 75%);
+ background-size: 200% 100%;
+ animation: shimmer 1.3s infinite;
+}
+@keyframes shimmer {
+ to {
+ background-position: -200% 0;
+ }
+}
+
+@media (max-width: 720px) {
+ .stats {
+ grid-template-columns: repeat(2, 1fr);
+ }
+}