ltcai 3.0.1 → 3.1.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.
Files changed (96) hide show
  1. package/README.md +27 -20
  2. package/docs/CHANGELOG.md +37 -0
  3. package/docs/V3_FRONTEND.md +20 -17
  4. package/latticeai/__init__.py +1 -1
  5. package/latticeai/api/auth.py +4 -1
  6. package/latticeai/api/search.py +4 -0
  7. package/latticeai/core/config.py +2 -0
  8. package/latticeai/core/embedding_providers.py +123 -0
  9. package/latticeai/core/workspace_os.py +1 -1
  10. package/latticeai/server_app.py +22 -6
  11. package/package.json +9 -4
  12. package/scripts/build_v3_assets.mjs +164 -0
  13. package/scripts/capture/README.md +28 -0
  14. package/scripts/capture/capture_enterprise.js +8 -0
  15. package/scripts/capture/capture_graph.js +8 -0
  16. package/scripts/capture/capture_onboarding.js +8 -0
  17. package/scripts/capture/capture_page.js +43 -0
  18. package/scripts/capture/capture_release_media.js +125 -0
  19. package/scripts/capture/capture_skills.js +8 -0
  20. package/scripts/capture/capture_workspace.js +8 -0
  21. package/scripts/generate_diagrams.py +513 -0
  22. package/scripts/lint_v3.mjs +33 -0
  23. package/scripts/release-0.3.1.sh +105 -0
  24. package/scripts/take_screenshots.js +69 -0
  25. package/scripts/validate_release_artifacts.py +167 -0
  26. package/static/account.html +9 -9
  27. package/static/activity.html +4 -4
  28. package/static/admin.html +8 -8
  29. package/static/agents.html +4 -4
  30. package/static/chat.html +9 -9
  31. package/static/css/tokens.5a595671.css +260 -0
  32. package/static/css/tokens.css +1 -1
  33. package/static/graph.html +9 -9
  34. package/static/plugins.html +4 -4
  35. package/static/sw.js +3 -1
  36. package/static/v3/asset-manifest.json +47 -0
  37. package/static/v3/css/lattice.base.e4cdd05d.css +128 -0
  38. package/static/v3/css/lattice.components.011e988b.css +447 -0
  39. package/static/v3/css/lattice.components.css +2 -2
  40. package/static/v3/css/lattice.shell.4920f42d.css +407 -0
  41. package/static/v3/css/lattice.tokens.c597ff81.css +132 -0
  42. package/static/v3/css/lattice.views.3ee19d4e.css +277 -0
  43. package/static/v3/index.html +38 -9
  44. package/static/v3/js/app.46fb61d9.js +26 -0
  45. package/static/v3/js/core/api.22a41d42.js +344 -0
  46. package/static/v3/js/core/api.js +68 -51
  47. package/static/v3/js/core/components.4c83e0a9.js +222 -0
  48. package/static/v3/js/core/components.js +9 -2
  49. package/static/v3/js/core/dom.a2773eb0.js +148 -0
  50. package/static/v3/js/core/router.584570f2.js +37 -0
  51. package/static/v3/js/core/routes.f935dd50.js +78 -0
  52. package/static/v3/js/core/routes.js +6 -1
  53. package/static/v3/js/core/shell.1b6199d6.js +363 -0
  54. package/static/v3/js/core/store.34ebd5e6.js +113 -0
  55. package/static/v3/js/views/admin-audit.660a1fb1.js +185 -0
  56. package/static/v3/js/views/admin-audit.js +1 -1
  57. package/static/v3/js/views/admin-permissions.a7ae5f09.js +177 -0
  58. package/static/v3/js/views/admin-permissions.js +4 -5
  59. package/static/v3/js/views/admin-policies.3658fd86.js +102 -0
  60. package/static/v3/js/views/admin-policies.js +4 -5
  61. package/static/v3/js/views/admin-private-vpc.7d342d36.js +135 -0
  62. package/static/v3/js/views/admin-private-vpc.js +2 -5
  63. package/static/v3/js/views/admin-security.07c66b72.js +180 -0
  64. package/static/v3/js/views/admin-security.js +4 -5
  65. package/static/v3/js/views/admin-users.03bac88c.js +168 -0
  66. package/static/v3/js/views/admin-users.js +6 -6
  67. package/static/v3/js/views/agents.14e48bdd.js +193 -0
  68. package/static/v3/js/views/agents.js +1 -2
  69. package/static/v3/js/views/chat.718144ce.js +449 -0
  70. package/static/v3/js/views/chat.js +2 -3
  71. package/static/v3/js/views/files.4935197e.js +186 -0
  72. package/static/v3/js/views/files.js +27 -21
  73. package/static/v3/js/views/home.cdde3b32.js +119 -0
  74. package/static/v3/js/views/hybrid-search.b22b97e0.js +195 -0
  75. package/static/v3/js/views/hybrid-search.js +1 -1
  76. package/static/v3/js/views/knowledge-graph.a14ea7e7.js +237 -0
  77. package/static/v3/js/views/knowledge-graph.js +2 -3
  78. package/static/v3/js/views/models.a1ffa147.js +256 -0
  79. package/static/v3/js/views/models.js +17 -8
  80. package/static/v3/js/views/my-computer.1b2ff621.js +237 -0
  81. package/static/v3/js/views/my-computer.js +5 -5
  82. package/static/v3/js/views/pipeline.c522f1ce.js +157 -0
  83. package/static/v3/js/views/pipeline.js +3 -7
  84. package/static/v3/js/views/settings.4f777210.js +250 -0
  85. package/static/v3/js/views/settings.js +6 -14
  86. package/static/workflows.html +4 -4
  87. package/static/workspace.html +5 -5
  88. package/docs/images/tmp_frames/frame_00.png +0 -0
  89. package/docs/images/tmp_frames/frame_01.png +0 -0
  90. package/docs/images/tmp_frames/frame_02.png +0 -0
  91. package/docs/images/tmp_frames/frame_03.png +0 -0
  92. package/docs/images/tmp_frames/hero_00.png +0 -0
  93. package/docs/images/tmp_frames/hero_01.png +0 -0
  94. package/docs/images/tmp_frames/hero_02.png +0 -0
  95. package/docs/images/tmp_frames/hero_03.png +0 -0
  96. package/static/v3/js/core/fixtures.js +0 -171
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Lattice AI — automated screenshot capture
3
+ * Usage: SESSION_TOKEN=xxx node scripts/take_screenshots.js
4
+ */
5
+ let playwright;
6
+ try { playwright = require('playwright'); } catch(e) {
7
+ playwright = require('/tmp/node_modules/playwright');
8
+ }
9
+ const { chromium } = playwright;
10
+ const path = require('path');
11
+ const fs = require('fs');
12
+
13
+ const BASE = 'http://localhost:4825';
14
+ const OUT = path.join(__dirname, '..', 'docs', 'images');
15
+ const TOKEN = process.env.SESSION_TOKEN || '';
16
+ fs.mkdirSync(OUT, { recursive: true });
17
+
18
+ const PAGES = [
19
+ { name: 'chat', url: `${BASE}/`, wait: 3500 },
20
+ { name: 'admin', url: `${BASE}/admin`, wait: 3000 },
21
+ { name: 'graph', url: `${BASE}/graph`, wait: 3500 },
22
+ ];
23
+
24
+ (async () => {
25
+ const browser = await chromium.launch({ headless: true });
26
+ const ctx = await browser.newContext({
27
+ viewport: { width: 1440, height: 900 },
28
+ deviceScaleFactor: 2,
29
+ });
30
+
31
+ // Inject session cookie so we skip the login page
32
+ if (TOKEN) {
33
+ await ctx.addCookies([{
34
+ name: 'session_token',
35
+ value: TOKEN,
36
+ domain: 'localhost',
37
+ path: '/',
38
+ httpOnly: true,
39
+ secure: false,
40
+ }]);
41
+ console.log('🍪 Session cookie injected');
42
+ }
43
+
44
+ for (const pg of PAGES) {
45
+ const page = await ctx.newPage();
46
+ console.log(`📸 ${pg.name} → ${pg.url}`);
47
+ try {
48
+ await page.goto(pg.url, { waitUntil: 'networkidle', timeout: 20000 });
49
+ await page.waitForTimeout(pg.wait);
50
+
51
+ // Dismiss any open modals
52
+ await page.evaluate(() => {
53
+ document.querySelectorAll('.modal-backdrop, [data-bs-backdrop], dialog[open]')
54
+ .forEach(el => { el.style.display = 'none'; });
55
+ });
56
+
57
+ const outPath = path.join(OUT, `screenshot-${pg.name}.png`);
58
+ await page.screenshot({ path: outPath, fullPage: false });
59
+ console.log(` ✅ → ${outPath}`);
60
+ } catch (e) {
61
+ console.error(` ❌ ${pg.name}: ${e.message}`);
62
+ } finally {
63
+ await page.close();
64
+ }
65
+ }
66
+
67
+ await browser.close();
68
+ console.log('\n✅ Done!');
69
+ })();
@@ -0,0 +1,167 @@
1
+ #!/usr/bin/env python3
2
+ """Validate release artifacts for a single, explicit version.
3
+
4
+ The release workflow must never upload ``dist/*`` with a glob: that bundles
5
+ *every* historical build and risks shipping a stale version. This validator is
6
+ the guard rail. It checks that exactly the expected 1.1.0-style artifacts exist
7
+ for the requested version, that no version string is mismatched, and (best
8
+ effort) that the VSIX actually contains the compiled extension entrypoint.
9
+
10
+ Usage:
11
+ python scripts/validate_release_artifacts.py 1.1.0
12
+ python scripts/validate_release_artifacts.py 1.1.0 --require-vsix
13
+ python scripts/validate_release_artifacts.py 1.1.0 --dist dist --json
14
+
15
+ Exit code is non-zero on any failure so CI can fail fast.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import argparse
21
+ import json
22
+ import re
23
+ import sys
24
+ import zipfile
25
+ from pathlib import Path
26
+ from typing import Dict, List, Optional
27
+
28
+ SEMVER_RE = re.compile(r"^\d+\.\d+\.\d+([.-][0-9A-Za-z.]+)?$")
29
+
30
+
31
+ def _expected_names(version: str) -> Dict[str, str]:
32
+ return {
33
+ "wheel": f"ltcai-{version}-py3-none-any.whl",
34
+ "sdist": f"ltcai-{version}.tar.gz",
35
+ "vsix": f"ltcai-{version}.vsix",
36
+ }
37
+
38
+
39
+ def _vsix_has_entrypoint(path: Path) -> bool:
40
+ """True if the VSIX contains the compiled extension entrypoint."""
41
+ try:
42
+ with zipfile.ZipFile(path) as zf:
43
+ names = zf.namelist()
44
+ except (zipfile.BadZipFile, OSError):
45
+ return False
46
+ return any(name.endswith("extension/out/extension.js") for name in names)
47
+
48
+
49
+ def _vsix_version(path: Path) -> Optional[str]:
50
+ try:
51
+ with zipfile.ZipFile(path) as zf:
52
+ for name in zf.namelist():
53
+ if name.endswith("extension/package.json"):
54
+ data = json.loads(zf.read(name).decode("utf-8"))
55
+ return str(data.get("version") or "")
56
+ except Exception:
57
+ return None
58
+ return None
59
+
60
+
61
+ def validate(
62
+ version: str,
63
+ dist_dir: Path,
64
+ *,
65
+ require_vsix: bool,
66
+ require_tgz: bool,
67
+ ) -> Dict[str, object]:
68
+ errors: List[str] = []
69
+ warnings: List[str] = []
70
+ found: Dict[str, object] = {}
71
+
72
+ if not SEMVER_RE.match(version):
73
+ errors.append(f"version '{version}' is not a valid semantic version")
74
+
75
+ if not dist_dir.is_dir():
76
+ errors.append(f"dist directory not found: {dist_dir}")
77
+ return {"version": version, "ok": False, "errors": errors, "warnings": warnings, "found": found}
78
+
79
+ expected = _expected_names(version)
80
+
81
+ # Required Python artifacts.
82
+ for key in ("wheel", "sdist"):
83
+ artifact = dist_dir / expected[key]
84
+ if artifact.is_file():
85
+ found[key] = str(artifact)
86
+ else:
87
+ errors.append(f"missing {key}: {artifact.name}")
88
+
89
+ # VSIX: required only when asked, but validate contents when present.
90
+ vsix = dist_dir / expected["vsix"]
91
+ if vsix.is_file():
92
+ found["vsix"] = str(vsix)
93
+ if not _vsix_has_entrypoint(vsix):
94
+ errors.append(f"{vsix.name} is missing extension/out/extension.js (compile step skipped?)")
95
+ vsix_ver = _vsix_version(vsix)
96
+ if vsix_ver and vsix_ver != version:
97
+ errors.append(f"{vsix.name} internal version '{vsix_ver}' != expected '{version}'")
98
+ elif require_vsix:
99
+ errors.append(f"missing vsix: {vsix.name}")
100
+
101
+ # npm pack tarball lives at repo root, not dist/.
102
+ if require_tgz:
103
+ tgz = dist_dir.parent / f"ltcai-{version}.tgz"
104
+ if tgz.is_file():
105
+ found["tgz"] = str(tgz)
106
+ else:
107
+ warnings.append(f"npm tarball not found: {tgz.name} (run `npm pack`)")
108
+
109
+ # Guard against stale-version mixing: warn loudly about other-version builds
110
+ # so a `dist/*` glob upload is obviously unsafe.
111
+ other_versions = set()
112
+ for item in dist_dir.glob("ltcai-*"):
113
+ # Capture just the semver core (e.g. 1.1.0), not packaging suffixes
114
+ # like -py3-none-any.whl / .tar.gz / .vsix.
115
+ m = re.match(r"ltcai-(\d+\.\d+\.\d+)(?:[-.]|$)", item.name)
116
+ if m and m.group(1) != version:
117
+ other_versions.add(m.group(1))
118
+ if other_versions:
119
+ warnings.append(
120
+ "dist/ also contains other versions "
121
+ f"{sorted(other_versions)} — NEVER upload with a `dist/*` glob; "
122
+ f"upload only the explicit {version} filenames."
123
+ )
124
+
125
+ return {
126
+ "version": version,
127
+ "ok": not errors,
128
+ "errors": errors,
129
+ "warnings": warnings,
130
+ "found": found,
131
+ "expected": expected,
132
+ }
133
+
134
+
135
+ def main(argv: Optional[List[str]] = None) -> int:
136
+ parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
137
+ parser.add_argument("version", help="exact version to validate, e.g. 1.1.0")
138
+ parser.add_argument("--dist", default="dist", help="dist directory (default: dist)")
139
+ parser.add_argument("--require-vsix", action="store_true", help="fail if the VSIX is absent")
140
+ parser.add_argument("--require-tgz", action="store_true", help="check for npm pack tarball at repo root")
141
+ parser.add_argument("--json", action="store_true", help="emit machine-readable JSON")
142
+ args = parser.parse_args(argv)
143
+
144
+ result = validate(
145
+ args.version,
146
+ Path(args.dist),
147
+ require_vsix=args.require_vsix,
148
+ require_tgz=args.require_tgz,
149
+ )
150
+
151
+ if args.json:
152
+ print(json.dumps(result, indent=2))
153
+ else:
154
+ status = "OK" if result["ok"] else "FAILED"
155
+ print(f"Release artifact validation for v{result['version']}: {status}")
156
+ for key, path in result["found"].items():
157
+ print(f" found {key}: {Path(path).name}")
158
+ for warning in result["warnings"]:
159
+ print(f" WARN: {warning}")
160
+ for error in result["errors"]:
161
+ print(f" ERROR: {error}")
162
+
163
+ return 0 if result["ok"] else 1
164
+
165
+
166
+ if __name__ == "__main__":
167
+ raise SystemExit(main())
@@ -4,7 +4,7 @@
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>Lattice AI</title>
7
- <script src="/static/scripts/ux.js?v=3.0.0"></script>
7
+ <script src="/static/scripts/ux.js"></script>
8
8
  <link rel="manifest" href="/manifest.json">
9
9
  <meta name="theme-color" content="#f3ecff">
10
10
  <meta name="apple-mobile-web-app-capable" content="yes">
@@ -14,13 +14,13 @@
14
14
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
15
15
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap">
16
16
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
17
- <link rel="stylesheet" href="/static/css/tokens.css?v=3.0.0">
18
- <link rel="stylesheet" href="/static/css/reference/base.css?v=3.0.0">
19
- <link rel="stylesheet" href="/static/css/reference/account.css?v=3.0.0">
20
- <link rel="stylesheet" href="/static/css/reference/admin.css?v=3.0.0">
21
- <link rel="stylesheet" href="/static/css/reference/graph.css?v=3.0.0">
22
- <link rel="stylesheet" href="/static/css/reference/chat.css?v=3.0.0">
23
- <link rel="stylesheet" href="/static/css/responsive.css?v=3.0.0">
17
+ <link rel="stylesheet" href="/static/css/tokens.css">
18
+ <link rel="stylesheet" href="/static/css/reference/base.css">
19
+ <link rel="stylesheet" href="/static/css/reference/account.css">
20
+ <link rel="stylesheet" href="/static/css/reference/admin.css">
21
+ <link rel="stylesheet" href="/static/css/reference/graph.css">
22
+ <link rel="stylesheet" href="/static/css/reference/chat.css">
23
+ <link rel="stylesheet" href="/static/css/responsive.css">
24
24
  </head>
25
25
  <body class="lattice-ref-auth">
26
26
  <div class="orb orb-1"></div>
@@ -110,6 +110,6 @@
110
110
  <a href="#" onclick="return false;" id="privacy-link">개인정보 처리방침</a>
111
111
  </footer>
112
112
 
113
- <script src="/static/scripts/account.js?v=3.0.0"></script>
113
+ <script src="/static/scripts/account.js"></script>
114
114
  </body>
115
115
  </html>
@@ -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>Realtime Activity — Lattice AI</title>
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" />
7
+ <script src="/static/scripts/ux.js"></script>
8
+ <link rel="stylesheet" href="/static/css/tokens.css" />
9
+ <link rel="stylesheet" href="/static/platform.css" />
10
+ <link rel="stylesheet" href="/static/css/responsive.css" />
11
11
  </head>
12
12
  <body>
13
13
  <main>
package/static/admin.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content">
7
7
  <title>Lattice AI Admin</title>
8
- <script src="/static/scripts/ux.js?v=3.0.0"></script>
8
+ <script src="/static/scripts/ux.js"></script>
9
9
  <link rel="manifest" href="/manifest.json">
10
10
  <meta name="theme-color" content="#f3ecff">
11
11
  <meta name="apple-mobile-web-app-capable" content="yes">
@@ -15,13 +15,13 @@
15
15
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
16
16
  <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap">
17
17
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@tabler/icons-webfont@latest/tabler-icons.min.css">
18
- <link rel="stylesheet" href="/static/css/tokens.css?v=3.0.0">
19
- <link rel="stylesheet" href="/static/css/reference/base.css?v=3.0.0">
20
- <link rel="stylesheet" href="/static/css/reference/account.css?v=3.0.0">
21
- <link rel="stylesheet" href="/static/css/reference/admin.css?v=3.0.0">
22
- <link rel="stylesheet" href="/static/css/reference/graph.css?v=3.0.0">
23
- <link rel="stylesheet" href="/static/css/reference/chat.css?v=3.0.0">
24
- <link rel="stylesheet" href="/static/css/responsive.css?v=3.0.0">
18
+ <link rel="stylesheet" href="/static/css/tokens.css">
19
+ <link rel="stylesheet" href="/static/css/reference/base.css">
20
+ <link rel="stylesheet" href="/static/css/reference/account.css">
21
+ <link rel="stylesheet" href="/static/css/reference/admin.css">
22
+ <link rel="stylesheet" href="/static/css/reference/graph.css">
23
+ <link rel="stylesheet" href="/static/css/reference/chat.css">
24
+ <link rel="stylesheet" href="/static/css/responsive.css">
25
25
  <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.0/dist/chart.umd.min.js"></script>
26
26
  </head>
27
27
 
@@ -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>Multi-Agent Runtime — Lattice AI</title>
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" />
7
+ <script src="/static/scripts/ux.js"></script>
8
+ <link rel="stylesheet" href="/static/css/tokens.css" />
9
+ <link rel="stylesheet" href="/static/platform.css" />
10
+ <link rel="stylesheet" href="/static/css/responsive.css" />
11
11
  </head>
12
12
  <body>
13
13
  <main>
package/static/chat.html CHANGED
@@ -5,7 +5,7 @@
5
5
  <meta charset="UTF-8">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover, interactive-widget=resizes-content">
7
7
  <title>Lattice AI — All-in-One Multimodal Workspace</title>
8
- <script src="/static/scripts/ux.js?v=3.0.0"></script>
8
+ <script src="/static/scripts/ux.js"></script>
9
9
 
10
10
  <!-- PWA -->
11
11
  <link rel="manifest" href="/manifest.json">
@@ -24,13 +24,13 @@
24
24
  <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
25
25
 
26
26
  <!-- ── Setup Wizard Styles ──────────────────────────────────────────── -->
27
- <link rel="stylesheet" href="/static/css/tokens.css?v=3.0.0">
28
- <link rel="stylesheet" href="/static/css/reference/base.css?v=3.0.0">
29
- <link rel="stylesheet" href="/static/css/reference/account.css?v=3.0.0">
30
- <link rel="stylesheet" href="/static/css/reference/admin.css?v=3.0.0">
31
- <link rel="stylesheet" href="/static/css/reference/graph.css?v=3.0.0">
32
- <link rel="stylesheet" href="/static/css/reference/chat.css?v=3.0.0">
33
- <link rel="stylesheet" href="/static/css/responsive.css?v=3.0.0">
27
+ <link rel="stylesheet" href="/static/css/tokens.css">
28
+ <link rel="stylesheet" href="/static/css/reference/base.css">
29
+ <link rel="stylesheet" href="/static/css/reference/account.css">
30
+ <link rel="stylesheet" href="/static/css/reference/admin.css">
31
+ <link rel="stylesheet" href="/static/css/reference/graph.css">
32
+ <link rel="stylesheet" href="/static/css/reference/chat.css">
33
+ <link rel="stylesheet" href="/static/css/responsive.css">
34
34
  </head>
35
35
 
36
36
  <body class="lattice-ref-chat">
@@ -838,7 +838,7 @@
838
838
  </div>
839
839
 
840
840
 
841
- <script src="/static/scripts/chat.js?v=3.0.0"></script>
841
+ <script src="/static/scripts/chat.js"></script>
842
842
  </body>
843
843
 
844
844
  </html>
@@ -0,0 +1,260 @@
1
+ /* ============================================================================
2
+ * Lattice AI — Design Tokens (Single Source of Truth) v3.1.0
3
+ *
4
+ * 이 파일이 색·면·테두리·그림자·포커스의 단일 출처다.
5
+ * :root → 라이트 테마 값
6
+ * :root[data-lt-theme="dark"] → 동일 토큰명의 다크 값
7
+ * @media (prefers-color-scheme: dark) → 사용자가 명시 선택 안 했을 때 OS 추종
8
+ *
9
+ * 컴포넌트 CSS 는 항상 var(--token) 만 사용한다. 따라서 이 파일의 값만 바꾸면
10
+ * 전체 UI 가 자연스럽게 전환된다. (컴포넌트별 다크 오버라이드 / !important 금지)
11
+ *
12
+ * 토큰 카테고리(요청 범위): background · surface · card · sidebar · modal · table ·
13
+ * graph · text · muted-text · border · accent · success · warning · danger ·
14
+ * input · overlay · shadow · focus-ring
15
+ * ========================================================================== */
16
+
17
+ :root {
18
+ color-scheme: light;
19
+
20
+ /* ── Brand raw scale (불변, 테마 무관 기준점) ───────────────────────────── */
21
+ --lt-color-primary-100: #dbeafe;
22
+ --lt-color-primary-200: #bfdbfe;
23
+ --lt-color-primary-400: #60a5fa;
24
+ --lt-color-primary-600: #2563eb;
25
+ --lt-color-primary-800: #1e3a8a;
26
+ --lt-color-ink-900: #111827;
27
+ --lt-color-accent-cyan: #0891b2;
28
+ --lt-color-accent-green: #0f9f8f;
29
+ --lt-color-accent-amber: #b7791f;
30
+ --lt-color-accent-pink: #b83280;
31
+
32
+ /* ╔══════════════════════════════════════════════════════════════════════╗
33
+ ║ SEMANTIC TOKENS — LIGHT (neutral workspace palette) ║
34
+ ╚══════════════════════════════════════════════════════════════════════╝ */
35
+
36
+ /* background / surfaces */
37
+ --bg: #f6f8f7;
38
+ --bg-soft: #eef3f2;
39
+ --surface: #ffffff;
40
+ --surface-2: #f0f5f4;
41
+ --surface-3: #e4ecea;
42
+ --surface-muted: #edf3f2;
43
+ --surface-elevated: rgba(255, 255, 255, 0.94);
44
+ --card: rgba(255, 255, 255, 0.92);
45
+ --sidebar: rgba(255, 255, 255, 0.94);
46
+ --modal: rgba(255, 255, 255, 0.98);
47
+ --table: rgba(255, 255, 255, 0.78);
48
+ --input: rgba(255, 255, 255, 0.86);
49
+ --overlay: rgba(15, 23, 42, 0.44);
50
+ --overlay-scrim: var(--overlay);
51
+ --app-bg:
52
+ linear-gradient(180deg, #f8faf9 0%, #eef4f2 54%, #f7faf9 100%);
53
+
54
+ /* text */
55
+ --text: #24223d;
56
+ --muted: #475569; /* muted-text */
57
+ --faint: #70818f;
58
+ --text-muted: var(--muted);
59
+
60
+ /* border / line */
61
+ --border: rgba(15, 23, 42, 0.12);
62
+ --border-strong: rgba(15, 23, 42, 0.22);
63
+ --line: rgba(15, 23, 42, 0.10);
64
+ --line-strong: rgba(15, 23, 42, 0.18);
65
+
66
+ /* accent */
67
+ --accent: #2563eb;
68
+ --accent-2: #0f9f8f;
69
+ --accent-3: #b7791f;
70
+ --accent-pink: #b83280;
71
+ --accent-soft: rgba(37, 99, 235, 0.10);
72
+ --accent-deep: #1d4ed8;
73
+
74
+ /* status */
75
+ --success: #0f8f6d;
76
+ --warning: #a16207;
77
+ --danger: #c2410c;
78
+
79
+ /* graph */
80
+ --graph-bg: #f8faf9;
81
+ --graph-grid: rgba(12, 92, 115, 0.08);
82
+ --graph-pill: rgba(255, 255, 255, 0.92);
83
+
84
+ /* shadow */
85
+ --shadow: 0 18px 50px rgba(15, 23, 42, 0.10);
86
+ --shadow-sm: 0 8px 22px rgba(15, 23, 42, 0.08);
87
+
88
+ /* focus ring */
89
+ --focus-ring: rgba(37, 99, 235, 0.55);
90
+
91
+ /* ── 구조 토큰(색 아님) ─────────────────────────────────────────────── */
92
+ --radius: 14px;
93
+ --radius-sm: 8px;
94
+ --content-width: 900px;
95
+
96
+ /* ── 레거시/별칭 — 코드가 참조하는 모든 변수를 의미 토큰으로 매핑 ──────── */
97
+ --ink: var(--text);
98
+ --blue: var(--accent);
99
+ --green: var(--success);
100
+ --amber: var(--warning);
101
+ --red: var(--danger);
102
+ --pink: var(--accent-pink);
103
+ --ok: var(--success);
104
+ --warn: var(--warning);
105
+ --err: var(--danger);
106
+ --panel: var(--surface);
107
+ --panel-2: var(--surface-2);
108
+ --panel-3: var(--surface-3);
109
+ --panel-strong: var(--surface);
110
+ --glow-green: 0 0 34px rgba(15, 159, 143, 0.14);
111
+ --glow-blue: 0 0 34px rgba(37, 99, 235, 0.14);
112
+ /* PPT reference 스킨 별칭 */
113
+ --ref-purple: var(--accent);
114
+ --ref-purple-2: var(--accent-2);
115
+ --ref-indigo: #2563eb;
116
+ --ref-ink: var(--text);
117
+ --ref-muted: var(--muted);
118
+ --ref-faint: var(--faint);
119
+ --ref-line: var(--border);
120
+ --ref-soft: var(--surface);
121
+ --ref-card: var(--card);
122
+ --ref-shadow: var(--shadow);
123
+
124
+ /* ── --lt-* 별칭 (workspace.css / platform.css 가 참조) — LIGHT ──────────
125
+ 순환참조를 피하려 구체값으로 둔다(소비자가 --bg 등을 --lt-* 로 재정의해도 안전). */
126
+ --lt-bg: #f6f8f7;
127
+ --lt-surface: #ffffff;
128
+ --lt-surface-2: #f0f5f4;
129
+ --lt-input: rgba(255, 255, 255, 0.86);
130
+ --lt-ink: #24223d;
131
+ --lt-ink-soft: #475569;
132
+ --lt-muted: #70818f;
133
+ --lt-line: rgba(15, 23, 42, 0.12);
134
+ --lt-accent: #2563eb;
135
+ --lt-accent-2: #0f9f8f;
136
+ --lt-shadow-md: 0 18px 50px rgba(15, 23, 42, 0.10);
137
+
138
+ /* ── 모션 / 타이포 ─────────────────────────────────────────────────── */
139
+ --lt-motion-ease: cubic-bezier(0.22, 1, 0.36, 1);
140
+ }
141
+
142
+ /* ╔════════════════════════════════════════════════════════════════════════╗
143
+ ║ SEMANTIC TOKENS — DARK (값만 교체, 같은 토큰명) ║
144
+ ╚════════════════════════════════════════════════════════════════════════╝ */
145
+ :root[data-lt-theme="dark"] {
146
+ color-scheme: dark;
147
+
148
+ --bg: #101418;
149
+ --bg-soft: #151b20;
150
+ --surface: #1b2328;
151
+ --surface-2: #202b31;
152
+ --surface-3: #29363d;
153
+ --surface-muted: #151d22;
154
+ --surface-elevated: #253139;
155
+ --card: rgba(255, 255, 255, 0.055);
156
+ --sidebar: #11191d;
157
+ --modal: rgba(27, 35, 40, 0.98);
158
+ --table: rgba(255, 255, 255, 0.045);
159
+ --input: rgba(255, 255, 255, 0.065);
160
+ --overlay: rgba(0, 0, 0, 0.64);
161
+ --overlay-scrim: var(--overlay);
162
+ --app-bg:
163
+ linear-gradient(180deg, #101418 0%, #151d22 54%, #101418 100%);
164
+
165
+ --text: #edf3f2;
166
+ --muted: #b8c5c2;
167
+ --faint: #849894;
168
+ --text-muted: var(--muted);
169
+
170
+ --border: rgba(196, 213, 208, 0.18);
171
+ --border-strong: rgba(196, 213, 208, 0.30);
172
+ --line: rgba(196, 213, 208, 0.14);
173
+ --line-strong: rgba(196, 213, 208, 0.24);
174
+
175
+ --accent: #0284c7;
176
+ --accent-2: #0f766e;
177
+ --accent-3: #facc15;
178
+ --accent-pink: #f0abfc;
179
+ --accent-soft: rgba(2, 132, 199, 0.20);
180
+ --accent-deep: #0369a1;
181
+
182
+ --success: #34d399;
183
+ --warning: #fbbf24;
184
+ --danger: #fb923c;
185
+
186
+ --graph-bg: #0f1518;
187
+ --graph-grid: rgba(94, 234, 212, 0.08);
188
+ --graph-pill: rgba(27, 35, 40, 0.92);
189
+
190
+ --shadow: 0 18px 54px rgba(0, 0, 0, 0.50);
191
+ --shadow-sm: 0 8px 24px rgba(0, 0, 0, 0.42);
192
+
193
+ --focus-ring: rgba(125, 211, 252, 0.70);
194
+
195
+ --lt-bg: #101418;
196
+ --lt-surface: #1b2328;
197
+ --lt-surface-2: #202b31;
198
+ --lt-input: rgba(255, 255, 255, 0.065);
199
+ --lt-ink: #edf3f2;
200
+ --lt-ink-soft: #b8c5c2;
201
+ --lt-muted: #849894;
202
+ --lt-line: rgba(196, 213, 208, 0.18);
203
+ --lt-accent: #0284c7;
204
+ --lt-accent-2: #0f766e;
205
+ --lt-shadow-md: 0 18px 54px rgba(0, 0, 0, 0.50);
206
+
207
+ --ref-indigo: #93c5fd;
208
+ --glow-green: 0 0 34px rgba(94, 234, 212, 0.18);
209
+ --glow-blue: 0 0 34px rgba(125, 211, 252, 0.18);
210
+ }
211
+
212
+ /* OS 다크 추종 — 사용자가 명시적으로 라이트를 고르지 않은 경우에만 */
213
+ @media (prefers-color-scheme: dark) {
214
+ :root:not([data-lt-theme="light"]):not([data-lt-theme="dark"]) {
215
+ color-scheme: dark;
216
+ --bg: #101418; --bg-soft: #151b20;
217
+ --surface: #1b2328; --surface-2: #202b31; --surface-3: #29363d;
218
+ --surface-muted: #151d22; --surface-elevated: #253139;
219
+ --card: rgba(255,255,255,0.055); --sidebar: #11191d;
220
+ --modal: rgba(27,35,40,0.98); --table: rgba(255,255,255,0.045);
221
+ --input: rgba(255,255,255,0.065); --overlay: rgba(0,0,0,0.64); --overlay-scrim: var(--overlay);
222
+ --app-bg:
223
+ linear-gradient(180deg, #101418 0%, #151d22 54%, #101418 100%);
224
+ --text: #edf3f2; --muted: #b8c5c2; --faint: #849894; --text-muted: var(--muted);
225
+ --border: rgba(196,213,208,0.18); --border-strong: rgba(196,213,208,0.30);
226
+ --line: rgba(196,213,208,0.14); --line-strong: rgba(196,213,208,0.24);
227
+ --accent: #0284c7; --accent-2: #0f766e; --accent-3: #facc15;
228
+ --accent-pink: #f0abfc; --accent-soft: rgba(2,132,199,0.20); --accent-deep: #0369a1;
229
+ --success: #34d399; --warning: #fbbf24; --danger: #fb923c;
230
+ --graph-bg: #0f1518; --graph-grid: rgba(94,234,212,0.08); --graph-pill: rgba(27,35,40,0.92);
231
+ --shadow: 0 18px 54px rgba(0,0,0,0.50); --shadow-sm: 0 8px 24px rgba(0,0,0,0.42);
232
+ --focus-ring: rgba(125,211,252,0.70);
233
+ --lt-bg: #101418; --lt-surface: #1b2328; --lt-surface-2: #202b31; --lt-input: rgba(255,255,255,0.065);
234
+ --lt-ink: #edf3f2; --lt-ink-soft: #b8c5c2; --lt-muted: #849894;
235
+ --lt-line: rgba(196,213,208,0.18); --lt-accent: #0284c7; --lt-accent-2: #0f766e;
236
+ --lt-shadow-md: 0 18px 54px rgba(0,0,0,0.50);
237
+ }
238
+ }
239
+
240
+ /* ── 전역 폴리시 ───────────────────────────────────────────────────────── */
241
+ ::selection { background: var(--accent-soft); color: var(--text); }
242
+
243
+ :focus-visible {
244
+ outline: 2px solid var(--focus-ring);
245
+ outline-offset: 2px;
246
+ }
247
+
248
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
249
+ ::-webkit-scrollbar-track { background: transparent; }
250
+ ::-webkit-scrollbar-thumb { background: var(--accent-soft); border-radius: 99px; }
251
+ ::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
252
+
253
+ @media (prefers-reduced-motion: reduce) {
254
+ *, *::before, *::after {
255
+ animation-duration: 0.001ms;
256
+ animation-iteration-count: 1;
257
+ transition-duration: 0.001ms;
258
+ scroll-behavior: auto;
259
+ }
260
+ }
@@ -1,5 +1,5 @@
1
1
  /* ============================================================================
2
- * Lattice AI — Design Tokens (Single Source of Truth) v3.0.0
2
+ * Lattice AI — Design Tokens (Single Source of Truth) v3.1.0
3
3
  *
4
4
  * 이 파일이 색·면·테두리·그림자·포커스의 단일 출처다.
5
5
  * :root → 라이트 테마 값