ltcai 2.2.7 → 3.0.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.
- package/README.md +63 -32
- package/docs/CHANGELOG.md +82 -0
- package/docs/V3_BACKEND_ARCHITECTURE.md +138 -0
- package/docs/V3_FRONTEND.md +136 -0
- package/knowledge_graph.py +649 -21
- package/latticeai/__init__.py +1 -1
- package/latticeai/api/admin.py +47 -0
- package/latticeai/api/agents.py +54 -31
- package/latticeai/api/auth.py +1 -1
- package/latticeai/api/chat.py +10 -2
- package/latticeai/api/search.py +236 -0
- package/latticeai/api/static_routes.py +11 -2
- package/latticeai/core/config.py +16 -0
- package/latticeai/core/embedding_providers.py +502 -0
- package/latticeai/core/local_embeddings.py +86 -0
- package/latticeai/core/workspace_os.py +1 -1
- package/latticeai/server_app.py +49 -1
- package/latticeai/services/agent_runtime.py +245 -0
- package/latticeai/services/search_service.py +346 -0
- package/package.json +6 -4
- package/static/account.html +9 -9
- package/static/activity.html +4 -4
- package/static/admin.html +8 -8
- package/static/agents.html +4 -4
- package/static/chat.html +10 -10
- package/static/css/reference/account.css +137 -1
- package/static/css/reference/chat.css +31 -37
- package/static/css/responsive.css +42 -0
- package/static/css/tokens.css +125 -130
- package/static/graph.html +9 -9
- package/static/manifest.json +3 -3
- package/static/plugins.html +4 -4
- package/static/scripts/account.js +4 -4
- package/static/scripts/chat.js +40 -8
- package/static/scripts/workspace.js +78 -0
- package/static/v3/css/lattice.base.css +128 -0
- package/static/v3/css/lattice.components.css +447 -0
- package/static/v3/css/lattice.shell.css +407 -0
- package/static/v3/css/lattice.tokens.css +132 -0
- package/static/v3/css/lattice.views.css +277 -0
- package/static/v3/index.html +40 -0
- package/static/v3/js/app.js +26 -0
- package/static/v3/js/core/api.js +327 -0
- package/static/v3/js/core/components.js +215 -0
- package/static/v3/js/core/dom.js +148 -0
- package/static/v3/js/core/fixtures.js +171 -0
- package/static/v3/js/core/router.js +37 -0
- package/static/v3/js/core/routes.js +73 -0
- package/static/v3/js/core/shell.js +363 -0
- package/static/v3/js/core/store.js +113 -0
- package/static/v3/js/views/admin-audit.js +185 -0
- package/static/v3/js/views/admin-permissions.js +178 -0
- package/static/v3/js/views/admin-policies.js +103 -0
- package/static/v3/js/views/admin-private-vpc.js +138 -0
- package/static/v3/js/views/admin-security.js +181 -0
- package/static/v3/js/views/admin-users.js +168 -0
- package/static/v3/js/views/agents.js +194 -0
- package/static/v3/js/views/chat.js +450 -0
- package/static/v3/js/views/files.js +180 -0
- package/static/v3/js/views/home.js +119 -0
- package/static/v3/js/views/hybrid-search.js +195 -0
- package/static/v3/js/views/knowledge-graph.js +238 -0
- package/static/v3/js/views/models.js +247 -0
- package/static/v3/js/views/my-computer.js +237 -0
- package/static/v3/js/views/pipeline.js +161 -0
- package/static/v3/js/views/settings.js +258 -0
- package/static/workflows.html +4 -4
- package/static/workspace.css +340 -2
- package/static/workspace.html +43 -24
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/* ============================================================================
|
|
2
|
+
* View: Settings — appearance, workspace, and integration readiness.
|
|
3
|
+
* This view WIRES real store state (theme + mode persist immediately) and
|
|
4
|
+
* probes the documented future endpoints so the v3 shell visibly reports
|
|
5
|
+
* whether it is talking to a live backend or clearly-badged sample data.
|
|
6
|
+
* ========================================================================== */
|
|
7
|
+
|
|
8
|
+
const MODE_DEFS = [
|
|
9
|
+
{ key: "basic", label: "Basic", desc: "Chat, search, and files — the essentials, nothing else." },
|
|
10
|
+
{ key: "advanced", label: "Advanced", desc: "Adds the pipeline, agents, and model runtime surfaces." },
|
|
11
|
+
{ key: "admin", label: "Admin", desc: "Reveals users, permissions, audit, security, and policies." },
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
// Endpoints the views light up against once the backend exposes them.
|
|
15
|
+
const PROBES = [
|
|
16
|
+
{ path: "/api/index/status", method: "GET", call: (api) => api.indexStatus() },
|
|
17
|
+
{ path: "/api/graph", method: "GET", call: (api) => api.graph() },
|
|
18
|
+
{ path: "/api/search/hybrid", method: "POST", call: (api) => api.hybridSearch("ping") },
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export async function render(ctx) {
|
|
22
|
+
const { h, icon, api, store, c, navigate, toast } = ctx;
|
|
23
|
+
|
|
24
|
+
const probesHost = h("div", c.loading({ lines: 3 }));
|
|
25
|
+
|
|
26
|
+
const embedHost = h("div", c.loading({ lines: 2 }));
|
|
27
|
+
|
|
28
|
+
const root = h("div.lt3-stack-6",
|
|
29
|
+
c.viewHeader({
|
|
30
|
+
eyebrow: "System",
|
|
31
|
+
title: "Settings",
|
|
32
|
+
sub: "Appearance, workspace, and integrations.",
|
|
33
|
+
}),
|
|
34
|
+
|
|
35
|
+
appearancePanel(ctx),
|
|
36
|
+
workspacePanel(ctx),
|
|
37
|
+
|
|
38
|
+
c.panel({
|
|
39
|
+
eyebrow: "Models",
|
|
40
|
+
title: "Embeddings",
|
|
41
|
+
sub: "The vector signal behind retrieval. Configure the provider with LATTICEAI_EMBEDDING_PROVIDER (hash · mlx · ollama · openai · custom).",
|
|
42
|
+
children: embedHost,
|
|
43
|
+
}),
|
|
44
|
+
|
|
45
|
+
c.panel({
|
|
46
|
+
eyebrow: "Status",
|
|
47
|
+
title: "Integration readiness",
|
|
48
|
+
sub: "Each view probes its endpoint and falls back to sample data until the backend answers. Live the moment these surfaces exist — no view changes required.",
|
|
49
|
+
children: h("div.lt3-stack-3",
|
|
50
|
+
probesHost,
|
|
51
|
+
h("p.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } },
|
|
52
|
+
"Views automatically switch to live data once these endpoints respond — the adapter prefers the real endpoint and only labels sample data when it is unreachable."),
|
|
53
|
+
),
|
|
54
|
+
}),
|
|
55
|
+
|
|
56
|
+
aboutPanel(ctx),
|
|
57
|
+
);
|
|
58
|
+
|
|
59
|
+
probeEndpoints(ctx, probesHost);
|
|
60
|
+
renderEmbeddings(ctx, embedHost);
|
|
61
|
+
return root;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/* ── Embeddings (Settings → Models → Embeddings) ────────────────────────── */
|
|
65
|
+
export function embeddingStatePill({ h, c }, st) {
|
|
66
|
+
const state = String(st.state || st.grade || "fallback").toLowerCase();
|
|
67
|
+
if (state === "production") return c.pill("Production", "ok");
|
|
68
|
+
if (state === "unavailable") return c.pill("Unavailable", "err");
|
|
69
|
+
return c.pill("Fallback", "warn");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function renderEmbeddings(ctx, host) {
|
|
73
|
+
const { h, c } = ctx;
|
|
74
|
+
const res = await ctx.api.embeddingsStatus();
|
|
75
|
+
const d = res.data || {};
|
|
76
|
+
const lastIndexed = d.last_indexed_at ? new Date(d.last_indexed_at).toLocaleString() : "Never";
|
|
77
|
+
host.replaceChildren(
|
|
78
|
+
h("div.lt3-stack-4",
|
|
79
|
+
h("div.lt3-row", { style: { "justify-content": "space-between", "align-items": "center", "flex-wrap": "wrap", gap: "var(--lt3-space-3)" } },
|
|
80
|
+
h("div.lt3-row-2",
|
|
81
|
+
h("span", { style: { color: "var(--lt3-pillar-vector, var(--accent))", display: "inline-flex" } }, ctx.icon("grid-dots")),
|
|
82
|
+
h("b", { style: { "font-size": "var(--lt3-text-md)" } }, providerLabel(d.active_provider || d.provider)),
|
|
83
|
+
),
|
|
84
|
+
h("div.lt3-row-2", embeddingStatePill(ctx, d), c.sourceBadge(res.source)),
|
|
85
|
+
),
|
|
86
|
+
d.fell_back
|
|
87
|
+
? c.banner(`Requested “${d.requested_provider}” is unavailable (${(d.health && d.health.detail) || "no detail"}); using the local hash fallback. Retrieval still works, but vectors are non-semantic until the provider is reachable.`, "warn", "alert-triangle")
|
|
88
|
+
: null,
|
|
89
|
+
h("dl.lt3-keyval",
|
|
90
|
+
h("dt", "Provider"), h("dd", providerLabel(d.active_provider || d.provider)),
|
|
91
|
+
h("dt", "Model"), h("dd", h("span.lt3-mono", d.model || d.model_id || "—")),
|
|
92
|
+
h("dt", "Dimensions"), h("dd", h("span.lt3-mono", String(d.dimensions || "—"))),
|
|
93
|
+
h("dt", "Status"), h("dd", embeddingStatePill(ctx, d)),
|
|
94
|
+
h("dt", "Last index"), h("dd", lastIndexed),
|
|
95
|
+
),
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function providerLabel(p) {
|
|
101
|
+
return ({ hash: "Local hash (fallback)", mlx: "MLX (Apple Silicon)", ollama: "Ollama",
|
|
102
|
+
openai: "OpenAI-compatible", custom: "Custom" }[String(p || "hash")]) || String(p || "—");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/* ── Appearance ─────────────────────────────────────────────────────────── */
|
|
106
|
+
function appearancePanel({ h, icon, store, c }) {
|
|
107
|
+
const themeKey = () => {
|
|
108
|
+
const t = store.get().theme;
|
|
109
|
+
return t === "light" || t === "dark" ? t : "";
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const themeSlot = h("div");
|
|
113
|
+
const buildTheme = () => c.segmented(
|
|
114
|
+
[{ key: "light", label: "Light" }, { key: "dark", label: "Dark" }, { key: "", label: "System" }],
|
|
115
|
+
themeKey(),
|
|
116
|
+
(k) => { store.setTheme(k); themeSlot.replaceChildren(buildTheme()); },
|
|
117
|
+
);
|
|
118
|
+
themeSlot.append(buildTheme());
|
|
119
|
+
|
|
120
|
+
const modeSeg = c.segmented(
|
|
121
|
+
MODE_DEFS.map((m) => ({ key: m.key, label: m.label })),
|
|
122
|
+
store.get().mode,
|
|
123
|
+
(k) => { store.setMode(k); modeNote.replaceChildren(noteFor(k)); },
|
|
124
|
+
);
|
|
125
|
+
const noteFor = (k) => h("span", (MODE_DEFS.find((m) => m.key === k) || MODE_DEFS[0]).desc);
|
|
126
|
+
const modeNote = h("p.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } }, noteFor(store.get().mode));
|
|
127
|
+
|
|
128
|
+
return c.panel({
|
|
129
|
+
eyebrow: "Appearance",
|
|
130
|
+
title: "Look and density",
|
|
131
|
+
sub: "Theme and surface mode persist on this machine and apply across every view.",
|
|
132
|
+
children: h("div.lt3-stack-6",
|
|
133
|
+
h("div.lt3-field",
|
|
134
|
+
h("label.lt3-label", { style: { "display": "flex", "gap": "var(--lt3-space-2)", "align-items": "center" } }, icon("palette"), "Theme"),
|
|
135
|
+
themeSlot,
|
|
136
|
+
h("span.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } }, "System follows your OS appearance preference."),
|
|
137
|
+
),
|
|
138
|
+
h("div.lt3-field",
|
|
139
|
+
h("label.lt3-label", { style: { "display": "flex", "gap": "var(--lt3-space-2)", "align-items": "center" } }, icon("adjustments"), "Mode"),
|
|
140
|
+
h("div", modeSeg),
|
|
141
|
+
modeNote,
|
|
142
|
+
),
|
|
143
|
+
),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/* ── Workspace ──────────────────────────────────────────────────────────── */
|
|
148
|
+
function workspacePanel({ h, icon, store, c, toast, api }) {
|
|
149
|
+
const ws = store.activeWorkspace();
|
|
150
|
+
|
|
151
|
+
const orgInput = h("input.lt3-input", {
|
|
152
|
+
type: "text", placeholder: "Organization name…", "aria-label": "New organization name",
|
|
153
|
+
style: { "flex": "1 1 220px" },
|
|
154
|
+
});
|
|
155
|
+
const createBtn = h("button.lt3-btn.lt3-btn--primary", { type: "button" }, icon("plus"), "Create organization");
|
|
156
|
+
const createOrg = async () => {
|
|
157
|
+
const name = (orgInput.value || "").trim();
|
|
158
|
+
if (!name) { toast("Enter an organization name first.", "info"); return; }
|
|
159
|
+
createBtn.disabled = true;
|
|
160
|
+
const res = await api.createOrg(name);
|
|
161
|
+
createBtn.disabled = false;
|
|
162
|
+
if (res && res.ok && res.data && !res.data.detail && !res.data.error) {
|
|
163
|
+
toast(`Organization “${name}” created.`, "ok");
|
|
164
|
+
orgInput.value = "";
|
|
165
|
+
} else {
|
|
166
|
+
const detail = (res && res.data && (res.data.detail || res.data.error)) || "the runtime is unavailable";
|
|
167
|
+
toast(`Could not create organization — ${detail}.`, "warn");
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
createBtn.addEventListener("click", createOrg);
|
|
171
|
+
|
|
172
|
+
let savedLang = "en";
|
|
173
|
+
try { savedLang = localStorage.getItem("lt3-lang") || "en"; } catch {}
|
|
174
|
+
const langSelect = h("select.lt3-select", {
|
|
175
|
+
"aria-label": "Interface language", value: savedLang,
|
|
176
|
+
on: { change: (e) => {
|
|
177
|
+
try { localStorage.setItem("lt3-lang", e.target.value); } catch {}
|
|
178
|
+
toast(`Interface language set to ${e.target.selectedOptions[0].text} (saved on this device).`, "ok");
|
|
179
|
+
} },
|
|
180
|
+
},
|
|
181
|
+
h("option", { value: "en" }, "English"),
|
|
182
|
+
h("option", { value: "ko" }, "한국어"),
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
return c.panel({
|
|
186
|
+
eyebrow: "Workspace",
|
|
187
|
+
title: "Active workspace",
|
|
188
|
+
sub: "Where your indexed knowledge, agents, and policies live.",
|
|
189
|
+
children: h("div.lt3-stack-6",
|
|
190
|
+
h("dl.lt3-keyval",
|
|
191
|
+
h("dt", "Name"), h("dd", ws.name),
|
|
192
|
+
h("dt", "Type"), h("dd", h("span.lt3-row-2", icon(ws.type === "personal" ? "user" : "building"), titleCase(ws.type || "personal"))),
|
|
193
|
+
h("dt", "Your role"), h("dd", c.pill(titleCase(ws.your_role || "owner"), "info")),
|
|
194
|
+
),
|
|
195
|
+
h("hr.lt3-divider"),
|
|
196
|
+
h("div.lt3-field",
|
|
197
|
+
h("label.lt3-label", { style: { "display": "flex", "gap": "var(--lt3-space-2)", "align-items": "center" } }, icon("building-community"), "Create organization"),
|
|
198
|
+
h("div.lt3-cluster",
|
|
199
|
+
orgInput,
|
|
200
|
+
createBtn,
|
|
201
|
+
),
|
|
202
|
+
h("span.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } }, "Creates a shared organization workspace on this server."),
|
|
203
|
+
),
|
|
204
|
+
h("div.lt3-field",
|
|
205
|
+
h("label.lt3-label", { for: "lt3-set-lang", style: { "display": "flex", "gap": "var(--lt3-space-2)", "align-items": "center" } }, icon("language"), "Language"),
|
|
206
|
+
h("div", { style: { "max-width": "260px" } }, langSelect),
|
|
207
|
+
),
|
|
208
|
+
),
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* ── Integration readiness ──────────────────────────────────────────────── */
|
|
213
|
+
async function probeEndpoints({ h, icon, api, c }, host) {
|
|
214
|
+
const results = await Promise.all(PROBES.map((p) => p.call(api)));
|
|
215
|
+
const rows = PROBES.map((p, i) => {
|
|
216
|
+
const res = results[i] || {};
|
|
217
|
+
return h("div.lt3-card.lt3-card--flat",
|
|
218
|
+
h("div.lt3-row", { style: { "justify-content": "space-between", "gap": "var(--lt3-space-3)", "flex-wrap": "wrap" } },
|
|
219
|
+
h("div.lt3-row-2",
|
|
220
|
+
h("span.lt3-pill", { style: { "font-weight": "var(--lt3-weight-medium)" } }, p.method),
|
|
221
|
+
h("code.lt3-mono", p.path),
|
|
222
|
+
),
|
|
223
|
+
c.sourceBadge(res.source === "live" ? "live" : "placeholder"),
|
|
224
|
+
),
|
|
225
|
+
);
|
|
226
|
+
});
|
|
227
|
+
host.replaceChildren(h("div.lt3-stack-2", rows));
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/* ── About ──────────────────────────────────────────────────────────────── */
|
|
231
|
+
function aboutPanel({ h, icon, c }) {
|
|
232
|
+
return c.panel({
|
|
233
|
+
eyebrow: "About",
|
|
234
|
+
title: "Lattice AI",
|
|
235
|
+
sub: "Local-first AI workspace.",
|
|
236
|
+
children: h("div.lt3-stack-4",
|
|
237
|
+
h("dl.lt3-keyval",
|
|
238
|
+
h("dt", "Application"), h("dd", "Lattice AI"),
|
|
239
|
+
h("dt", "Version"), h("dd", h("span.lt3-mono", "v3.0.1")),
|
|
240
|
+
h("dt", "Edition"), h("dd", "Local-first AI workspace"),
|
|
241
|
+
),
|
|
242
|
+
h("hr.lt3-divider"),
|
|
243
|
+
h("div.lt3-cluster",
|
|
244
|
+
h("button.lt3-btn.lt3-btn--ghost", {
|
|
245
|
+
type: "button",
|
|
246
|
+
on: { click: () => { window.location.href = "/workspace"; } },
|
|
247
|
+
}, icon("layout-dashboard"), "Open classic workspace"),
|
|
248
|
+
h("span.lt3-faint", { style: { "font-size": "var(--lt3-text-xs)" } }, "The original Lattice surface remains available."),
|
|
249
|
+
),
|
|
250
|
+
),
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* ── helpers ────────────────────────────────────────────────────────────── */
|
|
255
|
+
function titleCase(s) {
|
|
256
|
+
s = String(s || "");
|
|
257
|
+
return s ? s.charAt(0).toUpperCase() + s.slice(1) : s;
|
|
258
|
+
}
|
package/static/workflows.html
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content" />
|
|
6
6
|
<title>Workflow Designer — Lattice AI</title>
|
|
7
|
-
<script src="/static/scripts/ux.js?v=
|
|
8
|
-
<link rel="stylesheet" href="/static/css/tokens.css?v=
|
|
9
|
-
<link rel="stylesheet" href="/static/platform.css?v=
|
|
10
|
-
<link rel="stylesheet" href="/static/css/responsive.css?v=
|
|
7
|
+
<script src="/static/scripts/ux.js?v=3.0.0"></script>
|
|
8
|
+
<link rel="stylesheet" href="/static/css/tokens.css?v=3.0.0" />
|
|
9
|
+
<link rel="stylesheet" href="/static/platform.css?v=3.0.0" />
|
|
10
|
+
<link rel="stylesheet" href="/static/css/responsive.css?v=3.0.0" />
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
13
13
|
<main>
|
package/static/workspace.css
CHANGED
|
@@ -730,8 +730,7 @@ textarea {
|
|
|
730
730
|
|
|
731
731
|
:root[data-lt-theme="dark"] main {
|
|
732
732
|
background:
|
|
733
|
-
|
|
734
|
-
linear-gradient(180deg, var(--bg) 0%, #10122c 100%);
|
|
733
|
+
linear-gradient(180deg, var(--bg) 0%, var(--bg-soft) 100%);
|
|
735
734
|
}
|
|
736
735
|
|
|
737
736
|
:root[data-lt-theme="dark"] .workspace-band,
|
|
@@ -781,3 +780,342 @@ textarea {
|
|
|
781
780
|
background: rgba(244, 113, 113, 0.16);
|
|
782
781
|
color: #fca5a5;
|
|
783
782
|
}
|
|
783
|
+
|
|
784
|
+
/* ── Product shell redesign (frontend-only) ─────────────────────────────── */
|
|
785
|
+
.workspace-page {
|
|
786
|
+
background: var(--bg);
|
|
787
|
+
color: var(--ink);
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
.workspace-page::before {
|
|
791
|
+
content: "";
|
|
792
|
+
position: fixed;
|
|
793
|
+
inset: 0;
|
|
794
|
+
pointer-events: none;
|
|
795
|
+
background:
|
|
796
|
+
linear-gradient(90deg, rgba(12, 92, 115, 0.035) 1px, transparent 1px),
|
|
797
|
+
linear-gradient(180deg, rgba(12, 92, 115, 0.035) 1px, transparent 1px);
|
|
798
|
+
background-size: 36px 36px;
|
|
799
|
+
mask-image: linear-gradient(180deg, rgba(0, 0, 0, 0.4), transparent 72%);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
.workspace-shell {
|
|
803
|
+
grid-template-columns: minmax(240px, 284px) minmax(0, 1fr);
|
|
804
|
+
background: var(--app-bg);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.workspace-rail {
|
|
808
|
+
background: var(--rail-bg);
|
|
809
|
+
color: var(--rail-ink);
|
|
810
|
+
border-right: 1px solid var(--rail-line);
|
|
811
|
+
box-shadow: none;
|
|
812
|
+
overflow-y: auto;
|
|
813
|
+
scrollbar-gutter: stable;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
.rail-brand {
|
|
817
|
+
min-height: 48px;
|
|
818
|
+
border-radius: 8px;
|
|
819
|
+
background: rgba(255, 255, 255, 0.06);
|
|
820
|
+
border: 1px solid var(--rail-line);
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
.rail-brand span {
|
|
824
|
+
line-height: 1;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
.rail-brand small {
|
|
828
|
+
margin-left: auto;
|
|
829
|
+
color: var(--rail-ink-soft);
|
|
830
|
+
font-size: 10px;
|
|
831
|
+
font-weight: 800;
|
|
832
|
+
text-transform: uppercase;
|
|
833
|
+
letter-spacing: 0.08em;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
.rail-section-label {
|
|
837
|
+
color: var(--rail-ink-soft);
|
|
838
|
+
display: block;
|
|
839
|
+
font-size: 10px;
|
|
840
|
+
font-weight: 900;
|
|
841
|
+
letter-spacing: 0.1em;
|
|
842
|
+
padding: 8px 12px 4px;
|
|
843
|
+
text-transform: uppercase;
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
.workspace-rail nav a,
|
|
847
|
+
.rail-links a {
|
|
848
|
+
border: 1px solid transparent;
|
|
849
|
+
min-height: 42px;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
.workspace-rail nav a.active,
|
|
853
|
+
.workspace-rail nav a:hover,
|
|
854
|
+
.rail-links a:hover {
|
|
855
|
+
background: var(--rail-hover);
|
|
856
|
+
border-color: var(--rail-line);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
.workspace-shell:not([data-admin-available="true"]) .admin-navigation {
|
|
860
|
+
display: none;
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
main {
|
|
864
|
+
gap: 16px;
|
|
865
|
+
padding: 20px clamp(16px, 2vw, 28px) 36px;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
.workspace-topbar {
|
|
869
|
+
position: sticky;
|
|
870
|
+
top: 0;
|
|
871
|
+
z-index: 4;
|
|
872
|
+
margin: -20px calc(clamp(16px, 2vw, 28px) * -1) 0;
|
|
873
|
+
padding: 16px clamp(16px, 2vw, 28px);
|
|
874
|
+
background: color-mix(in srgb, var(--surface-elevated) 94%, transparent);
|
|
875
|
+
border-bottom: 1px solid var(--line);
|
|
876
|
+
backdrop-filter: blur(18px);
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
.topbar-subtitle {
|
|
880
|
+
color: var(--muted);
|
|
881
|
+
font-size: 13px;
|
|
882
|
+
line-height: 1.45;
|
|
883
|
+
margin: 6px 0 0;
|
|
884
|
+
max-width: 760px;
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
.top-actions {
|
|
888
|
+
flex-wrap: wrap;
|
|
889
|
+
justify-content: flex-end;
|
|
890
|
+
min-width: min(100%, 560px);
|
|
891
|
+
}
|
|
892
|
+
|
|
893
|
+
.global-mode-switcher {
|
|
894
|
+
display: inline-grid;
|
|
895
|
+
grid-auto-flow: column;
|
|
896
|
+
grid-auto-columns: minmax(76px, 1fr);
|
|
897
|
+
gap: 2px;
|
|
898
|
+
padding: 3px;
|
|
899
|
+
border: 1px solid var(--line);
|
|
900
|
+
border-radius: 8px;
|
|
901
|
+
background: var(--surface-2);
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
.global-mode-switcher button {
|
|
905
|
+
min-height: 34px;
|
|
906
|
+
border-radius: 6px;
|
|
907
|
+
background: transparent;
|
|
908
|
+
color: var(--muted);
|
|
909
|
+
font-size: 12px;
|
|
910
|
+
font-weight: 800;
|
|
911
|
+
padding: 0 10px;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
.global-mode-switcher button.active {
|
|
915
|
+
background: var(--accent);
|
|
916
|
+
color: #fff;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.global-mode-switcher button:disabled {
|
|
920
|
+
cursor: not-allowed;
|
|
921
|
+
opacity: 0.45;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.chrome-select {
|
|
925
|
+
width: auto;
|
|
926
|
+
min-width: 108px;
|
|
927
|
+
min-height: 38px;
|
|
928
|
+
color: var(--ink);
|
|
929
|
+
background: var(--surface);
|
|
930
|
+
}
|
|
931
|
+
|
|
932
|
+
.workspace-switcher {
|
|
933
|
+
background: var(--surface);
|
|
934
|
+
border-color: var(--line);
|
|
935
|
+
color: var(--ink);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
.workspace-switcher select {
|
|
939
|
+
color: var(--ink);
|
|
940
|
+
max-width: min(220px, 36vw);
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
.workspace-role-pill {
|
|
944
|
+
color: var(--muted);
|
|
945
|
+
opacity: 1;
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
.metric-grid {
|
|
949
|
+
grid-template-columns: repeat(4, minmax(150px, 1fr));
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
.metric-card,
|
|
953
|
+
.workspace-panel,
|
|
954
|
+
.workspace-band {
|
|
955
|
+
background: var(--surface);
|
|
956
|
+
border-color: var(--line);
|
|
957
|
+
border-radius: 8px;
|
|
958
|
+
box-shadow: none;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
.metric-card {
|
|
962
|
+
border-left: 3px solid var(--accent);
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
.metric-card i,
|
|
966
|
+
.health-card i {
|
|
967
|
+
color: var(--accent-2);
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
.workspace-band,
|
|
971
|
+
.workspace-panel {
|
|
972
|
+
padding: 18px;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
.section-head {
|
|
976
|
+
border-bottom: 1px solid var(--line);
|
|
977
|
+
margin: -2px 0 14px;
|
|
978
|
+
padding-bottom: 12px;
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
.eyebrow {
|
|
982
|
+
color: var(--accent);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
.primary-action {
|
|
986
|
+
background: var(--accent);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
.secondary-action,
|
|
990
|
+
.tab.active,
|
|
991
|
+
.switch-chip.active {
|
|
992
|
+
background: var(--accent-soft);
|
|
993
|
+
color: var(--accent);
|
|
994
|
+
border-color: color-mix(in srgb, var(--accent) 34%, var(--line));
|
|
995
|
+
}
|
|
996
|
+
|
|
997
|
+
.icon-action,
|
|
998
|
+
.small-action,
|
|
999
|
+
.step-chip,
|
|
1000
|
+
.tab,
|
|
1001
|
+
.switch-chip,
|
|
1002
|
+
.list-item,
|
|
1003
|
+
.health-card,
|
|
1004
|
+
.capability-card {
|
|
1005
|
+
background: var(--surface-2);
|
|
1006
|
+
border-color: var(--line);
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
.list-item,
|
|
1010
|
+
.health-card,
|
|
1011
|
+
.capability-card {
|
|
1012
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
:root[data-lt-theme="dark"] .list-item,
|
|
1016
|
+
:root[data-lt-theme="dark"] .health-card,
|
|
1017
|
+
:root[data-lt-theme="dark"] .capability-card {
|
|
1018
|
+
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
.status-pill {
|
|
1022
|
+
background: var(--accent-soft);
|
|
1023
|
+
color: var(--accent);
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
.status-complete {
|
|
1027
|
+
background: color-mix(in srgb, var(--success) 16%, transparent);
|
|
1028
|
+
color: var(--success);
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
.status-running {
|
|
1032
|
+
background: color-mix(in srgb, var(--warning) 18%, transparent);
|
|
1033
|
+
color: var(--warning);
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
.status-failed {
|
|
1037
|
+
background: color-mix(in srgb, var(--danger) 16%, transparent);
|
|
1038
|
+
color: var(--danger);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.importance-bar {
|
|
1042
|
+
background: var(--surface-3);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.importance-bar span,
|
|
1046
|
+
.skill-progress-track span {
|
|
1047
|
+
background: linear-gradient(90deg, var(--accent), var(--accent-2));
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
.code-box,
|
|
1051
|
+
.state-box,
|
|
1052
|
+
.toast {
|
|
1053
|
+
background: #101820;
|
|
1054
|
+
border-color: rgba(148, 163, 184, 0.22);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
@media (min-width: 1600px) {
|
|
1058
|
+
main {
|
|
1059
|
+
max-width: 1680px;
|
|
1060
|
+
width: 100%;
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
.metric-grid,
|
|
1064
|
+
.health-grid {
|
|
1065
|
+
grid-template-columns: repeat(4, minmax(180px, 1fr));
|
|
1066
|
+
}
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
@media (max-width: 1180px) {
|
|
1070
|
+
.workspace-topbar {
|
|
1071
|
+
align-items: flex-start;
|
|
1072
|
+
flex-direction: column;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
.top-actions {
|
|
1076
|
+
justify-content: flex-start;
|
|
1077
|
+
width: 100%;
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
@media (max-width: 860px) {
|
|
1082
|
+
.workspace-shell {
|
|
1083
|
+
grid-template-columns: 1fr;
|
|
1084
|
+
}
|
|
1085
|
+
|
|
1086
|
+
.workspace-rail {
|
|
1087
|
+
position: relative;
|
|
1088
|
+
height: auto;
|
|
1089
|
+
max-height: none;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
.workspace-rail nav,
|
|
1093
|
+
.rail-links {
|
|
1094
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
.rail-section-label,
|
|
1098
|
+
.rail-brand {
|
|
1099
|
+
grid-column: 1 / -1;
|
|
1100
|
+
}
|
|
1101
|
+
|
|
1102
|
+
.workspace-topbar {
|
|
1103
|
+
position: static;
|
|
1104
|
+
margin-top: 0;
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
.global-mode-switcher {
|
|
1108
|
+
grid-auto-columns: minmax(88px, 1fr);
|
|
1109
|
+
width: 100%;
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
.workspace-switcher,
|
|
1113
|
+
.chrome-select,
|
|
1114
|
+
.primary-action {
|
|
1115
|
+
width: 100%;
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
.workspace-switcher select {
|
|
1119
|
+
max-width: none;
|
|
1120
|
+
}
|
|
1121
|
+
}
|