ltcai 2.2.2 → 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 +66 -27
- package/codex_telegram_bot.py +6 -2
- package/docs/CHANGELOG.md +154 -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 +21 -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/logging_safety.py +62 -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 +8 -4
- package/static/account.html +9 -4
- package/static/activity.html +4 -4
- package/static/admin.html +8 -3
- package/static/agents.html +4 -4
- package/static/chat.html +16 -11
- package/static/css/reference/account.css +439 -0
- package/static/css/reference/admin.css +610 -0
- package/static/css/reference/base.css +1658 -0
- package/static/{lattice-reference.css → css/reference/chat.css} +271 -3633
- package/static/css/reference/graph.css +1016 -0
- package/static/css/responsive.css +248 -1
- package/static/css/tokens.css +132 -126
- package/static/favicon.ico +0 -0
- package/static/graph.html +9 -4
- package/static/manifest.json +3 -3
- package/static/platform.css +1 -1
- package/static/plugins.html +4 -4
- package/static/scripts/account.js +4 -4
- package/static/scripts/chat.js +227 -77
- package/static/scripts/workspace.js +78 -0
- package/static/sw.js +5 -3
- 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 +408 -14
- package/static/workspace.html +43 -24
- package/telegram_bot.py +18 -14
package/static/plugins.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>Plugin SDK — 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>
|
|
@@ -162,7 +162,7 @@ const API_BASE = window.location.protocol === 'file:' ? 'http://localhost:4825'
|
|
|
162
162
|
localStorage.setItem('ltcai_user_nickname', data.nickname || data.name || data.email);
|
|
163
163
|
localStorage.setItem('ltcai_is_admin', data.is_admin ? 'true' : 'false');
|
|
164
164
|
requestSetupAfterLogin();
|
|
165
|
-
window.location.href = '/
|
|
165
|
+
window.location.href = '/app';
|
|
166
166
|
} else {
|
|
167
167
|
const data = await res.json().catch(() => ({}));
|
|
168
168
|
setMsg('login-msg', data.detail || t('err_login_fail'));
|
|
@@ -205,7 +205,7 @@ const API_BASE = window.location.protocol === 'file:' ? 'http://localhost:4825'
|
|
|
205
205
|
localStorage.setItem('ltcai_user_nickname', data.nickname || data.name || data.email);
|
|
206
206
|
localStorage.setItem('ltcai_is_admin', data.is_admin ? 'true' : 'false');
|
|
207
207
|
requestSetupAfterLogin();
|
|
208
|
-
window.location.href = '/
|
|
208
|
+
window.location.href = '/app';
|
|
209
209
|
}
|
|
210
210
|
});
|
|
211
211
|
} else {
|
|
@@ -221,9 +221,9 @@ const API_BASE = window.location.protocol === 'file:' ? 'http://localhost:4825'
|
|
|
221
221
|
}
|
|
222
222
|
}
|
|
223
223
|
|
|
224
|
-
// If already logged in, skip to
|
|
224
|
+
// If already logged in, skip to the v3 workspace shell.
|
|
225
225
|
apiFetch('/account/profile').then(r => {
|
|
226
|
-
if (r.ok) window.location.href = '/
|
|
226
|
+
if (r.ok) window.location.href = '/app';
|
|
227
227
|
}).catch(() => {});
|
|
228
228
|
|
|
229
229
|
initSSO();
|
package/static/scripts/chat.js
CHANGED
|
@@ -14,17 +14,133 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
14
14
|
let telegramHistorySyncEnabled = false;
|
|
15
15
|
let telegramHistorySyncInFlight = false;
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
17
|
+
// ── 멀티 LLM 파이프라인 상태 ──
|
|
18
|
+
let pipelineConfig = { planning: null, executing: null, reviewing: null };
|
|
19
|
+
let pipelineActive = false; // true이면 전송 시 pipeline 모드
|
|
20
|
+
const MODAL_LAYER_IDS = [
|
|
21
|
+
'acct-modal-overlay',
|
|
22
|
+
'mcp-modal-overlay',
|
|
23
|
+
'workspace-modal-overlay',
|
|
24
|
+
'mode-modal-overlay',
|
|
25
|
+
'model-overlay',
|
|
26
|
+
'pipeline-overlay',
|
|
27
|
+
'admin-overlay',
|
|
28
|
+
'vpc-overlay',
|
|
29
|
+
'status-overlay',
|
|
30
|
+
'file-create-overlay',
|
|
31
|
+
'file-editor-overlay',
|
|
32
|
+
'local-browser-overlay',
|
|
33
|
+
'advanced-settings-overlay',
|
|
34
|
+
'cu-overlay',
|
|
35
|
+
'setup-overlay',
|
|
36
|
+
'onboarding-overlay',
|
|
37
|
+
'perm-overlay',
|
|
38
|
+
];
|
|
39
|
+
const MODAL_LAYER_SET = new Set(MODAL_LAYER_IDS);
|
|
40
|
+
let activeModalLayerId = null;
|
|
41
|
+
let restoreModalLayerId = null;
|
|
42
|
+
let bodyOverflowBeforeModal = '';
|
|
43
|
+
|
|
44
|
+
function modalLayerEl(id) {
|
|
45
|
+
return document.getElementById(id);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function isModalLayerVisible(el) {
|
|
49
|
+
if (!el) return false;
|
|
50
|
+
return el.classList.contains('open') || getComputedStyle(el).display !== 'none';
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function refreshBodyModalLock() {
|
|
54
|
+
const hasOpen = MODAL_LAYER_IDS.some(id => isModalLayerVisible(modalLayerEl(id)));
|
|
55
|
+
if (hasOpen) {
|
|
56
|
+
if (!document.body.classList.contains('modal-open')) {
|
|
57
|
+
bodyOverflowBeforeModal = document.body.style.overflow || '';
|
|
58
|
+
}
|
|
59
|
+
document.body.classList.add('modal-open');
|
|
60
|
+
document.body.style.overflow = 'hidden';
|
|
61
|
+
} else {
|
|
62
|
+
document.body.classList.remove('modal-open');
|
|
63
|
+
document.body.style.overflow = bodyOverflowBeforeModal;
|
|
64
|
+
bodyOverflowBeforeModal = '';
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function hideModalElement(id) {
|
|
69
|
+
const el = modalLayerEl(id);
|
|
70
|
+
if (!el) return;
|
|
71
|
+
el.classList.remove('open');
|
|
72
|
+
el.style.display = 'none';
|
|
73
|
+
el.setAttribute('aria-hidden', 'true');
|
|
74
|
+
if ('inert' in el) el.inert = true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function showModalLayer(id, options = {}) {
|
|
78
|
+
const el = modalLayerEl(id);
|
|
79
|
+
if (!el) return;
|
|
80
|
+
const current = activeModalLayerId && activeModalLayerId !== id ? activeModalLayerId : null;
|
|
81
|
+
MODAL_LAYER_IDS.forEach(otherId => {
|
|
82
|
+
if (otherId !== id) hideModalElement(otherId);
|
|
83
|
+
});
|
|
84
|
+
if (options.restorePrevious && current) restoreModalLayerId = current;
|
|
85
|
+
else if (!options.keepRestore) restoreModalLayerId = null;
|
|
86
|
+
el.removeAttribute('aria-hidden');
|
|
87
|
+
if ('inert' in el) el.inert = false;
|
|
88
|
+
el.classList.add('open');
|
|
89
|
+
el.style.display = 'flex';
|
|
90
|
+
activeModalLayerId = id;
|
|
91
|
+
refreshBodyModalLock();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function closeModalLayer(id, options = {}) {
|
|
95
|
+
hideModalElement(id);
|
|
96
|
+
if (activeModalLayerId === id) activeModalLayerId = null;
|
|
97
|
+
const restoreId = restoreModalLayerId;
|
|
98
|
+
if (!options.skipRestore) restoreModalLayerId = null;
|
|
99
|
+
refreshBodyModalLock();
|
|
100
|
+
if (!options.skipRestore && restoreId && restoreId !== id) {
|
|
101
|
+
showModalLayer(restoreId, { keepRestore: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function closeAllModalLayers() {
|
|
106
|
+
MODAL_LAYER_IDS.forEach(hideModalElement);
|
|
107
|
+
activeModalLayerId = null;
|
|
108
|
+
restoreModalLayerId = null;
|
|
109
|
+
refreshBodyModalLock();
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function dismissModalLayer(id) {
|
|
113
|
+
if (id === 'perm-overlay') {
|
|
114
|
+
resolvePermission(false);
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
closeModalLayer(id);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
document.addEventListener('keydown', (event) => {
|
|
121
|
+
if (event.key !== 'Escape') return;
|
|
122
|
+
const visible = [...MODAL_LAYER_IDS].reverse().find(id => isModalLayerVisible(modalLayerEl(id)));
|
|
123
|
+
if (!visible) return;
|
|
124
|
+
event.preventDefault();
|
|
125
|
+
dismissModalLayer(visible);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
document.addEventListener('click', (event) => {
|
|
129
|
+
const target = event.target;
|
|
130
|
+
if (target && target.id && MODAL_LAYER_SET.has(target.id)) dismissModalLayer(target.id);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
window.addEventListener('pagehide', closeAllModalLayers);
|
|
134
|
+
window.addEventListener('popstate', closeAllModalLayers);
|
|
135
|
+
window.addEventListener('hashchange', closeAllModalLayers);
|
|
136
|
+
|
|
137
|
+
function openPipelineModal() {
|
|
138
|
+
showModalLayer('pipeline-overlay');
|
|
139
|
+
loadPipelineModelOptions();
|
|
140
|
+
}
|
|
141
|
+
function closePipelineModal() {
|
|
142
|
+
closeModalLayer('pipeline-overlay');
|
|
143
|
+
}
|
|
28
144
|
function resetPipeline() {
|
|
29
145
|
pipelineConfig = { planning: null, executing: null, reviewing: null };
|
|
30
146
|
pipelineActive = false;
|
|
@@ -226,9 +342,13 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
226
342
|
logout: '로그아웃', admin_dashboard: '관리자 대시보드',
|
|
227
343
|
my_status: '내 상태 보기', auto_setup: '자동 설정',
|
|
228
344
|
nav_home: '홈', nav_chat: '채팅', nav_workspace: 'Workspace OS', nav_knowledge: '지식 그래프',
|
|
229
|
-
nav_pipeline: '파이프라인', nav_files: '내 컴퓨터',
|
|
345
|
+
nav_pipeline: '파이프라인', nav_files: '파일', nav_computer: '내 컴퓨터',
|
|
346
|
+
nav_search: '검색', nav_new_chat: '새 대화',
|
|
230
347
|
nav_model_status: '모델 상태', nav_runtime: '실행 방식 설정',
|
|
231
348
|
nav_advanced_settings: '고급 설정',
|
|
349
|
+
nav_user_management: '사용자 관리', nav_permission_management: '권한 관리',
|
|
350
|
+
nav_audit_logs: '감사 로그', nav_security: '보안', nav_sensitive_data: '민감정보 감시',
|
|
351
|
+
nav_org_policies: '조직 정책', nav_private_vpc: 'Private VPC',
|
|
232
352
|
history_search_ph: '대화 검색...', new_chat: 'New Chat',
|
|
233
353
|
history_section: '대화', history_empty: '아직 저장된 대화가 없습니다.',
|
|
234
354
|
new_conversation: '새 대화', previous_history: '이전 대화 기록',
|
|
@@ -305,9 +425,13 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
305
425
|
logout: 'Logout', admin_dashboard: 'Admin Dashboard',
|
|
306
426
|
my_status: 'My Status', auto_setup: 'Auto Setup',
|
|
307
427
|
nav_home: 'Home', nav_chat: 'Chat', nav_workspace: 'Workspace OS', nav_knowledge: 'Knowledge Graph',
|
|
308
|
-
nav_pipeline: 'Pipeline', nav_files: 'My Computer',
|
|
428
|
+
nav_pipeline: 'Pipeline', nav_files: 'Files', nav_computer: 'My Computer',
|
|
429
|
+
nav_search: 'Search', nav_new_chat: 'New Chat',
|
|
309
430
|
nav_model_status: 'Model Status', nav_runtime: 'Execution Settings',
|
|
310
431
|
nav_advanced_settings: 'Advanced Settings',
|
|
432
|
+
nav_user_management: 'User Management', nav_permission_management: 'Permissions',
|
|
433
|
+
nav_audit_logs: 'Audit Logs', nav_security: 'Security', nav_sensitive_data: 'Sensitive Data',
|
|
434
|
+
nav_org_policies: 'Org Policies', nav_private_vpc: 'Private VPC',
|
|
311
435
|
history_search_ph: 'Search chats...', new_chat: 'New Chat',
|
|
312
436
|
history_section: 'Chats', history_empty: 'No saved chats yet.',
|
|
313
437
|
new_conversation: 'New chat', previous_history: 'Previous chat history',
|
|
@@ -559,26 +683,37 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
559
683
|
const BASE_NAV_ITEMS = [
|
|
560
684
|
{ id: 'home', icon: 'ti-home', labelKey: 'nav_home' },
|
|
561
685
|
{ id: 'chat', icon: 'ti-message-circle', labelKey: 'nav_chat' },
|
|
562
|
-
{ id: 'workspace-os', icon: 'ti-layout-dashboard', labelKey: 'nav_workspace' },
|
|
563
686
|
{ id: 'knowledge', icon: 'ti-chart-dots-3', labelKey: 'nav_knowledge' },
|
|
564
687
|
{ id: 'pipeline', icon: 'ti-git-branch', labelKey: 'nav_pipeline' },
|
|
565
|
-
{ id: 'files', icon: 'ti-
|
|
566
|
-
{ id: '
|
|
688
|
+
{ id: 'files', icon: 'ti-files', labelKey: 'nav_files' },
|
|
689
|
+
{ id: 'my-computer', icon: 'ti-device-desktop', labelKey: 'nav_computer' },
|
|
690
|
+
{ id: 'search', icon: 'ti-search', labelKey: 'nav_search' },
|
|
691
|
+
{ id: 'new-chat', icon: 'ti-plus', labelKey: 'nav_new_chat' },
|
|
567
692
|
];
|
|
568
693
|
|
|
569
694
|
const ADVANCED_NAV_ITEMS = [
|
|
695
|
+
{ id: 'workspace-os', icon: 'ti-layout-dashboard', labelKey: 'nav_workspace' },
|
|
570
696
|
{ id: 'model-status', icon: 'ti-cpu-2', labelKey: 'nav_model_status' },
|
|
571
697
|
{ id: 'runtime', icon: 'ti-adjustments-cog', labelKey: 'nav_runtime' },
|
|
572
698
|
{ id: 'advanced-settings', icon: 'ti-settings', labelKey: 'nav_advanced_settings' },
|
|
573
699
|
];
|
|
574
700
|
|
|
701
|
+
const ADMIN_NAV_ITEMS = [
|
|
702
|
+
{ id: 'admin-users', icon: 'ti-users', labelKey: 'nav_user_management' },
|
|
703
|
+
{ id: 'admin-permissions', icon: 'ti-key', labelKey: 'nav_permission_management' },
|
|
704
|
+
{ id: 'admin-audit', icon: 'ti-report-search', labelKey: 'nav_audit_logs' },
|
|
705
|
+
{ id: 'admin-security', icon: 'ti-shield-check', labelKey: 'nav_security' },
|
|
706
|
+
{ id: 'admin-sensitive-data', icon: 'ti-radar', labelKey: 'nav_sensitive_data' },
|
|
707
|
+
{ id: 'admin-policies', icon: 'ti-building-skyscraper', labelKey: 'nav_org_policies' },
|
|
708
|
+
{ id: 'private-vpc', icon: 'ti-cloud-lock', labelKey: 'nav_private_vpc' },
|
|
709
|
+
];
|
|
710
|
+
|
|
575
711
|
function navItemsForMode(mode) {
|
|
576
712
|
if (mode === 'advanced') return [...BASE_NAV_ITEMS, ...ADVANCED_NAV_ITEMS];
|
|
577
|
-
// 관리자 모드: 내 상태 보기 뒤에 고급 3개 + 관리자 대시보드
|
|
578
713
|
if (mode === 'admin') return [
|
|
579
714
|
...BASE_NAV_ITEMS,
|
|
580
715
|
...ADVANCED_NAV_ITEMS,
|
|
581
|
-
|
|
716
|
+
...ADMIN_NAV_ITEMS,
|
|
582
717
|
];
|
|
583
718
|
return BASE_NAV_ITEMS;
|
|
584
719
|
}
|
|
@@ -627,12 +762,25 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
627
762
|
else if (id === 'knowledge') openDataGraph();
|
|
628
763
|
else if (id === 'pipeline') openPipelineModal();
|
|
629
764
|
else if (id === 'files') openLocalBrowser();
|
|
630
|
-
else if (id === 'computer') openCuPanel();
|
|
765
|
+
else if (id === 'my-computer') openCuPanel();
|
|
766
|
+
else if (id === 'search') {
|
|
767
|
+
showChat();
|
|
768
|
+
markActiveNav('search');
|
|
769
|
+
const search = document.getElementById('history-search-input');
|
|
770
|
+
if (search) search.focus();
|
|
771
|
+
}
|
|
772
|
+
else if (id === 'new-chat') startNewChat();
|
|
631
773
|
else if (id === 'status') openStatusPanel();
|
|
632
774
|
else if (id === 'model-status') openStatusPanel();
|
|
633
775
|
else if (id === 'runtime') openModelPanel();
|
|
634
776
|
else if (id === 'advanced-settings') openAdvancedSettingsPanel();
|
|
635
777
|
else if (id === 'admin-dashboard') openAdminPanel();
|
|
778
|
+
else if (id === 'admin-users') window.location.href = `${API_BASE}/admin#users`;
|
|
779
|
+
else if (id === 'admin-permissions') window.location.href = `${API_BASE}/admin#permissions`;
|
|
780
|
+
else if (id === 'admin-audit') window.location.href = `${API_BASE}/admin#audit`;
|
|
781
|
+
else if (id === 'admin-security' || id === 'admin-sensitive-data') window.location.href = `${API_BASE}/admin#security`;
|
|
782
|
+
else if (id === 'admin-policies') window.location.href = `${API_BASE}/admin#enterprise`;
|
|
783
|
+
else if (id === 'private-vpc') openVpcPanel();
|
|
636
784
|
}
|
|
637
785
|
|
|
638
786
|
function focusChatInput() {
|
|
@@ -731,12 +879,12 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
731
879
|
focusChatInput();
|
|
732
880
|
}
|
|
733
881
|
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
882
|
+
function openAdvancedSettingsPanel() {
|
|
883
|
+
showModalLayer('advanced-settings-overlay');
|
|
884
|
+
}
|
|
737
885
|
|
|
738
|
-
|
|
739
|
-
|
|
886
|
+
function closeAdvancedSettingsPanel() {
|
|
887
|
+
closeModalLayer('advanced-settings-overlay');
|
|
740
888
|
}
|
|
741
889
|
|
|
742
890
|
function updateWorkspaceModeUi() {
|
|
@@ -778,19 +926,19 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
778
926
|
}
|
|
779
927
|
|
|
780
928
|
function selectWorkspace(kind) {
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
929
|
+
setWorkspacePreference(kind);
|
|
930
|
+
closeModalLayer('workspace-modal-overlay');
|
|
931
|
+
updateWorkspaceModeUi();
|
|
932
|
+
openModeSelector();
|
|
933
|
+
}
|
|
786
934
|
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
935
|
+
function openModeSelector() {
|
|
936
|
+
updateWorkspaceModeUi();
|
|
937
|
+
showModalLayer('mode-modal-overlay');
|
|
938
|
+
}
|
|
791
939
|
|
|
792
|
-
|
|
793
|
-
|
|
940
|
+
function closeModeSelector() {
|
|
941
|
+
closeModalLayer('mode-modal-overlay');
|
|
794
942
|
}
|
|
795
943
|
|
|
796
944
|
function selectMode(mode) {
|
|
@@ -829,12 +977,12 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
829
977
|
async function startOnboardingIfNeeded(force = false) {
|
|
830
978
|
updateWorkspaceModeUi();
|
|
831
979
|
const forceAfterLogin = consumeSetupAfterLoginFlag();
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
980
|
+
const hasLoadedModel = await modelLoadedForChat();
|
|
981
|
+
if (!force && !forceAfterLogin && onboardingComplete() && hasLoadedModel) {
|
|
982
|
+
closeModalLayer('onboarding-overlay');
|
|
983
|
+
return;
|
|
984
|
+
}
|
|
985
|
+
showModalLayer('onboarding-overlay');
|
|
838
986
|
if (forceAfterLogin || !hasLoadedModel) {
|
|
839
987
|
renderPcAnalysis();
|
|
840
988
|
} else {
|
|
@@ -1051,7 +1199,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
1051
1199
|
// Top pick callout
|
|
1052
1200
|
const top = rec.top_pick;
|
|
1053
1201
|
const topHtml = top ? `
|
|
1054
|
-
<div style="border:1px solid
|
|
1202
|
+
<div style="border:1px solid var(--success);background:var(--accent-soft);color:var(--text);border-radius:10px;padding:10px 12px;margin:8px 0">
|
|
1055
1203
|
<div style="font-weight:700">⭐ Best for this PC — ${escapeHtml(top.name || top.id)} ${badge('recommended')}</div>
|
|
1056
1204
|
<div style="font-size:12px;opacity:0.8;margin-top:3px">${escapeHtml(top.reason || '')}</div>
|
|
1057
1205
|
<div style="font-size:12px;margin-top:4px">${escapeHtml(top.size || '')} · ${escapeHtml(ram(top))} · ${escapeHtml(nextStep(rec.engine))}</div>
|
|
@@ -1362,12 +1510,12 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
1362
1510
|
`);
|
|
1363
1511
|
}
|
|
1364
1512
|
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1513
|
+
function finishOnboarding(mode) {
|
|
1514
|
+
selectMode(mode);
|
|
1515
|
+
setOnboardingComplete();
|
|
1516
|
+
closeModalLayer('onboarding-overlay');
|
|
1517
|
+
showChat();
|
|
1518
|
+
}
|
|
1371
1519
|
|
|
1372
1520
|
function switchAcctTab(tab) {
|
|
1373
1521
|
['profile', 'password'].forEach(t => {
|
|
@@ -1390,10 +1538,10 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
1390
1538
|
document.getElementById('profile-nickname').value = data.nickname || '';
|
|
1391
1539
|
}
|
|
1392
1540
|
} catch {}
|
|
1393
|
-
|
|
1541
|
+
showModalLayer('acct-modal-overlay');
|
|
1394
1542
|
}
|
|
1395
1543
|
function closeAcctModal() {
|
|
1396
|
-
|
|
1544
|
+
closeModalLayer('acct-modal-overlay');
|
|
1397
1545
|
}
|
|
1398
1546
|
document.addEventListener('click', (e) => {
|
|
1399
1547
|
const overlay = document.getElementById('acct-modal-overlay');
|
|
@@ -1707,7 +1855,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
1707
1855
|
}
|
|
1708
1856
|
|
|
1709
1857
|
async function openModelPanel() {
|
|
1710
|
-
|
|
1858
|
+
showModalLayer('model-overlay');
|
|
1711
1859
|
document.getElementById('model-list').innerHTML = '<div class="sensitivity-preview">실행 엔진과 모델 목록을 불러오는 중입니다...</div>';
|
|
1712
1860
|
try {
|
|
1713
1861
|
const res = await apiFetch('/engines');
|
|
@@ -1742,7 +1890,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
1742
1890
|
}
|
|
1743
1891
|
|
|
1744
1892
|
function closeModelPanel() {
|
|
1745
|
-
|
|
1893
|
+
closeModalLayer('model-overlay');
|
|
1746
1894
|
}
|
|
1747
1895
|
|
|
1748
1896
|
async function installEngine(engineId) {
|
|
@@ -2194,7 +2342,9 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2194
2342
|
if (!t) {
|
|
2195
2343
|
t = document.createElement('div');
|
|
2196
2344
|
t.id = 'ltcai-toast';
|
|
2197
|
-
|
|
2345
|
+
// Styling lives in static/css/responsive.css (#ltcai-toast) so the
|
|
2346
|
+
// toast is token-driven and adapts to light/dark. Only the opacity
|
|
2347
|
+
// animation state is toggled inline below.
|
|
2198
2348
|
document.body.appendChild(t);
|
|
2199
2349
|
}
|
|
2200
2350
|
t.textContent = msg;
|
|
@@ -2204,7 +2354,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2204
2354
|
}
|
|
2205
2355
|
|
|
2206
2356
|
function closeAdminPanel() {
|
|
2207
|
-
|
|
2357
|
+
closeModalLayer('admin-overlay');
|
|
2208
2358
|
}
|
|
2209
2359
|
|
|
2210
2360
|
// ── VPC Panel ────────────────────────────────────────
|
|
@@ -2235,7 +2385,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2235
2385
|
}
|
|
2236
2386
|
|
|
2237
2387
|
async function openVpcPanel() {
|
|
2238
|
-
|
|
2388
|
+
showModalLayer('vpc-overlay');
|
|
2239
2389
|
try {
|
|
2240
2390
|
const res = await apiFetch('/vpc/status');
|
|
2241
2391
|
if (res.ok) fillVpcPanelForm(await res.json());
|
|
@@ -2243,7 +2393,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2243
2393
|
}
|
|
2244
2394
|
|
|
2245
2395
|
function closeVpcPanel() {
|
|
2246
|
-
|
|
2396
|
+
closeModalLayer('vpc-overlay');
|
|
2247
2397
|
}
|
|
2248
2398
|
|
|
2249
2399
|
function selectVpcProvider(name, btn) {
|
|
@@ -2293,7 +2443,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2293
2443
|
|
|
2294
2444
|
// ── Status Summary Panel ─────────────────────────────
|
|
2295
2445
|
async function openStatusPanel() {
|
|
2296
|
-
|
|
2446
|
+
showModalLayer('status-overlay');
|
|
2297
2447
|
document.getElementById('status-panel-body').innerHTML = '<div class="sensitivity-preview">상태를 불러오는 중...</div>';
|
|
2298
2448
|
try {
|
|
2299
2449
|
const mode = getCurrentMode();
|
|
@@ -2362,7 +2512,7 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2362
2512
|
}
|
|
2363
2513
|
|
|
2364
2514
|
function closeStatusPanel() {
|
|
2365
|
-
|
|
2515
|
+
closeModalLayer('status-overlay');
|
|
2366
2516
|
}
|
|
2367
2517
|
|
|
2368
2518
|
// ── 파일 생성 패널 ────────────────────────────────────
|
|
@@ -2415,11 +2565,11 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2415
2565
|
</div>`;
|
|
2416
2566
|
}
|
|
2417
2567
|
document.getElementById('file-create-form').innerHTML = formHtml;
|
|
2418
|
-
|
|
2568
|
+
showModalLayer('file-create-overlay');
|
|
2419
2569
|
}
|
|
2420
2570
|
|
|
2421
2571
|
function closeFileCreate() {
|
|
2422
|
-
|
|
2572
|
+
closeModalLayer('file-create-overlay');
|
|
2423
2573
|
}
|
|
2424
2574
|
|
|
2425
2575
|
function _formatBytes(b) {
|
|
@@ -2507,24 +2657,24 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2507
2657
|
document.getElementById('perm-title').textContent = '파일 접근 요청';
|
|
2508
2658
|
document.getElementById('perm-path').textContent = path;
|
|
2509
2659
|
document.getElementById('perm-desc').textContent = `AI가 아래 경로에 대한 "${actionLabel}" 작업을 요청합니다. 허용하시겠습니까?`;
|
|
2510
|
-
|
|
2660
|
+
showModalLayer('perm-overlay', { restorePrevious: true });
|
|
2511
2661
|
});
|
|
2512
2662
|
}
|
|
2513
2663
|
|
|
2514
2664
|
function resolvePermission(allowed) {
|
|
2515
|
-
|
|
2665
|
+
closeModalLayer('perm-overlay');
|
|
2516
2666
|
if (_permResolve) { _permResolve(allowed); _permResolve = null; }
|
|
2517
2667
|
}
|
|
2518
2668
|
|
|
2519
2669
|
let _localCurrentPath = '~';
|
|
2520
2670
|
|
|
2521
2671
|
async function openLocalBrowser() {
|
|
2522
|
-
|
|
2672
|
+
showModalLayer('local-browser-overlay');
|
|
2523
2673
|
await localNav(_localCurrentPath || '~');
|
|
2524
2674
|
}
|
|
2525
2675
|
|
|
2526
2676
|
function closeLocalBrowser() {
|
|
2527
|
-
|
|
2677
|
+
closeModalLayer('local-browser-overlay');
|
|
2528
2678
|
}
|
|
2529
2679
|
|
|
2530
2680
|
async function localNav(path) {
|
|
@@ -2710,8 +2860,8 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2710
2860
|
document.getElementById('editor-filepath').textContent = path;
|
|
2711
2861
|
document.getElementById('file-editor-content').value = text;
|
|
2712
2862
|
document.getElementById('editor-status').textContent = '';
|
|
2713
|
-
|
|
2714
|
-
|
|
2863
|
+
closeModalLayer('local-browser-overlay', { skipRestore: true });
|
|
2864
|
+
showModalLayer('file-editor-overlay');
|
|
2715
2865
|
} catch(e) {
|
|
2716
2866
|
resultEl.innerHTML = `
|
|
2717
2867
|
<div class="sensitivity-preview">⚠️ 문서 읽기 실패: ${escapeHtml(e.message)}<br>
|
|
@@ -2761,15 +2911,15 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2761
2911
|
document.getElementById('editor-filepath').textContent = path;
|
|
2762
2912
|
document.getElementById('file-editor-content').value = content;
|
|
2763
2913
|
document.getElementById('editor-status').textContent = '';
|
|
2764
|
-
|
|
2765
|
-
|
|
2914
|
+
closeModalLayer('local-browser-overlay', { skipRestore: true });
|
|
2915
|
+
showModalLayer('file-editor-overlay');
|
|
2766
2916
|
} catch(e) {
|
|
2767
2917
|
resultEl.innerHTML = `<div class="sensitivity-preview">${escapeHtml(e.message)}</div>`;
|
|
2768
2918
|
}
|
|
2769
2919
|
}
|
|
2770
2920
|
|
|
2771
2921
|
function closeFileEditor() {
|
|
2772
|
-
|
|
2922
|
+
closeModalLayer('file-editor-overlay');
|
|
2773
2923
|
}
|
|
2774
2924
|
|
|
2775
2925
|
async function saveLocalFile() {
|
|
@@ -2860,8 +3010,8 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
2860
3010
|
document.getElementById('file-editor-content').value = text;
|
|
2861
3011
|
document.getElementById('editor-status').textContent = '⚠️ 이미지/표 등 비텍스트 요소는 표시되지 않을 수 있습니다';
|
|
2862
3012
|
document.getElementById('editor-status').style.color = 'var(--accent-2)';
|
|
2863
|
-
|
|
2864
|
-
|
|
3013
|
+
closeModalLayer('local-browser-overlay', { skipRestore: true });
|
|
3014
|
+
showModalLayer('file-editor-overlay');
|
|
2865
3015
|
} catch(e) {
|
|
2866
3016
|
resultEl.innerHTML = `<div class="sensitivity-preview">텍스트 추출 실패: ${escapeHtml(e.message)}</div>`;
|
|
2867
3017
|
}
|
|
@@ -4186,12 +4336,12 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
4186
4336
|
let cuAgentAbort = null;
|
|
4187
4337
|
|
|
4188
4338
|
async function openCuPanel() {
|
|
4189
|
-
|
|
4339
|
+
showModalLayer('cu-overlay');
|
|
4190
4340
|
await cuRefreshStatus();
|
|
4191
4341
|
}
|
|
4192
4342
|
|
|
4193
4343
|
function closeCuPanel() {
|
|
4194
|
-
|
|
4344
|
+
closeModalLayer('cu-overlay');
|
|
4195
4345
|
}
|
|
4196
4346
|
|
|
4197
4347
|
async function cuRefreshStatus() {
|
|
@@ -4358,12 +4508,12 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
4358
4508
|
let _wizItems = []; // items selected by user in step 2
|
|
4359
4509
|
|
|
4360
4510
|
function openSetupWizard() {
|
|
4361
|
-
|
|
4511
|
+
showModalLayer('setup-overlay');
|
|
4362
4512
|
_runStep1();
|
|
4363
4513
|
}
|
|
4364
4514
|
|
|
4365
4515
|
function closeSetupWizard() {
|
|
4366
|
-
|
|
4516
|
+
closeModalLayer('setup-overlay');
|
|
4367
4517
|
}
|
|
4368
4518
|
|
|
4369
4519
|
// Prevent click-through on overlay background
|
|
@@ -4678,12 +4828,12 @@ const chatViewport = document.getElementById('chat-viewport');
|
|
|
4678
4828
|
let _mcpCurrentTab = 'registry';
|
|
4679
4829
|
|
|
4680
4830
|
async function openMcpModal() {
|
|
4681
|
-
|
|
4831
|
+
showModalLayer('mcp-modal-overlay');
|
|
4682
4832
|
await renderMcpModal(_mcpCurrentTab);
|
|
4683
4833
|
}
|
|
4684
4834
|
|
|
4685
4835
|
function closeMcpModal() {
|
|
4686
|
-
|
|
4836
|
+
closeModalLayer('mcp-modal-overlay');
|
|
4687
4837
|
}
|
|
4688
4838
|
|
|
4689
4839
|
function switchMcpTab(tab, btn) {
|