ltcai 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -156,7 +156,7 @@ See [docs/architecture.md](docs/architecture.md) for request and data-flow detai
156
156
  </table>
157
157
 
158
158
  > Every image in this section is a **real screenshot** of the running app
159
- > (Lattice AI v1.6.0), captured with a headless browser.
159
+ > (Lattice AI v1.7.0), captured with a headless browser.
160
160
 
161
161
  ---
162
162
 
@@ -179,7 +179,9 @@ DeepSeek, and more), with estimated RAM and a clear next step.
179
179
  A **Current Workspace** card shows exactly where you are; switch instantly
180
180
  between a **Personal** workspace and shared **Organization** workspaces. Org data
181
181
  is scoped by `workspace_id`, and `owner / admin / member / viewer` roles map to a
182
- transparent permission matrix with member management.
182
+ transparent permission matrix with member management. A Workspace Health panel
183
+ summarizes indexed files, graph size, installed skills, memories, agent runs,
184
+ current model, last sync time, and status at a glance.
183
185
 
184
186
  <div align="center">
185
187
  <img src="docs/images/workspace.png" alt="Current Workspace summary card with scoped counts" width="100%"/>
@@ -196,6 +198,10 @@ relationships, related entities, and a path back to you.
196
198
  <img src="docs/images/graph.png" alt="Knowledge graph entity explorer with relationship detail" width="100%"/>
197
199
  </div>
198
200
 
201
+ The Graph Canvas also supports node expand/collapse, focused subgraphs,
202
+ relationship highlighting, shortest-path visualization, and direct navigation
203
+ back into source conversations or files.
204
+
199
205
  ### Skills & editions
200
206
 
201
207
  Browse and install skills from an in-product marketplace; an honest editions
@@ -347,24 +353,29 @@ Supported routes include OpenAI-compatible APIs, OpenRouter, Groq, Together, xAI
347
353
 
348
354
  ## Current release
349
355
 
350
- **1.6.0 — Product Experience Deepening.** A UX release: the screens in this README
351
- are now real captured UI.
352
-
353
- - **Knowledge Graph explorer** — entity cards, a relationship/related-entities/
354
- shortest-path detail panel, recent activity, and a memory feed (additive UI on
355
- existing endpoints)
356
- - **Workspace UX** — a "Current Workspace" summary card with quick-switch chips
357
- - **Model Recommendation 2.0** machine summary, a best-pick callout with
358
- estimated RAM and next step, per-family status, and a cloud caution
359
- - **Skill Marketplace** Recommended / Popular / Installed / Updates tabs
360
- - **Enterprise capability panel** — an honest 12-capability matrix (Community: all
361
- disabled, nothing gated)
362
- - **Real screenshots** — `docs/images/*` refreshed from the running app; API,
363
- schemas, `server:app`, CLI, MCP, and the Knowledge Graph contract unchanged
356
+ **1.7.0 — Graph & Collaboration Release.** An additive UI and validation release
357
+ for graph exploration, workspace health, Enterprise admin visibility, skills,
358
+ screenshots, and visual smoke coverage.
359
+
360
+ - **Graph Canvas** expand/collapse, subgraph focus, relationship/path
361
+ highlighting, shortest-path visualization, and click-through node navigation.
362
+ - **Workspace Health** — indexed files, graph nodes/relationships, installed
363
+ skills, memories, agent runs, current model, last sync, and workspace status.
364
+ - **Enterprise Admin UI** — admin policies, audit export, SIEM export preview,
365
+ organization settings, and capability status surfaced in `/admin`.
366
+ - **Skill Marketplace completion** — install progress, validation status,
367
+ recommended/popular skills, updates, version, and source metadata.
368
+ - **Screenshot automation + visual smoke tests** — `scripts/capture/*` and a
369
+ scheduled Playwright workflow cover Workspace, Graph, Skills, Organization,
370
+ and Enterprise screens.
371
+ - **Compatibility preserved** — API schemas, `server:app`,
372
+ `latticeai.server_app.app`, CLI, MCP, model, workspace, chat, KG, and VS Code
373
+ extension surfaces remain backward compatible.
364
374
 
365
375
  | Version | Theme |
366
376
  |---|---|
367
- | **1.6.0** | Product Experience Deepening (UX + real screenshots) |
377
+ | **1.7.0** | Graph & Collaboration Release |
378
+ | 1.6.0 | Product Experience Deepening (UX + real screenshots) |
368
379
  | 1.5.0 | Unified Product Release (CI/VSIX recovery, model recommendation, Enterprise PoC) |
369
380
  | 1.4.0 | Server App final decomposition |
370
381
  | 1.1.0–1.3.0 | Organization workspaces, modularization, route safety net |
package/docs/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## [1.7.0] - 2026-06-01
4
+
5
+ > Graph & Collaboration Release — Graph Canvas interactions, Enterprise Admin
6
+ > UI, Skill Marketplace completion, Workspace Health, screenshot automation, and
7
+ > Playwright visual smoke coverage. Backward compatible: API paths/schemas,
8
+ > `server:app`, `latticeai.server_app.app`, CLI, Workspace/Chat/Model/MCP/KG
9
+ > APIs, and VS Code extension commands remain stable.
10
+
11
+ ### Added
12
+
13
+ - **Graph Canvas** — node expand/collapse, focused subgraphs, relationship
14
+ highlighting, shortest-path visualization, URL/node click-through navigation,
15
+ and source/conversation actions. Uses existing graph/relationship APIs; no
16
+ schema change or destructive migration.
17
+ - **Enterprise Admin UI** — `/admin#enterprise` now surfaces Admin Policies,
18
+ Audit Export, SIEM Export preview, Organization Settings, and Enterprise
19
+ Capability Status. Community remains fully functional and ungated.
20
+ - **Skill Marketplace completion** — install progress (Download → Validate →
21
+ Ready), validation status, recommended/popular/update surfaces, version
22
+ metadata, and source metadata.
23
+ - **Workspace Health Dashboard** — indexed files, graph nodes, graph
24
+ relationships, installed skills, memory entries, agent runs, current model,
25
+ last sync time, and workspace status.
26
+ - **Screenshot automation** — `scripts/capture/` contains reproducible
27
+ Playwright capture scripts for workspace, graph, skills, enterprise, and
28
+ onboarding screenshots.
29
+ - **Visual smoke tests** — `tests/visual/*` plus `.github/workflows/visual.yml`
30
+ run Workspace, Graph, Skills, Organization, and Enterprise screen checks on
31
+ PR/push and nightly schedule with failure artifacts.
32
+
33
+ ### Changed
34
+
35
+ - Python package, npm package, VS Code extension, Workspace OS, FastAPI app, and
36
+ `/health` version metadata aligned at `1.7.0`.
37
+ - CI package validation is version-scoped instead of a broad `dist/*` check.
38
+
39
+ ### Validation
40
+
41
+ - Unit, integration, startup/import, route compatibility, MCP, model endpoint,
42
+ visual smoke, VSIX build, and release artifact validation are the release
43
+ target checks. Package-store publish remains manual only.
44
+
3
45
  ## [1.6.0] - 2026-06-01
4
46
 
5
47
  > Product Experience Deepening — user-facing UX (Knowledge Graph explorer,
@@ -50,6 +50,10 @@ the boundary stays predictable for contributors and users.
50
50
 
51
51
  - `GET /workspace/editions` → edition + per-capability matrix.
52
52
  - `GET /workspace/os` → `edition` block in the Workspace OS summary.
53
+ - `/admin#enterprise` → Admin policy, audit export, SIEM preview,
54
+ organization settings, and capability status UI.
55
+ - `GET /admin/enterprise` and `GET /admin/enterprise/siem-export` → descriptive
56
+ Community-safe Enterprise surfaces for integrations.
53
57
  - `latticeai.core.enterprise.detect_edition()` for in-process checks.
54
58
 
55
59
  In the Community build all of the above report `community` with every Enterprise
@@ -33,8 +33,14 @@ Community code consults the seam at extension points via
33
33
  `is_capability_enabled(EnterpriseCapability.X)`. In the Community build this is
34
34
  always `False`, so the Community code path is taken and nothing is gated off.
35
35
 
36
- The live edition + capability matrix is exposed at `GET /workspace/editions`
37
- and surfaced in the Workspace OS summary (`GET /workspace/os` → `edition`).
36
+ The live edition + capability matrix is exposed at `GET /workspace/editions`,
37
+ surfaced in the Workspace OS summary (`GET /workspace/os` → `edition`), and
38
+ shown in the Enterprise Admin UI at `/admin#enterprise`.
39
+
40
+ Community also exposes descriptive admin surfaces at `GET /admin/enterprise`
41
+ and `GET /admin/enterprise/siem-export`: they show policy/export/envelope
42
+ shapes while reporting Enterprise-only capabilities disabled and never
43
+ streaming external events.
38
44
 
39
45
  ## Enterprise capability roadmap
40
46
 
@@ -1,3 +1,3 @@
1
1
  """Lattice AI - modular server package."""
2
2
 
3
- __version__ = "1.6.0"
3
+ __version__ = "1.7.0"
@@ -18,7 +18,7 @@ from pathlib import Path
18
18
  from typing import Any, Callable, Dict, Iterable, List, Optional
19
19
 
20
20
 
21
- WORKSPACE_OS_VERSION = "1.6.0"
21
+ WORKSPACE_OS_VERSION = "1.7.0"
22
22
 
23
23
  # Workspace types separate single-user Personal workspaces from shared
24
24
  # Organization workspaces. Both keep the same local-first JSON store; the type
@@ -353,6 +353,7 @@ class WorkspaceOSStore:
353
353
  "workspaces": state.get("workspaces"),
354
354
  "navigation": list(WORKSPACE_AREAS),
355
355
  "feature_flags": state.get("feature_flags"),
356
+ "updated_at": state.get("updated_at"),
356
357
  "counts": {
357
358
  "snapshots": len(_listify(state.get("snapshots"))),
358
359
  "traces": len(_listify(state.get("traces"))),
@@ -1424,6 +1425,9 @@ class WorkspaceOSStore:
1424
1425
  "description": desc,
1425
1426
  "version": version,
1426
1427
  "installed": True,
1428
+ "install_status": entry.get("install_status") or "ready",
1429
+ "validation_status": "ready" if skill_md.exists() else "missing_manifest",
1430
+ "source": entry.get("source") or "local",
1427
1431
  "path": str(skill_dir),
1428
1432
  "updated_at": entry.get("updated_at") or _now(),
1429
1433
  })
@@ -1438,6 +1442,9 @@ class WorkspaceOSStore:
1438
1442
  **item,
1439
1443
  "enabled": bool(state_entry.get("enabled", True)),
1440
1444
  "installed": bool(state_entry.get("installed")),
1445
+ "install_status": state_entry.get("install_status") or ("ready" if state_entry.get("installed") else "available"),
1446
+ "validation_status": state_entry.get("validation_status") or item.get("validation_status") or ("ready" if state_entry.get("installed") else "not_installed"),
1447
+ "source": state_entry.get("source") or item.get("source") or item.get("plugin") or "marketplace",
1441
1448
  "version": state_entry.get("version") or item.get("version") or "remote",
1442
1449
  })
1443
1450
  self.save_state(state)
@@ -1465,6 +1472,9 @@ class WorkspaceOSStore:
1465
1472
  "installed": True,
1466
1473
  "enabled": entry.get("enabled", True),
1467
1474
  "version": version,
1475
+ "install_status": "ready",
1476
+ "validation_status": "ready",
1477
+ "source": (metadata or {}).get("source") or entry.get("source") or "marketplace",
1468
1478
  "metadata": metadata or entry.get("metadata") or {},
1469
1479
  "updated_at": _now(),
1470
1480
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "1.6.0",
3
+ "version": "1.7.0",
4
4
  "description": "Lattice AI Workspace OS for local-first graph, memory, agent, workflow, and skill operations",
5
5
  "homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
6
6
  "repository": {
@@ -23,6 +23,12 @@
23
23
  "test": "python3 -m pytest tests/ -v",
24
24
  "test:unit": "python3 -m pytest tests/unit/ -v",
25
25
  "test:integration": "python3 -m pytest tests/integration/ -v",
26
+ "test:visual": "playwright test",
27
+ "capture:workspace": "node scripts/capture/capture_workspace.js",
28
+ "capture:graph": "node scripts/capture/capture_graph.js",
29
+ "capture:skills": "node scripts/capture/capture_skills.js",
30
+ "capture:enterprise": "node scripts/capture/capture_enterprise.js",
31
+ "capture:onboarding": "node scripts/capture/capture_onboarding.js",
26
32
  "publish:npm": "npm publish --access public",
27
33
  "publish:pypi": "python3 -m twine upload --skip-existing dist/ltcai-$npm_package_version.tar.gz dist/ltcai-$npm_package_version-py3-none-any.whl"
28
34
  },
@@ -75,5 +81,8 @@
75
81
  ],
76
82
  "publishConfig": {
77
83
  "access": "public"
84
+ },
85
+ "devDependencies": {
86
+ "@playwright/test": "^1.60.0"
78
87
  }
79
88
  }
package/static/admin.html CHANGED
@@ -26,6 +26,7 @@
26
26
  <a href="#users" data-admin-nav="users"><i class="ti ti-users"></i> <span data-i18n="nav_users">사용자 관리</span></a>
27
27
  <a href="#permissions" data-admin-nav="permissions"><i class="ti ti-key"></i> <span data-i18n="nav_permissions">권한 관리</span></a>
28
28
  <a href="#sso" data-admin-nav="sso"><i class="ti ti-lock-access"></i> <span data-i18n="nav_sso">SSO 관리</span></a>
29
+ <a href="#enterprise" data-admin-nav="enterprise"><i class="ti ti-building-skyscraper"></i> <span data-i18n="nav_enterprise">Enterprise</span></a>
29
30
  <a href="#security" data-admin-nav="security"><i class="ti ti-shield-check"></i> <span data-i18n="nav_security">보안 모니터링</span></a>
30
31
  <a href="#audit" data-admin-nav="audit"><i class="ti ti-report-search"></i> <span data-i18n="nav_audit">감사 로그</span></a>
31
32
  <a href="/workspace"><i class="ti ti-layout-dashboard"></i> <span>Workspace OS</span></a>
@@ -256,6 +257,67 @@
256
257
  </section>
257
258
  </section>
258
259
 
260
+ <section class="admin-view" id="admin-view-enterprise" data-admin-view="enterprise">
261
+ <section class="panel">
262
+ <div class="panel-header">
263
+ <div>
264
+ <h3 data-i18n="enterprise_title">Enterprise Admin</h3>
265
+ <p data-i18n="enterprise_desc">Admin policies, audit export, SIEM export, organization settings, and capability status.</p>
266
+ </div>
267
+ <div class="tag-row" id="enterprise-status-tags"></div>
268
+ </div>
269
+ <div class="panel-body">
270
+ <div class="enterprise-grid" id="enterprise-capability-status"></div>
271
+ </div>
272
+ </section>
273
+
274
+ <section class="panel-grid">
275
+ <article class="panel">
276
+ <div class="panel-header">
277
+ <div>
278
+ <h3 data-i18n="enterprise_policies">Admin Policies</h3>
279
+ <p data-i18n="enterprise_policies_desc">Effective Community policy and Enterprise policy-pack status.</p>
280
+ </div>
281
+ </div>
282
+ <div class="panel-body" id="enterprise-admin-policies"></div>
283
+ </article>
284
+ <article class="panel">
285
+ <div class="panel-header">
286
+ <div>
287
+ <h3 data-i18n="enterprise_org">Organization Settings</h3>
288
+ <p data-i18n="enterprise_org_desc">Workspace governance and organization capability status.</p>
289
+ </div>
290
+ </div>
291
+ <div class="panel-body" id="enterprise-org-settings"></div>
292
+ </article>
293
+ </section>
294
+
295
+ <section class="panel-grid">
296
+ <article class="panel">
297
+ <div class="panel-header">
298
+ <div>
299
+ <h3 data-i18n="enterprise_audit_export">Audit Export</h3>
300
+ <p data-i18n="enterprise_audit_export_desc">Local export remains available in Community; retention is an Enterprise extension point.</p>
301
+ </div>
302
+ </div>
303
+ <div class="panel-body" id="enterprise-audit-export"></div>
304
+ </article>
305
+ <article class="panel">
306
+ <div class="panel-header">
307
+ <div>
308
+ <h3 data-i18n="enterprise_siem">SIEM Export</h3>
309
+ <p data-i18n="enterprise_siem_desc">Preview the SIEM envelope without streaming external events in Community.</p>
310
+ </div>
311
+ <button class="btn" id="refresh-siem-btn" type="button"><i class="ti ti-refresh"></i> <span>SIEM</span></button>
312
+ </div>
313
+ <div class="panel-body">
314
+ <div id="enterprise-siem-export"></div>
315
+ <pre class="enterprise-json" id="enterprise-siem-preview"></pre>
316
+ </div>
317
+ </article>
318
+ </section>
319
+ </section>
320
+
259
321
  <section class="admin-view" id="admin-view-security" data-admin-view="security">
260
322
  <!-- Security & Audit Command Center (피드백 #5) -->
261
323
  <section class="panel" id="security-overview-panel">
package/static/graph.html CHANGED
@@ -51,7 +51,12 @@
51
51
  </section>
52
52
 
53
53
  <div class="toolbar">
54
- <button class="tb-btn" id="refresh-btn">↺ Refresh</button>
54
+ <button class="tb-btn" id="refresh-btn"><i class="ti ti-refresh"></i> Refresh</button>
55
+ <button class="tb-btn" id="fit-btn" title="Fit graph"><i class="ti ti-arrows-maximize"></i> Fit</button>
56
+ <button class="tb-btn" id="expand-btn" title="Expand selected node"><i class="ti ti-circle-plus"></i> Expand</button>
57
+ <button class="tb-btn" id="collapse-btn" title="Collapse selected neighbors"><i class="ti ti-circle-minus"></i> Collapse</button>
58
+ <button class="tb-btn" id="focus-btn" title="Focus selected subgraph"><i class="ti ti-focus-2"></i> Focus</button>
59
+ <button class="tb-btn" id="path-btn" title="Shortest path from saved start"><i class="ti ti-route"></i> Path</button>
55
60
  <div class="lang-picker" id="graph-lang-picker">
56
61
  <button class="tb-btn" id="graph-lang-btn" type="button" onclick="toggleLangMenu('graph-lang-picker')">Language</button>
57
62
  <div class="lang-picker-menu" id="graph-lang-picker-menu">
@@ -60,6 +65,7 @@
60
65
  </div>
61
66
  </div>
62
67
  </div>
68
+ <div id="graph-focus-chip" class="focus-chip" hidden></div>
63
69
  </main>
64
70
 
65
71
  <aside>
@@ -1555,6 +1555,91 @@
1555
1555
  line-height: 1.5;
1556
1556
  }
1557
1557
 
1558
+ .lattice-ref-admin .enterprise-grid {
1559
+ display: grid;
1560
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
1561
+ gap: 10px;
1562
+ }
1563
+
1564
+ .lattice-ref-admin .enterprise-cap-card {
1565
+ display: flex;
1566
+ align-items: center;
1567
+ gap: 10px;
1568
+ min-width: 0;
1569
+ border: 1px solid rgba(111,66,232,0.12);
1570
+ border-radius: 8px;
1571
+ background: rgba(255,255,255,0.70);
1572
+ padding: 11px 12px;
1573
+ }
1574
+
1575
+ .lattice-ref-admin .enterprise-cap-card i {
1576
+ color: #7a74a0;
1577
+ font-size: 18px;
1578
+ }
1579
+
1580
+ .lattice-ref-admin .enterprise-cap-card.on i {
1581
+ color: #0d8f72;
1582
+ }
1583
+
1584
+ .lattice-ref-admin .enterprise-cap-card span {
1585
+ flex: 1;
1586
+ min-width: 0;
1587
+ color: #14162c;
1588
+ font-size: 13px;
1589
+ font-weight: 800;
1590
+ overflow: hidden;
1591
+ text-overflow: ellipsis;
1592
+ white-space: nowrap;
1593
+ text-transform: capitalize;
1594
+ }
1595
+
1596
+ .lattice-ref-admin .enterprise-cap-card strong {
1597
+ color: #4a4668;
1598
+ font-size: 11px;
1599
+ }
1600
+
1601
+ .lattice-ref-admin .enterprise-kv {
1602
+ display: grid;
1603
+ gap: 8px;
1604
+ }
1605
+
1606
+ .lattice-ref-admin .enterprise-kv div {
1607
+ display: grid;
1608
+ grid-template-columns: 150px minmax(0, 1fr);
1609
+ gap: 10px;
1610
+ align-items: start;
1611
+ border: 1px solid rgba(111,66,232,0.10);
1612
+ border-radius: 8px;
1613
+ background: rgba(255,255,255,0.62);
1614
+ padding: 9px 10px;
1615
+ }
1616
+
1617
+ .lattice-ref-admin .enterprise-kv span {
1618
+ color: #4a4668;
1619
+ font-size: 12px;
1620
+ font-weight: 800;
1621
+ }
1622
+
1623
+ .lattice-ref-admin .enterprise-kv strong {
1624
+ color: #14162c;
1625
+ font-size: 12px;
1626
+ line-height: 1.45;
1627
+ overflow-wrap: anywhere;
1628
+ }
1629
+
1630
+ .lattice-ref-admin .enterprise-json {
1631
+ max-height: 280px;
1632
+ overflow: auto;
1633
+ margin: 12px 0 0;
1634
+ border: 1px solid rgba(111,66,232,0.10);
1635
+ border-radius: 8px;
1636
+ background: rgba(20,22,44,0.05);
1637
+ color: #14162c;
1638
+ padding: 12px;
1639
+ font-size: 12px;
1640
+ white-space: pre-wrap;
1641
+ }
1642
+
1558
1643
  @media (max-width: 980px) {
1559
1644
  .lattice-ref-chat .reference-card-grid,
1560
1645
  .reference-lists {
@@ -2784,9 +2869,11 @@ body.lattice-ref-graph {
2784
2869
  top: 16px;
2785
2870
  right: 16px;
2786
2871
  display: flex;
2872
+ flex-wrap: wrap;
2787
2873
  gap: 8px;
2788
2874
  padding: 8px;
2789
2875
  border-radius: 10px;
2876
+ max-width: min(760px, calc(100% - 32px));
2790
2877
  }
2791
2878
 
2792
2879
  .tb-btn {
@@ -3301,6 +3388,103 @@ body.lattice-ref-graph {
3301
3388
 
3302
3389
  .jump-btn:hover { filter: brightness(1.04); }
3303
3390
 
3391
+ .jump-btn.secondary {
3392
+ border: 1px solid rgba(111,66,232,0.20);
3393
+ color: var(--text);
3394
+ background: rgba(255,255,255,0.82);
3395
+ box-shadow: none;
3396
+ cursor: pointer;
3397
+ font: inherit;
3398
+ font-size: 12px;
3399
+ font-weight: 700;
3400
+ }
3401
+
3402
+ .detail-actions {
3403
+ display: flex;
3404
+ flex-wrap: wrap;
3405
+ gap: 8px;
3406
+ margin-bottom: 14px;
3407
+ }
3408
+
3409
+ .related-node-list {
3410
+ display: grid;
3411
+ gap: 7px;
3412
+ margin-bottom: 14px;
3413
+ }
3414
+
3415
+ .related-node-btn {
3416
+ width: 100%;
3417
+ min-width: 0;
3418
+ display: grid;
3419
+ grid-template-columns: 18px minmax(0, 1fr) auto;
3420
+ align-items: center;
3421
+ gap: 8px;
3422
+ border: 1px solid rgba(111,66,232,0.13);
3423
+ border-radius: 8px;
3424
+ background: rgba(255,255,255,0.76);
3425
+ color: var(--text);
3426
+ padding: 8px 10px;
3427
+ text-align: left;
3428
+ cursor: pointer;
3429
+ }
3430
+
3431
+ .related-node-btn:hover {
3432
+ border-color: rgba(111,66,232,0.34);
3433
+ background: rgba(111,66,232,0.07);
3434
+ }
3435
+
3436
+ .related-node-btn strong {
3437
+ min-width: 0;
3438
+ overflow: hidden;
3439
+ text-overflow: ellipsis;
3440
+ white-space: nowrap;
3441
+ font-size: 12px;
3442
+ }
3443
+
3444
+ .related-node-btn em {
3445
+ color: var(--faint);
3446
+ font-size: 11px;
3447
+ font-style: normal;
3448
+ white-space: nowrap;
3449
+ }
3450
+
3451
+ .focus-chip {
3452
+ position: absolute;
3453
+ z-index: 21;
3454
+ left: 16px;
3455
+ bottom: 16px;
3456
+ max-width: min(560px, calc(100% - 32px));
3457
+ display: flex;
3458
+ flex-wrap: wrap;
3459
+ gap: 8px;
3460
+ padding: 8px;
3461
+ border: 1px solid var(--line);
3462
+ border-radius: 10px;
3463
+ background: rgba(255,255,255,0.90);
3464
+ box-shadow: var(--shadow);
3465
+ backdrop-filter: blur(18px);
3466
+ }
3467
+
3468
+ .focus-chip span {
3469
+ display: inline-flex;
3470
+ align-items: center;
3471
+ gap: 6px;
3472
+ border-radius: 999px;
3473
+ background: rgba(111,66,232,0.08);
3474
+ color: var(--text);
3475
+ padding: 5px 10px;
3476
+ font-size: 11px;
3477
+ font-weight: 800;
3478
+ max-width: 260px;
3479
+ overflow: hidden;
3480
+ text-overflow: ellipsis;
3481
+ white-space: nowrap;
3482
+ }
3483
+
3484
+ .search-shell.search-open .search-results {
3485
+ display: block;
3486
+ }
3487
+
3304
3488
  .empty-hint {
3305
3489
  margin: 0;
3306
3490
  color: var(--muted);