@jungjaehoon/mama-os 0.18.2 → 0.19.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 (171) hide show
  1. package/dist/agent/agent-loop.d.ts +25 -0
  2. package/dist/agent/agent-loop.d.ts.map +1 -1
  3. package/dist/agent/agent-loop.js +67 -14
  4. package/dist/agent/agent-loop.js.map +1 -1
  5. package/dist/agent/code-act/host-bridge.d.ts.map +1 -1
  6. package/dist/agent/code-act/host-bridge.js +98 -0
  7. package/dist/agent/code-act/host-bridge.js.map +1 -1
  8. package/dist/agent/code-act/type-definition-generator.d.ts.map +1 -1
  9. package/dist/agent/code-act/type-definition-generator.js +0 -1
  10. package/dist/agent/code-act/type-definition-generator.js.map +1 -1
  11. package/dist/agent/gateway-tool-executor.d.ts +36 -1
  12. package/dist/agent/gateway-tool-executor.d.ts.map +1 -1
  13. package/dist/agent/gateway-tool-executor.js +938 -54
  14. package/dist/agent/gateway-tool-executor.js.map +1 -1
  15. package/dist/agent/gateway-tools.md +9 -0
  16. package/dist/agent/managed-agent-runtime-sync.d.ts +36 -0
  17. package/dist/agent/managed-agent-runtime-sync.d.ts.map +1 -0
  18. package/dist/agent/managed-agent-runtime-sync.js +207 -0
  19. package/dist/agent/managed-agent-runtime-sync.js.map +1 -0
  20. package/dist/agent/managed-agent-validation.d.ts +4 -0
  21. package/dist/agent/managed-agent-validation.d.ts.map +1 -0
  22. package/dist/agent/managed-agent-validation.js +84 -0
  23. package/dist/agent/managed-agent-validation.js.map +1 -0
  24. package/dist/agent/os-agent-capabilities.md +400 -0
  25. package/dist/agent/skill-loader.d.ts +2 -0
  26. package/dist/agent/skill-loader.d.ts.map +1 -1
  27. package/dist/agent/skill-loader.js +28 -0
  28. package/dist/agent/skill-loader.js.map +1 -1
  29. package/dist/agent/tool-registry.d.ts.map +1 -1
  30. package/dist/agent/tool-registry.js +66 -0
  31. package/dist/agent/tool-registry.js.map +1 -1
  32. package/dist/agent/types.d.ts +2 -1
  33. package/dist/agent/types.d.ts.map +1 -1
  34. package/dist/agent/types.js.map +1 -1
  35. package/dist/api/agent-handler.d.ts +34 -0
  36. package/dist/api/agent-handler.d.ts.map +1 -0
  37. package/dist/api/agent-handler.js +216 -0
  38. package/dist/api/agent-handler.js.map +1 -0
  39. package/dist/api/graph-api-types.d.ts +4 -0
  40. package/dist/api/graph-api-types.d.ts.map +1 -1
  41. package/dist/api/graph-api.d.ts +2 -2
  42. package/dist/api/graph-api.d.ts.map +1 -1
  43. package/dist/api/graph-api.js +480 -51
  44. package/dist/api/graph-api.js.map +1 -1
  45. package/dist/api/index.d.ts.map +1 -1
  46. package/dist/api/index.js +4 -0
  47. package/dist/api/index.js.map +1 -1
  48. package/dist/api/token-handler.d.ts +1 -0
  49. package/dist/api/token-handler.d.ts.map +1 -1
  50. package/dist/api/token-handler.js +4 -3
  51. package/dist/api/token-handler.js.map +1 -1
  52. package/dist/api/ui-command-handler.d.ts +48 -0
  53. package/dist/api/ui-command-handler.d.ts.map +1 -0
  54. package/dist/api/ui-command-handler.js +160 -0
  55. package/dist/api/ui-command-handler.js.map +1 -0
  56. package/dist/cli/commands/start.d.ts.map +1 -1
  57. package/dist/cli/commands/start.js +127 -1
  58. package/dist/cli/commands/start.js.map +1 -1
  59. package/dist/cli/config/config-manager.d.ts.map +1 -1
  60. package/dist/cli/config/config-manager.js +16 -31
  61. package/dist/cli/config/config-manager.js.map +1 -1
  62. package/dist/cli/runtime/agent-loop-init.d.ts.map +1 -1
  63. package/dist/cli/runtime/agent-loop-init.js +31 -7
  64. package/dist/cli/runtime/agent-loop-init.js.map +1 -1
  65. package/dist/cli/runtime/api-routes-init.d.ts +3 -0
  66. package/dist/cli/runtime/api-routes-init.d.ts.map +1 -1
  67. package/dist/cli/runtime/api-routes-init.js +283 -34
  68. package/dist/cli/runtime/api-routes-init.js.map +1 -1
  69. package/dist/cli/runtime/gateway-init.d.ts +2 -1
  70. package/dist/cli/runtime/gateway-init.d.ts.map +1 -1
  71. package/dist/cli/runtime/gateway-init.js +5 -1
  72. package/dist/cli/runtime/gateway-init.js.map +1 -1
  73. package/dist/connectors/framework/raw-store.d.ts +4 -0
  74. package/dist/connectors/framework/raw-store.d.ts.map +1 -1
  75. package/dist/connectors/framework/raw-store.js +33 -10
  76. package/dist/connectors/framework/raw-store.js.map +1 -1
  77. package/dist/db/agent-store.d.ts +115 -0
  78. package/dist/db/agent-store.d.ts.map +1 -0
  79. package/dist/db/agent-store.js +248 -0
  80. package/dist/db/agent-store.js.map +1 -0
  81. package/dist/db/migrations/agent-activity-validation-columns.d.ts +3 -0
  82. package/dist/db/migrations/agent-activity-validation-columns.d.ts.map +1 -0
  83. package/dist/db/migrations/agent-activity-validation-columns.js +22 -0
  84. package/dist/db/migrations/agent-activity-validation-columns.js.map +1 -0
  85. package/dist/db/migrations/agent-metrics-response-avg.d.ts +3 -0
  86. package/dist/db/migrations/agent-metrics-response-avg.d.ts.map +1 -0
  87. package/dist/db/migrations/agent-metrics-response-avg.js +19 -0
  88. package/dist/db/migrations/agent-metrics-response-avg.js.map +1 -0
  89. package/dist/db/migrations/agent-store-tables.d.ts +3 -0
  90. package/dist/db/migrations/agent-store-tables.d.ts.map +1 -0
  91. package/dist/db/migrations/agent-store-tables.js +59 -0
  92. package/dist/db/migrations/agent-store-tables.js.map +1 -0
  93. package/dist/db/migrations/token-usage-agent-version.d.ts +3 -0
  94. package/dist/db/migrations/token-usage-agent-version.d.ts.map +1 -0
  95. package/dist/db/migrations/token-usage-agent-version.js +16 -0
  96. package/dist/db/migrations/token-usage-agent-version.js.map +1 -0
  97. package/dist/db/migrations/validation-session-tables.d.ts +3 -0
  98. package/dist/db/migrations/validation-session-tables.d.ts.map +1 -0
  99. package/dist/db/migrations/validation-session-tables.js +59 -0
  100. package/dist/db/migrations/validation-session-tables.js.map +1 -0
  101. package/dist/gateways/message-router.d.ts +10 -0
  102. package/dist/gateways/message-router.d.ts.map +1 -1
  103. package/dist/gateways/message-router.js +188 -14
  104. package/dist/gateways/message-router.js.map +1 -1
  105. package/dist/gateways/types.d.ts +1 -1
  106. package/dist/gateways/types.d.ts.map +1 -1
  107. package/dist/multi-agent/agent-process-manager.js +1 -1
  108. package/dist/multi-agent/agent-process-manager.js.map +1 -1
  109. package/dist/multi-agent/conductor-persona.d.ts +13 -0
  110. package/dist/multi-agent/conductor-persona.d.ts.map +1 -0
  111. package/dist/multi-agent/conductor-persona.js +157 -0
  112. package/dist/multi-agent/conductor-persona.js.map +1 -0
  113. package/dist/multi-agent/dashboard-agent-persona.d.ts +1 -1
  114. package/dist/multi-agent/dashboard-agent-persona.d.ts.map +1 -1
  115. package/dist/multi-agent/dashboard-agent-persona.js +7 -3
  116. package/dist/multi-agent/dashboard-agent-persona.js.map +1 -1
  117. package/dist/multi-agent/delegation-manager.d.ts +5 -0
  118. package/dist/multi-agent/delegation-manager.d.ts.map +1 -1
  119. package/dist/multi-agent/delegation-manager.js +37 -0
  120. package/dist/multi-agent/delegation-manager.js.map +1 -1
  121. package/dist/multi-agent/ultrawork.d.ts +3 -0
  122. package/dist/multi-agent/ultrawork.d.ts.map +1 -1
  123. package/dist/multi-agent/ultrawork.js +9 -0
  124. package/dist/multi-agent/ultrawork.js.map +1 -1
  125. package/dist/validation/session-service.d.ts +72 -0
  126. package/dist/validation/session-service.d.ts.map +1 -0
  127. package/dist/validation/session-service.js +298 -0
  128. package/dist/validation/session-service.js.map +1 -0
  129. package/dist/validation/store.d.ts +25 -0
  130. package/dist/validation/store.d.ts.map +1 -0
  131. package/dist/validation/store.js +200 -0
  132. package/dist/validation/store.js.map +1 -0
  133. package/dist/validation/types.d.ts +119 -0
  134. package/dist/validation/types.d.ts.map +1 -0
  135. package/dist/validation/types.js +57 -0
  136. package/dist/validation/types.js.map +1 -0
  137. package/package.json +3 -3
  138. package/public/viewer/js/modules/agents.js +1148 -0
  139. package/public/viewer/js/modules/chat.js +20 -11
  140. package/public/viewer/js/modules/connector-feed.js +35 -0
  141. package/public/viewer/js/modules/dashboard.js +49 -0
  142. package/public/viewer/js/modules/memory.js +32 -0
  143. package/public/viewer/js/modules/settings.js +34 -79
  144. package/public/viewer/js/modules/wiki.js +59 -4
  145. package/public/viewer/js/utils/api.js +70 -0
  146. package/public/viewer/js/utils/dom.js +3 -0
  147. package/public/viewer/js/utils/ui-commands.js +93 -0
  148. package/public/viewer/log-viewer.html +2 -2
  149. package/public/viewer/src/modules/agents.ts +1299 -0
  150. package/public/viewer/src/modules/chat.ts +23 -14
  151. package/public/viewer/src/modules/connector-feed.ts +35 -0
  152. package/public/viewer/src/modules/dashboard.ts +50 -0
  153. package/public/viewer/src/modules/memory.ts +31 -0
  154. package/public/viewer/src/modules/settings.ts +36 -96
  155. package/public/viewer/src/modules/wiki.ts +73 -6
  156. package/public/viewer/src/types/global.d.ts +0 -9
  157. package/public/viewer/src/utils/api.ts +156 -2
  158. package/public/viewer/src/utils/dom.ts +6 -1
  159. package/public/viewer/src/utils/ui-commands.ts +118 -0
  160. package/public/viewer/viewer.css +105 -10
  161. package/public/viewer/viewer.html +1868 -777
  162. package/scripts/generate-gateway-tools.ts +5 -1
  163. package/public/viewer/js/modules/playground.js +0 -148
  164. package/public/viewer/js/modules/skills.js +0 -451
  165. package/public/viewer/src/modules/playground.ts +0 -173
  166. package/public/viewer/src/modules/skills.ts +0 -491
  167. package/templates/playgrounds/cron-workflow-lab.html +0 -1601
  168. package/templates/playgrounds/mama-log-viewer.html +0 -1341
  169. package/templates/playgrounds/skill-lab-playground.html +0 -1625
  170. package/templates/playgrounds/wave-visualizer.html +0 -694
  171. package/templates/skills/playground.md +0 -197
@@ -2,10 +2,7 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <meta
6
- name="viewport"
7
- content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"
8
- />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
9
6
  <title>MAMA - Memory-Augmented Assistant</title>
10
7
 
11
8
  <meta name="theme-color" content="#F9F7F4" />
@@ -39,7 +36,10 @@
39
36
  <script defer src="https://unpkg.com/vis-network@9/dist/vis-network.min.js"></script>
40
37
  <script defer src="https://unpkg.com/lucide@0.460.0"></script>
41
38
  <script defer src="https://cdn.jsdelivr.net/npm/marked@11/marked.min.js"></script>
42
- <script defer src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.5/purify.min.js"></script>
39
+ <script
40
+ defer
41
+ src="https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.2.5/purify.min.js"
42
+ ></script>
43
43
  <!-- Tailwind must load synchronously -->
44
44
  <script src="https://cdn.tailwindcss.com"></script>
45
45
  <script>
@@ -75,7 +75,7 @@
75
75
  },
76
76
  },
77
77
  spacing: {
78
- 'sidebar': 'var(--sidebar-width)',
78
+ sidebar: 'var(--sidebar-width)',
79
79
  },
80
80
  borderRadius: {
81
81
  DEFAULT: 'var(--radius)',
@@ -90,25 +90,25 @@
90
90
  mono: ['var(--font-mono)', 'monospace'],
91
91
  },
92
92
  boxShadow: {
93
- 'soft': '0 4px 20px rgba(19, 19, 19, 0.08)',
94
- 'medium': '0 8px 32px rgba(19, 19, 19, 0.12)',
95
- 'float': '0 12px 40px rgba(19, 19, 19, 0.15)',
96
- 'yellow': '0 4px 20px rgba(255, 206, 0, 0.4)',
93
+ soft: '0 4px 20px rgba(19, 19, 19, 0.08)',
94
+ medium: '0 8px 32px rgba(19, 19, 19, 0.12)',
95
+ float: '0 12px 40px rgba(19, 19, 19, 0.15)',
96
+ yellow: '0 4px 20px rgba(255, 206, 0, 0.4)',
97
97
  },
98
- }
99
- }
100
- }
98
+ },
99
+ },
100
+ };
101
101
  </script>
102
102
  <style>
103
103
  :root {
104
104
  /* MAMA Brand Colors */
105
- --mama-yellow: #FFCE00;
106
- --mama-yellow-hover: #E6B800;
107
- --mama-lavender: #EDDBF7;
108
- --mama-lavender-light: #F5EBF9;
109
- --mama-lavender-dark: #D4C4E0;
105
+ --mama-yellow: #ffce00;
106
+ --mama-yellow-hover: #e6b800;
107
+ --mama-lavender: #eddbf7;
108
+ --mama-lavender-light: #f5ebf9;
109
+ --mama-lavender-dark: #d4c4e0;
110
110
  --mama-black: #131313;
111
- --mama-blush: #FF9999;
111
+ --mama-blush: #ff9999;
112
112
 
113
113
  /* Radius hierarchy: outer > inner > button */
114
114
  --radius-card: 12px;
@@ -117,23 +117,23 @@
117
117
  --radius-pill: 9999px;
118
118
 
119
119
  /* Semantic Colors (HSL for Tailwind) */
120
- --background: 40 20% 97%; /* warm off-white */
121
- --foreground: 0 0% 7%; /* black */
122
- --card: 0 0% 100%; /* white */
120
+ --background: 40 20% 97%; /* warm off-white */
121
+ --foreground: 0 0% 7%; /* black */
122
+ --card: 0 0% 100%; /* white */
123
123
  --card-foreground: 0 0% 7%;
124
- --border: 0 0% 89%; /* light gray */
125
- --muted: 40 15% 95%; /* warm muted */
124
+ --border: 0 0% 89%; /* light gray */
125
+ --muted: 40 15% 95%; /* warm muted */
126
126
  --muted-foreground: 0 0% 45%;
127
- --primary: 48 100% 50%; /* yellow */
127
+ --primary: 48 100% 50%; /* yellow */
128
128
  --primary-foreground: 0 0% 7%;
129
- --secondary: 280 40% 95%; /* subtle lavender accent */
129
+ --secondary: 280 40% 95%; /* subtle lavender accent */
130
130
  --secondary-foreground: 0 0% 7%;
131
131
  --destructive: 0 84% 60%;
132
132
  --destructive-foreground: 0 0% 98%;
133
133
  --success: 142 76% 36%;
134
134
  --warning: 38 92% 50%;
135
135
  --info: 199 89% 48%;
136
- --ring: 48 100% 50%; /* yellow */
136
+ --ring: 48 100% 50%; /* yellow */
137
137
 
138
138
  /* Layout */
139
139
  --radius: 0.75rem;
@@ -148,86 +148,174 @@
148
148
 
149
149
  /* ── Sidebar Navigation ── */
150
150
  #mama-sidebar {
151
- position: fixed; left: 0; top: 0; height: 100vh;
152
- width: var(--sidebar-width); background: #FFFFFF;
153
- border-right: 1px solid #EDE9E1;
154
- display: flex; flex-direction: column; align-items: center;
155
- padding: 16px 0; z-index: 100; transition: width 0.2s ease;
151
+ position: fixed;
152
+ left: 0;
153
+ top: 0;
154
+ height: 100vh;
155
+ width: var(--sidebar-width);
156
+ background: #ffffff;
157
+ border-right: 1px solid #ede9e1;
158
+ display: flex;
159
+ flex-direction: column;
160
+ align-items: center;
161
+ padding: 16px 0;
162
+ z-index: 100;
163
+ transition: width 0.2s ease;
156
164
  overflow: hidden;
157
165
  }
158
166
  #mama-sidebar:hover {
159
167
  width: var(--sidebar-width-expanded);
160
- box-shadow: 4px 0 24px rgba(0,0,0,0.06);
168
+ box-shadow: 4px 0 24px rgba(0, 0, 0, 0.06);
161
169
  }
162
170
  .mama-sidebar-logo {
163
- display: flex; align-items: center; gap: 10px;
164
- width: 100%; padding: 0 14px; margin-bottom: 24px;
165
- text-decoration: none; color: #1A1A1A; white-space: nowrap;
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 10px;
174
+ width: 100%;
175
+ padding: 0 14px;
176
+ margin-bottom: 24px;
177
+ text-decoration: none;
178
+ color: #1a1a1a;
179
+ white-space: nowrap;
180
+ }
181
+ .mama-sidebar-logo img {
182
+ flex-shrink: 0;
166
183
  }
167
- .mama-sidebar-logo img { flex-shrink: 0; }
168
184
  .mama-sidebar-logo span {
169
- font-family: 'Inter', 'Noto Sans KR', sans-serif; font-weight: 700;
170
- font-size: 16px; opacity: 0; transition: opacity 0.15s ease;
185
+ font-family: 'Inter', 'Noto Sans KR', sans-serif;
186
+ font-weight: 700;
187
+ font-size: 16px;
188
+ opacity: 0;
189
+ transition: opacity 0.15s ease;
190
+ }
191
+ #mama-sidebar:hover .mama-sidebar-logo span {
192
+ opacity: 1;
171
193
  }
172
- #mama-sidebar:hover .mama-sidebar-logo span { opacity: 1; }
173
194
 
174
195
  .mama-nav-items {
175
- display: flex; flex-direction: column; gap: 4px;
176
- width: 100%; padding: 0 8px;
196
+ display: flex;
197
+ flex-direction: column;
198
+ gap: 4px;
199
+ width: 100%;
200
+ padding: 0 8px;
177
201
  }
178
202
  .mama-nav-item {
179
- position: relative; display: flex; align-items: center; gap: 12px;
180
- width: 100%; height: 40px; padding: 0 12px;
181
- border-radius: 6px; border: none; background: transparent;
182
- color: #6B6560; font-family: 'Inter', 'Noto Sans KR', sans-serif;
183
- font-size: 13px; font-weight: 600; cursor: pointer;
184
- transition: background 0.15s ease, color 0.15s ease;
185
- white-space: nowrap; overflow: hidden;
186
- }
187
- .mama-nav-item:hover { background: #F9F7F4; color: #1A1A1A; }
203
+ position: relative;
204
+ display: flex;
205
+ align-items: center;
206
+ gap: 12px;
207
+ width: 100%;
208
+ height: 40px;
209
+ padding: 0 12px;
210
+ border-radius: 6px;
211
+ border: none;
212
+ background: transparent;
213
+ color: #6b6560;
214
+ font-family: 'Inter', 'Noto Sans KR', sans-serif;
215
+ font-size: 13px;
216
+ font-weight: 600;
217
+ cursor: pointer;
218
+ transition:
219
+ background 0.15s ease,
220
+ color 0.15s ease;
221
+ white-space: nowrap;
222
+ overflow: hidden;
223
+ }
224
+ .mama-nav-item:hover {
225
+ background: #f9f7f4;
226
+ color: #1a1a1a;
227
+ }
188
228
  .mama-nav-item i,
189
- .mama-nav-item svg { flex-shrink: 0; width: 18px; height: 18px; }
229
+ .mama-nav-item svg {
230
+ flex-shrink: 0;
231
+ width: 18px;
232
+ height: 18px;
233
+ }
190
234
  .mama-nav-item span {
191
- opacity: 0; transition: opacity 0.15s ease;
235
+ opacity: 0;
236
+ transition: opacity 0.15s ease;
237
+ }
238
+ #mama-sidebar:hover .mama-nav-item span {
239
+ opacity: 1;
192
240
  }
193
- #mama-sidebar:hover .mama-nav-item span { opacity: 1; }
194
241
 
195
242
  .mama-nav-item.mama-nav-active {
196
- color: #1A1A1A; background: #FEF9E7;
243
+ color: #1a1a1a;
244
+ background: #fef9e7;
197
245
  }
198
246
  .mama-nav-item.mama-nav-active::before {
199
- content: ''; position: absolute; left: -8px; top: 8px;
200
- width: 3px; height: 24px; border-radius: 0 3px 3px 0;
201
- background: #F5C518;
247
+ content: '';
248
+ position: absolute;
249
+ left: -8px;
250
+ top: 8px;
251
+ width: 3px;
252
+ height: 24px;
253
+ border-radius: 0 3px 3px 0;
254
+ background: #ffce00;
202
255
  }
203
256
 
204
257
  /* ── Mobile bottom tab bar ── */
205
258
  #mama-mobile-tabs {
206
- display: none; position: fixed; bottom: 0; left: 0; right: 0;
207
- height: 64px; background: #FFFFFF; border-top: 1px solid #EDE9E1;
208
- z-index: 100; padding: 0 8px;
209
- align-items: center; justify-content: space-around;
259
+ display: none;
260
+ position: fixed;
261
+ bottom: 0;
262
+ left: 0;
263
+ right: 0;
264
+ height: 64px;
265
+ background: #ffffff;
266
+ border-top: 1px solid #ede9e1;
267
+ z-index: 100;
268
+ padding: 0 8px;
269
+ align-items: center;
270
+ justify-content: space-around;
210
271
  }
211
272
  .mama-mobile-tab {
212
- display: flex; flex-direction: column; align-items: center; gap: 2px;
213
- padding: 8px 12px; border: none; background: transparent;
214
- color: #6B6560; font-size: 10px; font-weight: 600;
215
- font-family: 'Inter', 'Noto Sans KR', sans-serif; cursor: pointer;
273
+ display: flex;
274
+ flex-direction: column;
275
+ align-items: center;
276
+ gap: 2px;
277
+ padding: 8px 12px;
278
+ border: none;
279
+ background: transparent;
280
+ color: #6b6560;
281
+ font-size: 10px;
282
+ font-weight: 600;
283
+ font-family: 'Inter', 'Noto Sans KR', sans-serif;
284
+ cursor: pointer;
216
285
  transition: color 0.15s ease;
217
286
  }
218
287
  .mama-mobile-tab i,
219
- .mama-mobile-tab svg { width: 20px; height: 20px; }
220
- .mama-mobile-tab.mama-nav-active { color: #F5C518; }
288
+ .mama-mobile-tab svg {
289
+ width: 20px;
290
+ height: 20px;
291
+ }
292
+ .mama-mobile-tab.mama-nav-active {
293
+ color: #ffce00;
294
+ position: relative;
295
+ }
296
+ .mama-mobile-tab.mama-nav-active::after {
297
+ content: '';
298
+ position: absolute;
299
+ top: 0;
300
+ left: 50%;
301
+ transform: translateX(-50%);
302
+ width: 24px;
303
+ height: 2px;
304
+ border-radius: 1px;
305
+ background: #ffce00;
306
+ }
221
307
 
222
308
  /* ── Slot container ── */
223
309
  .mama-slots-container {
224
- display: flex; flex-direction: column; gap: 16px;
310
+ display: flex;
311
+ flex-direction: column;
312
+ gap: 16px;
225
313
  min-height: 200px;
226
314
  }
227
315
 
228
316
  /* ── Settings Sections ── */
229
317
  .mama-settings-section {
230
- border-bottom: 1px solid #EDE9E1;
318
+ border-bottom: 1px solid #ede9e1;
231
319
  padding: 16px 0;
232
320
  border-radius: 4px;
233
321
  }
@@ -238,7 +326,7 @@
238
326
  font-family: 'Inter', 'Noto Sans KR', sans-serif;
239
327
  font-size: 16px;
240
328
  font-weight: 600;
241
- color: #1A1A1A;
329
+ color: #1a1a1a;
242
330
  cursor: pointer;
243
331
  display: flex;
244
332
  align-items: center;
@@ -251,24 +339,81 @@
251
339
  width: 100%;
252
340
  text-align: left;
253
341
  }
254
- .mama-settings-heading:hover { color: #3D3630; }
342
+ .mama-settings-heading:hover {
343
+ color: #3d3630;
344
+ }
255
345
  .mama-collapse-icon {
256
346
  font-size: 14px;
257
- color: #9B9389;
347
+ color: #9b9389;
258
348
  transition: transform 0.2s ease;
259
349
  display: inline-block;
260
350
  }
261
- .mama-settings-section.collapsed .mama-settings-body { display: none; }
262
- .mama-settings-section.collapsed .mama-collapse-icon { transform: rotate(-90deg); }
351
+ .mama-settings-section.collapsed .mama-settings-body {
352
+ display: none;
353
+ }
354
+ .mama-settings-section.collapsed .mama-collapse-icon {
355
+ transform: rotate(-90deg);
356
+ }
263
357
 
264
358
  /* ── Responsive ── */
265
359
  @media (max-width: 767px) {
266
- #mama-sidebar { display: none; }
267
- #mama-mobile-tabs { display: flex; }
268
- main.flex-1 { margin-left: 0 !important; padding-bottom: 72px; }
360
+ #mama-sidebar {
361
+ display: none;
362
+ }
363
+ #mama-mobile-tabs {
364
+ display: flex;
365
+ }
366
+ main.flex-1 {
367
+ margin-left: 0 !important;
368
+ padding-bottom: 72px;
369
+ }
370
+ /* Hide right panel chat on mobile — use full-screen chat tab instead */
371
+ #chat-resize-handle-bar {
372
+ display: none !important;
373
+ }
374
+ #chat-panel-wrapper {
375
+ display: none !important;
376
+ }
377
+ #chat-collapsed-bar {
378
+ display: none !important;
379
+ }
380
+ /* Full-screen chat overlay when chat tab is active */
381
+ #chat-panel-wrapper.mobile-chat-active {
382
+ display: flex !important;
383
+ position: fixed;
384
+ top: 0;
385
+ left: 0;
386
+ right: 0;
387
+ bottom: 64px;
388
+ width: 100% !important;
389
+ height: auto !important;
390
+ z-index: 90;
391
+ border-left: none;
392
+ flex-direction: column;
393
+ }
394
+ #chat-panel-wrapper.mobile-chat-active #chat-panel {
395
+ flex: 1;
396
+ display: flex;
397
+ flex-direction: column;
398
+ height: 100% !important;
399
+ min-height: 0;
400
+ overflow: visible;
401
+ }
402
+ #chat-panel-wrapper.mobile-chat-active #chat-messages {
403
+ flex: 1;
404
+ min-height: 0;
405
+ overflow-y: auto;
406
+ }
407
+ #chat-panel-wrapper.mobile-chat-active .chat-input-area,
408
+ #chat-panel-wrapper.mobile-chat-active > div:last-child {
409
+ flex-shrink: 0;
410
+ padding-bottom: max(4px, env(safe-area-inset-bottom));
411
+ }
269
412
  }
270
413
  @media (min-width: 768px) {
271
- main.flex-1 { margin-left: var(--sidebar-width); }
414
+ main.flex-1 {
415
+ margin-left: var(--sidebar-width);
416
+ }
272
417
  }
273
418
  </style>
274
419
 
@@ -279,663 +424,1186 @@
279
424
  <!-- Sidebar Navigation -->
280
425
  <nav id="mama-sidebar">
281
426
  <a href="/" class="mama-sidebar-logo">
282
- <img src="/viewer/icons/mama-icon.svg" alt="MAMA" width="28" height="28">
427
+ <img src="/viewer/icons/mama-icon.svg" alt="MAMA" width="28" height="28" />
283
428
  <span>MAMA</span>
284
429
  </a>
285
430
  <div class="mama-nav-items">
286
- <button class="mama-nav-item mama-nav-active" data-tab="dashboard" onclick="window.switchTab && window.switchTab('dashboard')">
431
+ <button
432
+ class="mama-nav-item mama-nav-active"
433
+ data-tab="dashboard"
434
+ onclick="window.switchTab && window.switchTab('dashboard')"
435
+ >
287
436
  <i data-lucide="layout-dashboard"></i>
288
437
  <span>Dashboard</span>
289
438
  </button>
290
- <button class="mama-nav-item" data-tab="feed" onclick="window.switchTab && window.switchTab('feed')">
439
+ <button
440
+ class="mama-nav-item"
441
+ data-tab="feed"
442
+ onclick="window.switchTab && window.switchTab('feed')"
443
+ >
291
444
  <i data-lucide="rss"></i>
292
445
  <span>Feed</span>
293
446
  </button>
294
- <button class="mama-nav-item" data-tab="wiki" onclick="window.switchTab && window.switchTab('wiki')">
447
+ <button
448
+ class="mama-nav-item"
449
+ data-tab="wiki"
450
+ onclick="window.switchTab && window.switchTab('wiki')"
451
+ >
295
452
  <i data-lucide="book-open"></i>
296
453
  <span>Wiki</span>
297
454
  </button>
298
- <button class="mama-nav-item" data-tab="memory" onclick="window.switchTab && window.switchTab('memory')">
455
+ <button
456
+ class="mama-nav-item"
457
+ data-tab="memory"
458
+ onclick="window.switchTab && window.switchTab('memory')"
459
+ >
299
460
  <i data-lucide="brain"></i>
300
461
  <span>Memory</span>
301
462
  </button>
302
- <button class="mama-nav-item" data-tab="logs" onclick="window.switchTab && window.switchTab('logs')">
463
+ <button
464
+ class="mama-nav-item"
465
+ data-tab="logs"
466
+ onclick="window.switchTab && window.switchTab('logs')"
467
+ >
303
468
  <i data-lucide="terminal"></i>
304
469
  <span>Logs</span>
305
470
  </button>
306
- <button class="mama-nav-item" data-tab="settings" onclick="window.switchTab && window.switchTab('settings')">
471
+ <button
472
+ class="mama-nav-item"
473
+ data-tab="agents"
474
+ onclick="window.switchTab && window.switchTab('agents')"
475
+ >
476
+ <i data-lucide="bot"></i>
477
+ <span>Agents</span>
478
+ </button>
479
+ <button
480
+ class="mama-nav-item"
481
+ data-tab="settings"
482
+ onclick="window.switchTab && window.switchTab('settings')"
483
+ >
307
484
  <i data-lucide="settings"></i>
308
485
  <span>Settings</span>
309
486
  </button>
310
487
  </div>
311
488
  </nav>
312
489
 
313
- <!-- Main Content -->
314
- <main class="flex-1 overflow-hidden flex flex-col">
315
- <!-- Setup tab hidden: runs on port 3848 -->
316
- <div class="tab-content" id="tab-setup" style="display: none">
317
- <div class="setup-container">
318
- <div class="progress-section" id="progress-section" style="display: none">
319
- <div class="progress-header">
320
- <span class="progress-label" id="progress-label">Getting started...</span>
321
- <span class="progress-count" id="progress-count">1 / 9</span>
322
- </div>
323
- <div class="progress-bar" id="progress-bar">
324
- <div class="progress-step"></div>
325
- <div class="progress-step"></div>
326
- <div class="progress-step"></div>
327
- <div class="progress-step"></div>
328
- <div class="progress-step"></div>
329
- <div class="progress-step"></div>
330
- <div class="progress-step"></div>
331
- <div class="progress-step"></div>
332
- <div class="progress-step"></div>
490
+ <!-- Content Area: Main + Chat Panel (SmartStore layout) -->
491
+ <div id="content-area" class="flex-1 flex min-h-0 overflow-hidden">
492
+ <!-- Main Content -->
493
+ <main class="flex-1 overflow-hidden flex flex-col min-w-0">
494
+ <!-- Dashboard Tab -->
495
+ <div class="tab-content" id="tab-dashboard">
496
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
497
+ <div id="dashboard-slots" class="mama-slots-container">
498
+ <div
499
+ id="slots-empty"
500
+ style="padding: 40px; text-align: center; color: #9e9891; font-size: 14px"
501
+ >
502
+ No briefing yet. Run the orchestrator to generate the first report.
503
+ </div>
333
504
  </div>
334
505
  </div>
506
+ </div>
335
507
 
336
- <div class="chat-container" id="setup-chat">
337
- <div class="chat-messages" id="setup-messages"></div>
338
-
339
- <div class="typing-indicator" id="setup-typing">
340
- <div class="typing-dot"></div>
341
- <div class="typing-dot"></div>
342
- <div class="typing-dot"></div>
343
- </div>
344
-
345
- <div class="chat-input-area">
346
- <div class="input-row">
347
- <div class="input-wrapper">
348
- <textarea
349
- class="chat-input"
350
- id="setup-input"
351
- placeholder="Type your message..."
352
- rows="1"
353
- disabled
354
- ></textarea>
355
- </div>
356
- <button
357
- class="btn btn-send"
358
- id="setup-send"
359
- onclick="sendSetupMessage()"
360
- disabled
361
- >
362
- <svg
363
- xmlns="http://www.w3.org/2000/svg"
364
- width="16"
365
- height="16"
366
- viewBox="0 0 24 24"
367
- fill="none"
368
- stroke="currentColor"
369
- stroke-width="2"
370
- stroke-linecap="round"
371
- stroke-linejoin="round"
372
- >
373
- <line x1="22" y1="2" x2="11" y2="13" />
374
- <polygon points="22 2 15 22 11 13 2 9 22 2" />
375
- </svg>
376
- <span>Send</span>
377
- </button>
508
+ <!-- Feed Tab -->
509
+ <div class="tab-content" id="tab-feed">
510
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
511
+ <div id="feed-content">
512
+ <div style="padding: 40px; text-align: center; color: #9e9891; font-size: 14px">
513
+ Loading connectors...
378
514
  </div>
379
515
  </div>
380
516
  </div>
381
517
  </div>
382
- </div>
383
518
 
384
- <!-- Dashboard Tab -->
385
- <div class="tab-content" id="tab-dashboard">
386
- <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
387
- <div id="dashboard-slots" class="mama-slots-container">
388
- <div id="slots-empty" style="padding:40px;text-align:center;color:#9E9891;font-size:14px">
389
- No briefing yet. Run the orchestrator to generate the first report.
519
+ <!-- Wiki Tab -->
520
+ <div class="tab-content" id="tab-wiki">
521
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
522
+ <div id="wiki-content">
523
+ <div style="padding: 40px; text-align: center; color: #9e9891; font-size: 14px">
524
+ Loading wiki...
525
+ </div>
390
526
  </div>
391
527
  </div>
392
528
  </div>
393
- </div>
394
529
 
395
- <!-- Feed Tab -->
396
- <div class="tab-content" id="tab-feed">
397
- <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
398
- <div id="feed-content">
399
- <div style="padding:40px;text-align:center;color:#9E9891;font-size:14px">Loading connectors...</div>
400
- </div>
530
+ <!-- Logs Tab -->
531
+ <div class="tab-content" id="tab-logs">
532
+ <iframe
533
+ id="logs-iframe"
534
+ style="width: 100%; height: 100%; border: none; border-radius: 8px"
535
+ loading="lazy"
536
+ ></iframe>
401
537
  </div>
402
- </div>
403
538
 
404
- <!-- Wiki Tab -->
405
- <div class="tab-content" id="tab-wiki">
406
- <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-4 md:p-6">
407
- <div id="wiki-content">
408
- <div style="padding:40px;text-align:center;color:#9E9891;font-size:14px">Loading wiki...</div>
409
- </div>
539
+ <!-- Agents Tab -->
540
+ <div class="tab-content" id="tab-agents">
541
+ <div class="flex-1 overflow-y-auto p-4" id="agents-content"></div>
410
542
  </div>
411
- </div>
412
-
413
- <!-- Logs Tab -->
414
- <div class="tab-content" id="tab-logs">
415
- <iframe id="logs-iframe" style="width:100%;height:100%;border:none;border-radius:8px;" loading="lazy"></iframe>
416
- </div>
417
-
418
- <!-- Settings Tab -->
419
- <div class="tab-content" id="tab-settings">
420
- <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-2 md:p-3">
421
- <!-- Compact Header -->
422
- <div class="flex items-center justify-between mb-3">
423
- <h1 class="text-sm font-bold text-gray-900">Settings</h1>
424
- <span class="text-[10px] text-gray-400">MAMA OS Configuration</span>
425
- </div>
426
543
 
427
- <!-- Section 1: Connectors -->
428
- <div class="mama-settings-section">
429
- <h3 class="mama-settings-heading" onclick="this.parentElement.classList.toggle('collapsed')">
430
- Connectors <span class="mama-collapse-icon">&#9662;</span>
431
- </h3>
432
- <div class="mama-settings-body">
433
- <p class="text-[10px] text-gray-500 mb-2">Data source connectors for polling external channels. Configure in <code>~/.mama/connectors.json</code>.</p>
434
- <div id="settings-connectors-list" class="space-y-1.5">
435
- <p class="text-[10px] text-gray-400">Loading connectors...</p>
436
- </div>
544
+ <!-- Settings Tab -->
545
+ <div class="tab-content" id="tab-settings">
546
+ <div class="flex-1 flex flex-col min-h-0 overflow-y-auto p-2 md:p-3">
547
+ <!-- Compact Header -->
548
+ <div class="flex items-center justify-between mb-3">
549
+ <h1 class="text-sm font-bold text-gray-900">Settings</h1>
550
+ <span class="text-[10px] text-gray-400">MAMA OS Configuration</span>
437
551
  </div>
438
- </div>
439
-
440
- <!-- Section 3: System -->
441
- <div class="mama-settings-section">
442
- <h3 class="mama-settings-heading" onclick="this.parentElement.classList.toggle('collapsed')">
443
- System <span class="mama-collapse-icon">&#9662;</span>
444
- </h3>
445
- <div class="mama-settings-body">
446
- <div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
447
-
448
- <!-- Left: Health + DB Info -->
449
- <div class="space-y-3">
450
-
451
- <!-- Health Check -->
452
- <section>
453
- <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Health</h2>
454
- <div id="settings-system-health" class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm">
455
- <p class="text-[10px] text-gray-400">Health status will appear here.</p>
456
- </div>
457
- </section>
458
-
459
- <!-- Database Info -->
460
- <section>
461
- <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Database</h2>
462
- <div id="settings-system-db" class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm">
463
- <p class="text-[10px] text-gray-400">Database info will appear here.</p>
464
- </div>
465
- </section>
466
552
 
553
+ <!-- Section 1: Connectors -->
554
+ <div class="mama-settings-section">
555
+ <h3
556
+ class="mama-settings-heading"
557
+ onclick="this.parentElement.classList.toggle('collapsed')"
558
+ >
559
+ Connectors <span class="mama-collapse-icon">&#9662;</span>
560
+ </h3>
561
+ <div class="mama-settings-body">
562
+ <p class="text-[10px] text-gray-500 mb-2">
563
+ Data source connectors for polling external channels. Configure in
564
+ <code>~/.mama/connectors.json</code>.
565
+ </p>
566
+ <div id="settings-connectors-list" class="space-y-1.5">
567
+ <p class="text-[10px] text-gray-400">Loading connectors...</p>
467
568
  </div>
569
+ </div>
570
+ </div>
468
571
 
469
- <!-- Right: Token Usage + Metrics + Cron -->
470
- <div class="space-y-3">
471
-
472
- <!-- Token Usage -->
473
- <section>
474
- <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Token Budget</h2>
475
- <div id="settings-system-tokens" class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm">
476
- <div class="grid grid-cols-2 gap-1.5">
477
- <div>
478
- <label class="block text-[9px] text-gray-500 mb-0.5">Daily Limit</label>
479
- <input type="number" id="settings-token-daily-limit" placeholder="1000000" class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50">
480
- </div>
481
- <div>
482
- <label class="block text-[9px] text-gray-500 mb-0.5">Alert ($)</label>
483
- <input type="number" id="settings-token-alert-threshold" step="0.01" placeholder="5.00" class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50">
484
- </div>
572
+ <!-- Section 3: System -->
573
+ <div class="mama-settings-section">
574
+ <h3
575
+ class="mama-settings-heading"
576
+ onclick="this.parentElement.classList.toggle('collapsed')"
577
+ >
578
+ System <span class="mama-collapse-icon">&#9662;</span>
579
+ </h3>
580
+ <div class="mama-settings-body">
581
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-3">
582
+ <!-- Left: Health + DB Info -->
583
+ <div class="space-y-3">
584
+ <!-- Health Check -->
585
+ <section>
586
+ <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Health</h2>
587
+ <div
588
+ id="settings-system-health"
589
+ class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm"
590
+ >
591
+ <p class="text-[10px] text-gray-400">Health status will appear here.</p>
485
592
  </div>
486
- </div>
487
- </section>
488
-
489
- <!-- Metrics -->
490
- <section>
491
- <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Metrics</h2>
492
- <div class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm">
493
- <div class="grid grid-cols-2 gap-1.5">
494
- <div>
495
- <label class="block text-[9px] text-gray-500 mb-0.5">Enable metrics</label>
496
- <input type="checkbox" id="settings-metrics-enabled" checked class="w-4 h-4 rounded border-gray-300 text-yellow-500 focus:ring-yellow-400">
497
- </div>
498
- <div>
499
- <label class="block text-[9px] text-gray-500 mb-0.5">Retention (days)</label>
500
- <input type="number" id="settings-metrics-retention" min="1" max="90" value="7" class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50">
593
+ </section>
594
+
595
+ <!-- Database Info -->
596
+ <section>
597
+ <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Database</h2>
598
+ <div
599
+ id="settings-system-db"
600
+ class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm"
601
+ >
602
+ <p class="text-[10px] text-gray-400">Database info will appear here.</p>
603
+ </div>
604
+ </section>
605
+ </div>
606
+
607
+ <!-- Right: Token Usage + Metrics + Cron -->
608
+ <div class="space-y-3">
609
+ <!-- Token Usage -->
610
+ <section>
611
+ <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Token Budget</h2>
612
+ <div
613
+ id="settings-system-tokens"
614
+ class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm"
615
+ >
616
+ <div class="grid grid-cols-2 gap-1.5">
617
+ <div>
618
+ <label class="block text-[9px] text-gray-500 mb-0.5"
619
+ >Daily Limit</label
620
+ >
621
+ <input
622
+ type="number"
623
+ id="settings-token-daily-limit"
624
+ placeholder="1000000"
625
+ class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50"
626
+ />
627
+ </div>
628
+ <div>
629
+ <label class="block text-[9px] text-gray-500 mb-0.5">Alert ($)</label>
630
+ <input
631
+ type="number"
632
+ id="settings-token-alert-threshold"
633
+ step="0.01"
634
+ placeholder="5.00"
635
+ class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50"
636
+ />
637
+ </div>
501
638
  </div>
502
639
  </div>
503
- </div>
504
- </section>
640
+ </section>
505
641
 
506
- <!-- Scheduled Jobs -->
507
- <section>
508
- <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Scheduled Jobs</h2>
509
- <div id="settings-cron-container" class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm mb-1.5">
510
- <p class="text-[10px] text-gray-500">Loading...</p>
511
- </div>
512
- <details class="bg-white border border-gray-200 rounded-lg shadow-sm">
513
- <summary class="px-2 py-1.5 text-[10px] font-medium text-gray-600 cursor-pointer hover:bg-gray-50">+ Add New Job</summary>
514
- <div class="p-2 border-t border-gray-100 space-y-1.5" id="settings-cron-form">
642
+ <!-- Metrics -->
643
+ <section>
644
+ <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Metrics</h2>
645
+ <div class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm">
515
646
  <div class="grid grid-cols-2 gap-1.5">
516
- <input type="text" id="settings-cron-name" placeholder="Job Name" aria-label="Cron job name" class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50">
517
- <input type="text" id="settings-cron-expr" placeholder="0 * * * *" aria-label="Cron expression" class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50">
647
+ <div>
648
+ <label class="block text-[9px] text-gray-500 mb-0.5"
649
+ >Enable metrics</label
650
+ >
651
+ <input
652
+ type="checkbox"
653
+ id="settings-metrics-enabled"
654
+ checked
655
+ class="w-4 h-4 rounded border-gray-300 text-yellow-500 focus:ring-yellow-400"
656
+ />
657
+ </div>
658
+ <div>
659
+ <label class="block text-[9px] text-gray-500 mb-0.5"
660
+ >Retention (days)</label
661
+ >
662
+ <input
663
+ type="number"
664
+ id="settings-metrics-retention"
665
+ min="1"
666
+ max="90"
667
+ value="7"
668
+ class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50"
669
+ />
670
+ </div>
518
671
  </div>
519
- <textarea id="settings-cron-prompt" rows="2" placeholder="Task prompt..." aria-label="Cron job task prompt" class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50 resize-none"></textarea>
520
- <button onclick="settingsModule.addCronJob()" class="w-full px-2 py-1 text-[10px] font-medium bg-yellow-400 hover:bg-yellow-300 rounded">Add</button>
521
672
  </div>
522
- </details>
523
- </section>
524
-
673
+ </section>
674
+
675
+ <!-- Scheduled Jobs -->
676
+ <section>
677
+ <h2 class="text-xs font-semibold text-gray-700 mb-1.5">Scheduled Jobs</h2>
678
+ <div
679
+ id="settings-cron-container"
680
+ class="bg-white border border-gray-200 rounded-lg p-2 shadow-sm mb-1.5"
681
+ >
682
+ <p class="text-[10px] text-gray-500">Loading...</p>
683
+ </div>
684
+ <details class="bg-white border border-gray-200 rounded-lg shadow-sm">
685
+ <summary
686
+ class="px-2 py-1.5 text-[10px] font-medium text-gray-600 cursor-pointer hover:bg-gray-50"
687
+ >
688
+ + Add New Job
689
+ </summary>
690
+ <div
691
+ class="p-2 border-t border-gray-100 space-y-1.5"
692
+ id="settings-cron-form"
693
+ >
694
+ <div class="grid grid-cols-2 gap-1.5">
695
+ <input
696
+ type="text"
697
+ id="settings-cron-name"
698
+ placeholder="Job Name"
699
+ aria-label="Cron job name"
700
+ class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50"
701
+ />
702
+ <input
703
+ type="text"
704
+ id="settings-cron-expr"
705
+ placeholder="0 * * * *"
706
+ aria-label="Cron expression"
707
+ class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50"
708
+ />
709
+ </div>
710
+ <textarea
711
+ id="settings-cron-prompt"
712
+ rows="2"
713
+ placeholder="Task prompt..."
714
+ aria-label="Cron job task prompt"
715
+ class="w-full px-1.5 py-0.5 text-[10px] border border-gray-200 rounded bg-gray-50 resize-none"
716
+ ></textarea>
717
+ <button
718
+ onclick="settingsModule.addCronJob()"
719
+ class="w-full px-2 py-1 text-[10px] font-medium bg-yellow-400 hover:bg-yellow-300 rounded"
720
+ >
721
+ Add
722
+ </button>
723
+ </div>
724
+ </details>
725
+ </section>
726
+ </div>
525
727
  </div>
526
728
  </div>
527
729
  </div>
528
- </div>
529
730
 
530
- <!-- Action Buttons -->
531
- <div class="flex items-center justify-between mt-3 pt-3 border-t border-gray-200">
532
- <button onclick="settingsModule.resetForm()" class="px-3 py-1.5 text-xs font-medium text-gray-600 bg-gray-100 hover:bg-gray-200 rounded transition-colors">Reset</button>
533
- <div class="flex items-center gap-2">
534
- <span id="settings-status" class="text-[10px] text-gray-500"></span>
535
- <button onclick="settingsModule.saveAndRestart()" class="px-4 py-1.5 text-xs font-medium text-black bg-yellow-400 hover:bg-yellow-300 rounded transition-colors flex items-center gap-1.5">
536
- <svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z"/><polyline points="17 21 17 13 7 13 7 21"/><polyline points="7 3 7 8 15 8"/></svg>
537
- Save &amp; Restart
731
+ <!-- Action Buttons -->
732
+ <div class="flex items-center justify-between mt-3 pt-3 border-t border-gray-200">
733
+ <button
734
+ onclick="settingsModule.resetForm()"
735
+ class="px-3 py-1.5 text-xs font-medium text-gray-600 bg-gray-100 hover:bg-gray-200 rounded transition-colors"
736
+ >
737
+ Reset
538
738
  </button>
739
+ <div class="flex items-center gap-2">
740
+ <span id="settings-status" class="text-[10px] text-gray-500"></span>
741
+ <button
742
+ onclick="settingsModule.saveAndRestart()"
743
+ class="px-4 py-1.5 text-xs font-medium text-black bg-yellow-400 hover:bg-yellow-300 rounded transition-colors flex items-center gap-1.5"
744
+ >
745
+ <svg
746
+ xmlns="http://www.w3.org/2000/svg"
747
+ width="12"
748
+ height="12"
749
+ viewBox="0 0 24 24"
750
+ fill="none"
751
+ stroke="currentColor"
752
+ stroke-width="2"
753
+ >
754
+ <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
755
+ <polyline points="17 21 17 13 7 13 7 21" />
756
+ <polyline points="7 3 7 8 15 8" />
757
+ </svg>
758
+ Save &amp; Restart
759
+ </button>
760
+ </div>
539
761
  </div>
540
762
  </div>
541
763
  </div>
542
- </div>
543
764
 
544
- <div class="tab-content" id="tab-memory">
545
- <div class="flex-1 flex flex-col min-h-0">
546
- <!-- Compact Filter Bar (full width, top) -->
547
- <div class="flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-white">
548
- <!-- Toggle sidebar -->
549
- <button
550
- id="checkpoint-sidebar-toggle"
551
- class="w-7 h-7 bg-white border border-gray-200 rounded flex items-center justify-center text-gray-500 hover:bg-gray-100 hover:text-gray-700"
552
- onclick="toggleCheckpointSidebar()"
553
- title="Toggle Decision List"
554
- aria-controls="checkpoint-sidebar"
555
- aria-expanded="true"
556
- >
557
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
558
- <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
559
- <line x1="3" y1="9" x2="21" y2="9" />
560
- <line x1="9" y1="21" x2="9" y2="9" />
561
- </svg>
562
- </button>
563
- <!-- Stats (compact inline) -->
564
- <span id="memory-stats-total" style="font-family:Inter,'Noto Sans KR',sans-serif;font-size:13px;font-weight:600;color:#1A1A1A">-</span>
565
- <span style="font-size:11px;color:#9E9891">decisions</span>
566
- <span style="color:#EDE9E1">|</span>
567
- <span id="memory-stats-week" style="font-size:11px;color:#6B6560">- this week</span>
568
- <!-- Spacer -->
569
- <div style="flex:1"></div>
570
- <!-- Topic filter -->
571
- <input list="topic-filter-list" class="px-2 py-1 bg-white border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-yellow-400" id="topic-filter" placeholder="Filter topics..." oninput="filterByTopic(this.value)" style="width:160px">
572
- <datalist id="topic-filter-list"></datalist>
573
- <select class="px-2 py-1 bg-white border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-yellow-400" id="outcome-filter" onchange="filterByOutcome(this.value)" style="display:none">
574
- <option value="">All Outcomes</option>
575
- <option value="success">Success</option>
576
- <option value="failed">Failed</option>
577
- <option value="partial">Partial</option>
578
- <option value="pending">Pending</option>
579
- </select>
580
- <!-- Search -->
581
- <input type="text" class="px-2 py-1 bg-white border border-gray-200 rounded text-xs placeholder-gray-400 focus:outline-none focus:ring-1 focus:ring-yellow-400" id="search-input" placeholder="Search content..." onkeyup="handleGraphSearch(event)" style="width:160px" />
582
- <button onclick="clearFilters()" class="px-2 py-1 text-[10px] text-gray-400 hover:text-gray-600">Clear</button>
583
- <!-- Export (collapsed) -->
584
- <details style="position:relative">
585
- <summary style="font-size:10px;color:#9E9891;cursor:pointer;padding:4px 8px;border:1px solid #EDE9E1;border-radius:4px">Export</summary>
586
- <div style="position:absolute;right:0;top:100%;background:#fff;border:1px solid #EDE9E1;border-radius:4px;padding:4px;z-index:10;display:flex;gap:4px;margin-top:2px">
587
- <button onclick="exportDecisions('json')" style="font-size:10px;padding:2px 8px;background:#F5F2ED;border:none;border-radius:2px;cursor:pointer">JSON</button>
588
- <button onclick="exportDecisions('markdown')" style="font-size:10px;padding:2px 8px;background:#F5F2ED;border:none;border-radius:2px;cursor:pointer">MD</button>
589
- <button onclick="exportDecisions('csv')" style="font-size:10px;padding:2px 8px;background:#F5F2ED;border:none;border-radius:2px;cursor:pointer">CSV</button>
590
- </div>
591
- </details>
592
- </div>
593
-
594
- <!-- Main content area: Sidebar + Graph -->
595
- <div class="flex-1 flex min-h-0 relative">
596
- <!-- Decision List Sidebar -->
597
- <aside id="checkpoint-sidebar" class="checkpoint-sidebar w-64 border-r border-gray-200 bg-white flex flex-col min-h-0 overflow-hidden" style="min-width: 180px; max-width: 400px;">
598
- <div class="p-2 border-b border-gray-200 flex items-center justify-between">
599
- <span style="font-family:Inter,'Noto Sans KR',sans-serif;font-size:12px;font-weight:600;color:#1A1A1A">Decisions</span>
600
- <span id="decision-list-count" style="font-size:10px;color:#9E9891">0</span>
601
- </div>
602
- <div class="flex-1 overflow-y-auto" id="decision-list" style="padding:4px">
603
- <div style="padding:16px;text-align:center;color:#9E9891;font-size:11px">Loading decisions...</div>
604
- </div>
605
- </aside>
606
-
607
- <!-- Resize handle for sidebar -->
608
- <div id="sidebar-resize-handle" class="w-1 bg-transparent hover:bg-yellow-400 cursor-col-resize transition-colors flex-shrink-0" title="Drag to resize"></div>
609
-
610
- <!-- Graph Container -->
611
- <div class="flex-1 flex flex-col relative min-h-0" id="graph-container">
612
- <div class="px-3 py-1.5 text-[10px] text-gray-500 border-b border-gray-200 bg-white/80" id="graph-stats" style="display:none"></div>
613
-
614
- <div id="graph-canvas" class="flex-1 w-full h-full min-h-[400px]" style="background: #FAFAFA;"></div>
615
-
616
- <div class="absolute inset-0 flex flex-col items-center justify-center bg-white/95" id="graph-loading">
617
- <div class="w-6 h-6 border-2 border-gray-400 border-t-transparent rounded-full animate-spin"></div>
618
- <span class="mt-2 text-xs text-gray-500">Loading graph...</span>
619
- </div>
765
+ <div class="tab-content" id="tab-memory">
766
+ <div class="flex-1 flex flex-col min-h-0">
767
+ <!-- Compact Filter Bar (full width, top) -->
768
+ <div class="flex items-center gap-2 px-3 py-2 border-b border-gray-200 bg-white">
769
+ <!-- Toggle sidebar -->
770
+ <button
771
+ id="checkpoint-sidebar-toggle"
772
+ class="w-7 h-7 bg-white border border-gray-200 rounded flex items-center justify-center text-gray-500 hover:bg-gray-100 hover:text-gray-700"
773
+ onclick="toggleCheckpointSidebar()"
774
+ title="Toggle Decision List"
775
+ aria-controls="checkpoint-sidebar"
776
+ aria-expanded="true"
777
+ >
778
+ <svg
779
+ xmlns="http://www.w3.org/2000/svg"
780
+ width="14"
781
+ height="14"
782
+ viewBox="0 0 24 24"
783
+ fill="none"
784
+ stroke="currentColor"
785
+ stroke-width="2"
786
+ stroke-linecap="round"
787
+ stroke-linejoin="round"
788
+ >
789
+ <rect x="3" y="3" width="18" height="18" rx="2" ry="2" />
790
+ <line x1="3" y1="9" x2="21" y2="9" />
791
+ <line x1="9" y1="21" x2="9" y2="9" />
792
+ </svg>
793
+ </button>
794
+ <!-- Stats (compact inline) -->
795
+ <span
796
+ id="memory-stats-total"
797
+ style="
798
+ font-family: Inter, 'Noto Sans KR', sans-serif;
799
+ font-size: 13px;
800
+ font-weight: 600;
801
+ color: #1a1a1a;
802
+ "
803
+ >-</span
804
+ >
805
+ <span style="font-size: 11px; color: #9e9891">decisions</span>
806
+ <span style="color: #ede9e1">|</span>
807
+ <span id="memory-stats-week" style="font-size: 11px; color: #6b6560"
808
+ >- this week</span
809
+ >
810
+ <!-- Spacer -->
811
+ <div style="flex: 1"></div>
812
+ <!-- Topic filter -->
813
+ <input
814
+ list="topic-filter-list"
815
+ class="px-2 py-1 bg-white border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-yellow-400"
816
+ id="topic-filter"
817
+ placeholder="Filter topics..."
818
+ oninput="filterByTopic(this.value)"
819
+ style="width: 160px"
820
+ />
821
+ <datalist id="topic-filter-list"></datalist>
822
+ <select
823
+ class="px-2 py-1 bg-white border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-yellow-400"
824
+ id="outcome-filter"
825
+ onchange="filterByOutcome(this.value)"
826
+ style="display: none"
827
+ >
828
+ <option value="">All Outcomes</option>
829
+ <option value="success">Success</option>
830
+ <option value="failed">Failed</option>
831
+ <option value="partial">Partial</option>
832
+ <option value="pending">Pending</option>
833
+ </select>
834
+ <!-- Search -->
835
+ <input
836
+ type="text"
837
+ class="px-2 py-1 bg-white border border-gray-200 rounded text-xs placeholder-gray-400 focus:outline-none focus:ring-1 focus:ring-yellow-400"
838
+ id="search-input"
839
+ placeholder="Search content..."
840
+ onkeyup="handleGraphSearch(event)"
841
+ style="width: 160px"
842
+ />
843
+ <button
844
+ onclick="clearFilters()"
845
+ class="px-2 py-1 text-[10px] text-gray-400 hover:text-gray-600"
846
+ >
847
+ Clear
848
+ </button>
849
+ <!-- Export (collapsed) -->
850
+ <details style="position: relative">
851
+ <summary
852
+ style="
853
+ font-size: 10px;
854
+ color: #9e9891;
855
+ cursor: pointer;
856
+ padding: 4px 8px;
857
+ border: 1px solid #ede9e1;
858
+ border-radius: 4px;
859
+ "
860
+ >
861
+ Export
862
+ </summary>
863
+ <div
864
+ style="
865
+ position: absolute;
866
+ right: 0;
867
+ top: 100%;
868
+ background: #fff;
869
+ border: 1px solid #ede9e1;
870
+ border-radius: 4px;
871
+ padding: 4px;
872
+ z-index: 10;
873
+ display: flex;
874
+ gap: 4px;
875
+ margin-top: 2px;
876
+ "
877
+ >
878
+ <button
879
+ onclick="exportDecisions('json')"
880
+ style="
881
+ font-size: 10px;
882
+ padding: 2px 8px;
883
+ background: #f5f2ed;
884
+ border: none;
885
+ border-radius: 2px;
886
+ cursor: pointer;
887
+ "
888
+ >
889
+ JSON
890
+ </button>
891
+ <button
892
+ onclick="exportDecisions('markdown')"
893
+ style="
894
+ font-size: 10px;
895
+ padding: 2px 8px;
896
+ background: #f5f2ed;
897
+ border: none;
898
+ border-radius: 2px;
899
+ cursor: pointer;
900
+ "
901
+ >
902
+ MD
903
+ </button>
904
+ <button
905
+ onclick="exportDecisions('csv')"
906
+ style="
907
+ font-size: 10px;
908
+ padding: 2px 8px;
909
+ background: #f5f2ed;
910
+ border: none;
911
+ border-radius: 2px;
912
+ cursor: pointer;
913
+ "
914
+ >
915
+ CSV
916
+ </button>
917
+ </div>
918
+ </details>
620
919
  </div>
621
- </div>
622
920
 
623
- <div class="legend-panel absolute bottom-16 right-4 w-48 bg-white/95 backdrop-blur-sm border border-gray-200 rounded-lg shadow-lg p-3 z-10" id="legend-panel">
624
- <button class="absolute top-2 right-2 p-1 rounded hover:bg-gray-100 transition-colors" onclick="toggleLegend()">
625
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
626
- <line x1="18" y1="6" x2="6" y2="18" />
627
- <line x1="6" y1="6" x2="18" y2="18" />
628
- </svg>
629
- </button>
630
- <h4 class="text-xs font-semibold text-gray-700 text-gray-700 flex items-center gap-1.5 mb-3">
631
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
632
- <circle cx="12" cy="12" r="10" />
633
- <line x1="12" y1="16" x2="12" y2="12" />
634
- <line x1="12" y1="8" x2="12.01" y2="8" />
635
- </svg>
636
- Legend
637
- </h4>
638
- <div class="legend-content space-y-3">
639
- <div>
640
- <div class="text-[10px] font-semibold text-gray-600 uppercase tracking-wide mb-1.5">Edge Types</div>
641
- <div class="flex items-center gap-2 text-xs text-gray-700 mb-1">
642
- <span class="w-6 h-0.5" style="background: #666666;"></span>
643
- <span>supersedes</span>
644
- </div>
645
- <div class="flex items-center gap-2 text-xs text-gray-700 mb-1">
646
- <span class="w-6 h-0.5" style="background: #B8860B; border-bottom: 2px dashed #B8860B;"></span>
647
- <span>builds_on</span>
648
- </div>
649
- <div class="flex items-center gap-2 text-xs text-gray-700 mb-1">
650
- <span class="w-6 h-0.5" style="background: #DC143C; border-bottom: 2px dashed #DC143C;"></span>
651
- <span>debates</span>
652
- </div>
653
- <div class="flex items-center gap-2 text-xs text-gray-700">
654
- <span class="w-6 h-1" style="background: #6B4C9A;"></span>
655
- <span>synthesizes</span>
921
+ <!-- Main content area: Sidebar + Graph -->
922
+ <div class="flex-1 flex min-h-0 relative">
923
+ <!-- Decision List Sidebar -->
924
+ <aside
925
+ id="checkpoint-sidebar"
926
+ class="checkpoint-sidebar w-64 border-r border-gray-200 bg-white flex flex-col min-h-0 overflow-hidden"
927
+ style="min-width: 180px; max-width: 400px"
928
+ >
929
+ <div class="p-2 border-b border-gray-200 flex items-center justify-between">
930
+ <span
931
+ style="
932
+ font-family: Inter, 'Noto Sans KR', sans-serif;
933
+ font-size: 12px;
934
+ font-weight: 600;
935
+ color: #1a1a1a;
936
+ "
937
+ >Decisions</span
938
+ >
939
+ <span id="decision-list-count" style="font-size: 10px; color: #9e9891">0</span>
656
940
  </div>
657
- </div>
658
- <div>
659
- <div class="text-[10px] font-semibold text-gray-500 text-gray-600 uppercase tracking-wide mb-1.5">Node Size</div>
660
- <div class="flex items-center gap-2 text-xs text-gray-600 text-gray-600 mb-1">
661
- <span class="w-2 h-2 rounded-full bg-indigo-500"></span>
662
- <span>1-2 connections</span>
941
+ <div class="flex-1 overflow-y-auto" id="decision-list" style="padding: 4px">
942
+ <div style="padding: 16px; text-align: center; color: #9e9891; font-size: 11px">
943
+ Loading decisions...
944
+ </div>
663
945
  </div>
664
- <div class="flex items-center gap-2 text-xs text-gray-600 text-gray-600 mb-1">
665
- <span class="w-3 h-3 rounded-full bg-indigo-500"></span>
666
- <span>3-5 connections</span>
946
+ </aside>
947
+
948
+ <!-- Resize handle for sidebar -->
949
+ <div
950
+ id="sidebar-resize-handle"
951
+ class="w-1 bg-transparent hover:bg-yellow-400 cursor-col-resize transition-colors flex-shrink-0"
952
+ title="Drag to resize"
953
+ ></div>
954
+
955
+ <!-- Graph Container -->
956
+ <div class="flex-1 flex flex-col relative min-h-0" id="graph-container">
957
+ <div
958
+ class="px-3 py-1.5 text-[10px] text-gray-500 border-b border-gray-200 bg-white/80"
959
+ id="graph-stats"
960
+ style="display: none"
961
+ ></div>
962
+
963
+ <div
964
+ id="graph-canvas"
965
+ class="flex-1 w-full h-full min-h-[400px]"
966
+ style="background: #fafafa"
967
+ ></div>
968
+
969
+ <div
970
+ class="absolute inset-0 flex flex-col items-center justify-center bg-white/95"
971
+ id="graph-loading"
972
+ >
973
+ <div
974
+ class="w-6 h-6 border-2 border-gray-400 border-t-transparent rounded-full animate-spin"
975
+ ></div>
976
+ <span class="mt-2 text-xs text-gray-500">Loading graph...</span>
667
977
  </div>
668
- <div class="flex items-center gap-2 text-xs text-gray-600 text-gray-600">
669
- <span class="w-4 h-4 rounded-full bg-indigo-500"></span>
670
- <span>6+ connections</span>
978
+
979
+ <div
980
+ class="legend-panel absolute bottom-16 right-4 w-48 bg-white/95 backdrop-blur-sm border border-gray-200 rounded-lg shadow-lg p-3 z-10"
981
+ id="legend-panel"
982
+ >
983
+ <button
984
+ class="absolute top-2 right-2 p-1 rounded hover:bg-gray-100 transition-colors"
985
+ onclick="toggleLegend()"
986
+ >
987
+ <svg
988
+ xmlns="http://www.w3.org/2000/svg"
989
+ width="14"
990
+ height="14"
991
+ viewBox="0 0 24 24"
992
+ fill="none"
993
+ stroke="currentColor"
994
+ stroke-width="2"
995
+ stroke-linecap="round"
996
+ stroke-linejoin="round"
997
+ >
998
+ <line x1="18" y1="6" x2="6" y2="18" />
999
+ <line x1="6" y1="6" x2="18" y2="18" />
1000
+ </svg>
1001
+ </button>
1002
+ <h4
1003
+ class="text-xs font-semibold text-gray-700 text-gray-700 flex items-center gap-1.5 mb-3"
1004
+ >
1005
+ <svg
1006
+ xmlns="http://www.w3.org/2000/svg"
1007
+ width="14"
1008
+ height="14"
1009
+ viewBox="0 0 24 24"
1010
+ fill="none"
1011
+ stroke="currentColor"
1012
+ stroke-width="2"
1013
+ stroke-linecap="round"
1014
+ stroke-linejoin="round"
1015
+ >
1016
+ <circle cx="12" cy="12" r="10" />
1017
+ <line x1="12" y1="16" x2="12" y2="12" />
1018
+ <line x1="12" y1="8" x2="12.01" y2="8" />
1019
+ </svg>
1020
+ Legend
1021
+ </h4>
1022
+ <div class="legend-content space-y-3">
1023
+ <div>
1024
+ <div
1025
+ class="text-[10px] font-semibold text-gray-600 uppercase tracking-wide mb-1.5"
1026
+ >
1027
+ Edge Types
1028
+ </div>
1029
+ <div class="flex items-center gap-2 text-xs text-gray-700 mb-1">
1030
+ <span class="w-6 h-0.5" style="background: #666666"></span>
1031
+ <span>supersedes</span>
1032
+ </div>
1033
+ <div class="flex items-center gap-2 text-xs text-gray-700 mb-1">
1034
+ <span
1035
+ class="w-6 h-0.5"
1036
+ style="background: #b8860b; border-bottom: 2px dashed #b8860b"
1037
+ ></span>
1038
+ <span>builds_on</span>
1039
+ </div>
1040
+ <div class="flex items-center gap-2 text-xs text-gray-700 mb-1">
1041
+ <span
1042
+ class="w-6 h-0.5"
1043
+ style="background: #dc143c; border-bottom: 2px dashed #dc143c"
1044
+ ></span>
1045
+ <span>debates</span>
1046
+ </div>
1047
+ <div class="flex items-center gap-2 text-xs text-gray-700">
1048
+ <span class="w-6 h-1" style="background: #6b4c9a"></span>
1049
+ <span>synthesizes</span>
1050
+ </div>
1051
+ </div>
1052
+ <div>
1053
+ <div
1054
+ class="text-[10px] font-semibold text-gray-500 text-gray-600 uppercase tracking-wide mb-1.5"
1055
+ >
1056
+ Node Size
1057
+ </div>
1058
+ <div
1059
+ class="flex items-center gap-2 text-xs text-gray-600 text-gray-600 mb-1"
1060
+ >
1061
+ <span class="w-2 h-2 rounded-full bg-indigo-500"></span>
1062
+ <span>1-2 connections</span>
1063
+ </div>
1064
+ <div
1065
+ class="flex items-center gap-2 text-xs text-gray-600 text-gray-600 mb-1"
1066
+ >
1067
+ <span class="w-3 h-3 rounded-full bg-indigo-500"></span>
1068
+ <span>3-5 connections</span>
1069
+ </div>
1070
+ <div class="flex items-center gap-2 text-xs text-gray-600 text-gray-600">
1071
+ <span class="w-4 h-4 rounded-full bg-indigo-500"></span>
1072
+ <span>6+ connections</span>
1073
+ </div>
1074
+ </div>
1075
+ </div>
671
1076
  </div>
1077
+ <button
1078
+ class="legend-toggle absolute bottom-4 right-4 px-3 py-1.5 text-xs font-medium bg-gray-100 bg-white hover:bg-gray-200 hover:bg-gray-100 border border-gray-200 border-gray-300 rounded-lg transition-colors flex items-center gap-1.5 z-10"
1079
+ onclick="toggleLegend()"
1080
+ >
1081
+ <svg
1082
+ xmlns="http://www.w3.org/2000/svg"
1083
+ width="14"
1084
+ height="14"
1085
+ viewBox="0 0 24 24"
1086
+ fill="none"
1087
+ stroke="currentColor"
1088
+ stroke-width="2"
1089
+ stroke-linecap="round"
1090
+ stroke-linejoin="round"
1091
+ >
1092
+ <circle cx="12" cy="12" r="10" />
1093
+ <line x1="12" y1="16" x2="12" y2="12" />
1094
+ <line x1="12" y1="8" x2="12.01" y2="8" />
1095
+ </svg>
1096
+ Legend
1097
+ </button>
672
1098
  </div>
1099
+ <!-- close graph-container -->
673
1100
  </div>
1101
+ <!-- close flex-1 flex relative -->
674
1102
  </div>
675
- <button class="legend-toggle absolute bottom-4 right-4 px-3 py-1.5 text-xs font-medium bg-gray-100 bg-white hover:bg-gray-200 hover:bg-gray-100 border border-gray-200 border-gray-300 rounded-lg transition-colors flex items-center gap-1.5 z-10" onclick="toggleLegend()">
676
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
677
- <circle cx="12" cy="12" r="10" />
678
- <line x1="12" y1="16" x2="12" y2="12" />
679
- <line x1="12" y1="8" x2="12.01" y2="8" />
680
- </svg>
681
- Legend
682
- </button>
683
1103
  </div>
684
- </div>
685
1104
 
686
- <div class="floating-panel fixed w-[360px] min-w-[280px] max-w-[90vw] h-[400px] min-h-[200px] max-h-[80vh] bg-white/95 backdrop-blur-sm border border-gray-200 rounded-xl shadow-2xl z-[1000] flex-col overflow-hidden resize" id="decision-detail-modal" style="top: 100px; right: 20px; left: auto; transform: none;">
687
- <div class="flex items-center justify-between px-3 py-2 border-b border-gray-200 bg-gray-50/80 cursor-move select-none" id="detail-modal-header">
688
- <h3 class="text-sm font-semibold text-gray-900 truncate flex-1" id="detail-topic">Decision Detail</h3>
689
- <button class="p-1 rounded hover:bg-gray-200 transition-colors text-gray-500 hover:text-gray-700 ml-2" onclick="closeDetailModal()">
690
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
691
- </button>
692
- </div>
693
- <div class="flex-1 p-3 overflow-y-auto space-y-3 text-xs">
694
- <div>
695
- <div class="font-semibold text-gray-500 uppercase tracking-wide mb-1">Decision</div>
696
- <div class="text-gray-900 leading-relaxed markdown-content" id="detail-decision"></div>
697
- </div>
698
- <div>
699
- <div class="font-semibold text-gray-500 uppercase tracking-wide mb-1 cursor-pointer hover:text-gray-700" onclick="toggleReasoning()">
700
- <span id="reasoning-arrow">▶</span> Reasoning
701
- </div>
702
- <div class="text-gray-900 leading-relaxed markdown-content hidden" id="detail-reasoning"></div>
703
- </div>
704
- <div class="flex items-center gap-3">
705
- <div class="flex items-center gap-2">
706
- <span class="font-semibold text-gray-500 uppercase">Outcome:</span>
707
- <select class="px-1.5 py-0.5 bg-white border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-yellow-400" id="detail-outcome-select">
708
- <option value="PENDING">PENDING</option>
709
- <option value="SUCCESS">SUCCESS</option>
710
- <option value="FAILED">FAILED</option>
711
- <option value="PARTIAL">PARTIAL</option>
712
- </select>
713
- <button class="px-1.5 py-0.5 bg-gray-100 hover:bg-gray-200 rounded transition-colors" onclick="saveOutcome()">Save</button>
714
- <span id="outcome-status"></span>
715
- </div>
1105
+ <div
1106
+ class="floating-panel fixed w-[360px] min-w-[280px] max-w-[90vw] h-[400px] min-h-[200px] max-h-[80vh] bg-white/95 backdrop-blur-sm border border-gray-200 rounded-xl shadow-2xl z-[1000] flex-col overflow-hidden resize"
1107
+ id="decision-detail-modal"
1108
+ style="top: 100px; right: 20px; left: auto; transform: none"
1109
+ >
1110
+ <div
1111
+ class="flex items-center justify-between px-3 py-2 border-b border-gray-200 bg-gray-50/80 cursor-move select-none"
1112
+ id="detail-modal-header"
1113
+ >
1114
+ <h3 class="text-sm font-semibold text-gray-900 truncate flex-1" id="detail-topic">
1115
+ Decision Detail
1116
+ </h3>
1117
+ <button
1118
+ class="p-1 rounded hover:bg-gray-200 transition-colors text-gray-500 hover:text-gray-700 ml-2"
1119
+ onclick="closeDetailModal()"
1120
+ >
1121
+ <svg
1122
+ xmlns="http://www.w3.org/2000/svg"
1123
+ width="14"
1124
+ height="14"
1125
+ viewBox="0 0 24 24"
1126
+ fill="none"
1127
+ stroke="currentColor"
1128
+ stroke-width="2"
1129
+ stroke-linecap="round"
1130
+ stroke-linejoin="round"
1131
+ >
1132
+ <line x1="18" y1="6" x2="6" y2="18" />
1133
+ <line x1="6" y1="6" x2="18" y2="18" />
1134
+ </svg>
1135
+ </button>
716
1136
  </div>
717
- <div class="flex gap-4">
1137
+ <div class="flex-1 p-3 overflow-y-auto space-y-3 text-xs">
718
1138
  <div>
719
- <span class="font-semibold text-gray-500 uppercase">Confidence:</span>
720
- <span class="text-gray-900" id="detail-confidence"></span>
1139
+ <div class="font-semibold text-gray-500 uppercase tracking-wide mb-1">Decision</div>
1140
+ <div
1141
+ class="text-gray-900 leading-relaxed markdown-content"
1142
+ id="detail-decision"
1143
+ ></div>
721
1144
  </div>
722
1145
  <div>
723
- <span class="font-semibold text-gray-500 uppercase">Created:</span>
724
- <span class="text-gray-900" id="detail-created"></span>
1146
+ <div
1147
+ class="font-semibold text-gray-500 uppercase tracking-wide mb-1 cursor-pointer hover:text-gray-700"
1148
+ onclick="toggleReasoning()"
1149
+ >
1150
+ <span id="reasoning-arrow">▶</span> Reasoning
1151
+ </div>
1152
+ <div
1153
+ class="text-gray-900 leading-relaxed markdown-content hidden"
1154
+ id="detail-reasoning"
1155
+ ></div>
725
1156
  </div>
726
- </div>
727
- <div>
728
- <div class="font-semibold text-gray-500 uppercase tracking-wide mb-1">Similar Decisions</div>
729
- <div class="text-gray-900" id="detail-similar">
730
- <span class="text-gray-400">Searching...</span>
1157
+ <div class="flex items-center gap-3">
1158
+ <div class="flex items-center gap-2">
1159
+ <span class="font-semibold text-gray-500 uppercase">Outcome:</span>
1160
+ <select
1161
+ class="px-1.5 py-0.5 bg-white border border-gray-200 rounded text-xs focus:outline-none focus:ring-1 focus:ring-yellow-400"
1162
+ id="detail-outcome-select"
1163
+ >
1164
+ <option value="PENDING">PENDING</option>
1165
+ <option value="SUCCESS">SUCCESS</option>
1166
+ <option value="FAILED">FAILED</option>
1167
+ <option value="PARTIAL">PARTIAL</option>
1168
+ </select>
1169
+ <button
1170
+ class="px-1.5 py-0.5 bg-gray-100 hover:bg-gray-200 rounded transition-colors"
1171
+ onclick="saveOutcome()"
1172
+ >
1173
+ Save
1174
+ </button>
1175
+ <span id="outcome-status"></span>
1176
+ </div>
731
1177
  </div>
732
- </div>
733
- </div>
734
- <div id="decision-resize-handle" class="decision-resize-handle" aria-label="Resize decision modal"></div>
735
- </div>
736
-
737
- <!-- Skills Marketplace Tab -->
738
- <div class="tab-content" id="tab-skills">
739
- <div class="flex-1 flex flex-col w-full max-w-6xl mx-auto p-4 h-full overflow-y-auto">
740
- <!-- Search Bar -->
741
- <div class="mb-4">
742
- <input id="skills-search" type="text"
743
- class="w-full bg-white border border-gray-200 rounded-lg px-4 py-2 text-sm text-gray-900 placeholder-gray-400 focus:outline-none focus:border-mama-yellow focus:ring-2 focus:ring-yellow-200"
744
- placeholder="Search skills...">
745
- </div>
746
-
747
- <!-- URL Install -->
748
- <div class="flex gap-2 mb-4">
749
- <input id="skills-url-input" type="text"
750
- class="flex-1 bg-white border border-gray-200 rounded-lg px-3 py-1.5 text-sm text-gray-900 placeholder-gray-400 focus:outline-none focus:border-mama-yellow focus:ring-2 focus:ring-yellow-200"
751
- placeholder="https://github.com/owner/repo">
752
- <button id="skills-url-install-btn"
753
- class="px-4 py-1.5 text-xs rounded-lg bg-mama-yellow text-mama-black font-medium hover:bg-mama-yellow-hover whitespace-nowrap">
754
- Install URL
755
- </button>
756
- </div>
757
-
758
- <!-- Filter Bar -->
759
- <div id="skills-filter-bar" class="flex gap-2 mb-4 flex-wrap">
760
- <button data-filter="all" class="px-3 py-1 text-xs rounded-full bg-mama-yellow text-mama-black font-medium">All</button>
761
- <button data-filter="installed" class="px-3 py-1 text-xs rounded-full bg-white text-gray-600">Installed</button>
762
- <button data-filter="mama" class="px-3 py-1 text-xs rounded-full bg-white text-gray-600">MAMA</button>
763
- <button data-filter="cowork" class="px-3 py-1 text-xs rounded-full bg-white text-gray-600">Cowork</button>
764
- <button data-filter="external" class="px-3 py-1 text-xs rounded-full bg-white text-gray-600">External</button>
765
- </div>
766
-
767
- <!-- Skills Content (rendered by skills.js) -->
768
- <div id="skills-content">
769
- <div class="flex items-center justify-center p-12 text-gray-500">
770
- <p class="text-sm">Loading skills...</p>
1178
+ <div class="flex gap-4">
1179
+ <div>
1180
+ <span class="font-semibold text-gray-500 uppercase">Confidence:</span>
1181
+ <span class="text-gray-900" id="detail-confidence"></span>
1182
+ </div>
1183
+ <div>
1184
+ <span class="font-semibold text-gray-500 uppercase">Created:</span>
1185
+ <span class="text-gray-900" id="detail-created"></span>
1186
+ </div>
1187
+ </div>
1188
+ <div>
1189
+ <div class="font-semibold text-gray-500 uppercase tracking-wide mb-1">
1190
+ Similar Decisions
1191
+ </div>
1192
+ <div class="text-gray-900" id="detail-similar">
1193
+ <span class="text-gray-400">Searching...</span>
1194
+ </div>
771
1195
  </div>
772
1196
  </div>
1197
+ <div
1198
+ id="decision-resize-handle"
1199
+ class="decision-resize-handle"
1200
+ aria-label="Resize decision modal"
1201
+ ></div>
773
1202
  </div>
774
- </div>
775
-
776
- <!-- Skill Detail Modal -->
777
- <div id="skill-detail-modal" class="hidden fixed inset-0 z-50 flex items-center justify-center bg-black/50">
778
- <div class="bg-gray-900 border border-gray-700 rounded-xl w-full max-w-2xl max-h-[80vh] flex flex-col shadow-2xl mx-4">
779
- <div class="flex items-center justify-between p-4 border-b border-gray-700">
780
- <h2 class="text-lg font-bold text-white">Skill Details</h2>
781
- <button onclick="window.skillsModule && window.skillsModule.closeDetail()"
782
- class="text-gray-400 hover:text-white text-xl leading-none">&times;</button>
783
- </div>
784
- <div id="skill-detail-content" class="p-4 overflow-y-auto flex-1 text-sm text-gray-300">
785
- </div>
786
- </div>
787
- </div>
788
-
789
-
790
- <!-- Floating Chat (global, visible on all tabs) -->
791
- <div id="floating-chat" class="fixed bottom-6 right-6 z-50">
792
- <button id="chat-bubble"
793
- class="w-14 h-14 rounded-full bg-mama-yellow shadow-yellow flex items-center justify-center hover:scale-110 transition-transform cursor-pointer relative"
794
- title="Open Chat">
795
- <svg class="w-6 h-6 text-mama-black" fill="none" stroke="currentColor" viewBox="0 0 24 24">
796
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
797
- </svg>
798
- <span id="chat-badge" class="hidden absolute -top-1 -right-1 w-4 h-4 bg-red-500 rounded-full border-2 border-white animate-pulse"></span>
799
- </button>
800
-
801
- <div id="chat-panel" class="chat-panel-closed absolute bottom-[4.5rem] right-0 w-[400px] h-[560px] bg-white rounded-2xl shadow-float border border-gray-300 flex flex-col overflow-hidden">
802
- <!-- Header -->
803
- <div id="chat-header" class="h-12 bg-mama-yellow/20 flex items-center px-4 border-b border-gray-300 shrink-0">
804
- <svg class="w-5 h-5 text-mama-black mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
805
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
806
- </svg>
807
- <span class="font-bold text-mama-black text-sm">MAMA Chat</span>
808
- <div class="ml-2 flex items-center gap-1 text-xs text-gray-500" id="chat-status">
809
- <span class="status-indicator w-2 h-2 rounded-full bg-red-500"></span>
810
- <span>Not connected</span>
1203
+ </main>
1204
+
1205
+ <!-- Chat Resize Handle (SmartStore pattern) -->
1206
+ <div
1207
+ id="chat-resize-handle-bar"
1208
+ class="w-1 flex-shrink-0 cursor-col-resize bg-transparent hover:bg-mama-yellow/30 active:bg-mama-yellow/50 transition-colors chat-resize-visible"
1209
+ ></div>
1210
+
1211
+ <!-- Right Chat Panel (always visible, SmartStore Layout pattern) -->
1212
+ <div
1213
+ id="chat-panel-wrapper"
1214
+ class="flex-shrink-0 border-l border-gray-200"
1215
+ style="width: 380px; background: #f5f3ef"
1216
+ >
1217
+ <div id="chat-panel" class="flex flex-col">
1218
+ <!-- Header (SmartStore ChatPanel pattern) -->
1219
+ <div
1220
+ id="chat-header"
1221
+ class="px-4 py-3 border-b border-gray-200 flex items-center gap-2.5 bg-white flex-shrink-0"
1222
+ >
1223
+ <div
1224
+ class="w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0"
1225
+ style="background: #ffce00"
1226
+ >
1227
+ <svg
1228
+ class="w-4 h-4 text-white"
1229
+ fill="none"
1230
+ stroke="currentColor"
1231
+ stroke-width="2"
1232
+ viewBox="0 0 24 24"
1233
+ >
1234
+ <path
1235
+ stroke-linecap="round"
1236
+ stroke-linejoin="round"
1237
+ d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"
1238
+ />
1239
+ </svg>
811
1240
  </div>
812
- <div class="ml-auto flex items-center gap-1">
813
- <button class="p-1.5 rounded-lg hover:bg-gray-100 transition-colors" id="chat-tts-toggle" onclick="toggleTTS()" title="TTS Off">
814
- <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
815
- <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
816
- <path d="M19.07 4.93a10 10 0 0 1 0 14.14M15.54 8.46a5 5 0 0 1 0 7.07" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
817
- </svg>
818
- </button>
819
- <div class="relative">
820
- <span class="text-[10px] text-gray-500 cursor-pointer px-1 py-0.5 hover:bg-gray-100 rounded" id="tts-speed-label" onclick="toggleTTSPopup()">1.8x</span>
821
- <div class="absolute bottom-full mb-2 left-1/2 -translate-x-1/2 bg-white border border-gray-300 rounded-lg p-3 shadow-lg hidden" id="tts-speed-popup">
822
- <div class="text-center text-sm font-semibold mb-2" id="tts-speed-value">1.8x</div>
823
- <input type="range" class="w-full accent-mama-yellow" id="tts-speed-slider" min="0.5" max="2.0" step="0.1" value="1.8" oninput="updateTTSSpeed(this.value)" />
824
- </div>
1241
+ <div class="flex-1 min-w-0">
1242
+ <div class="flex items-center gap-1.5">
1243
+ <span class="text-[13px] font-semibold text-mama-black">MAMA Chat</span>
1244
+ <span
1245
+ id="chat-tab-indicator"
1246
+ class="text-[10px] bg-gray-100 text-gray-500 px-1.5 py-0.5 rounded-md font-medium"
1247
+ >Dashboard</span
1248
+ >
1249
+ <span
1250
+ class="status-indicator w-1.5 h-1.5 rounded-full bg-gray-300 flex-shrink-0"
1251
+ id="chat-status"
1252
+ ></span>
825
1253
  </div>
826
- <button class="p-1.5 rounded-lg hover:bg-gray-100 transition-colors" id="chat-handsfree-toggle" onclick="toggleHandsFree()" title="Hands-free Off">
827
- <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
828
- <circle cx="12" cy="12" r="2" stroke-width="2"/>
829
- <path d="M16.24 7.76a6 6 0 0 1 0 8.49m-8.48-.01a6 6 0 0 1 0-8.49m11.31-2.82a10 10 0 0 1 0 14.14m-14.14 0a10 10 0 0 1 0-14.14" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
1254
+ </div>
1255
+ <div class="flex items-center gap-1 flex-shrink-0">
1256
+ <button
1257
+ class="w-7 h-7 rounded-lg flex items-center justify-center text-gray-400 hover:text-red-500 hover:bg-red-50 transition-colors"
1258
+ id="chat-clear-btn"
1259
+ onclick="clearChatHistory()"
1260
+ title="Clear chat"
1261
+ style="display: none"
1262
+ >
1263
+ <svg
1264
+ class="w-3.5 h-3.5"
1265
+ fill="none"
1266
+ stroke="currentColor"
1267
+ stroke-width="1.5"
1268
+ viewBox="0 0 24 24"
1269
+ >
1270
+ <path
1271
+ stroke-linecap="round"
1272
+ stroke-linejoin="round"
1273
+ d="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0"
1274
+ />
830
1275
  </svg>
831
1276
  </button>
832
- <button id="chat-close" class="p-1.5 rounded-lg hover:bg-red-100 transition-colors text-gray-500 hover:text-red-600" title="Close">
833
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
834
- <line x1="18" y1="6" x2="6" y2="18" stroke-width="2" stroke-linecap="round"/>
835
- <line x1="6" y1="6" x2="18" y2="18" stroke-width="2" stroke-linecap="round"/>
1277
+ <button
1278
+ id="chat-collapse-btn"
1279
+ class="w-7 h-7 rounded-lg flex items-center justify-center text-gray-400 hover:text-mama-black hover:bg-gray-100 transition-colors"
1280
+ title="Collapse chat"
1281
+ onclick="toggleChatPanel()"
1282
+ >
1283
+ <svg
1284
+ class="w-4 h-4"
1285
+ fill="none"
1286
+ stroke="currentColor"
1287
+ stroke-width="2"
1288
+ viewBox="0 0 24 24"
1289
+ >
1290
+ <path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7" />
836
1291
  </svg>
837
1292
  </button>
838
1293
  </div>
839
1294
  </div>
840
1295
 
841
- <!-- Messages -->
842
- <div class="flex-1 overflow-y-auto p-3 flex flex-col gap-3 min-h-0" id="chat-messages">
843
- <div class="flex-1 flex flex-col items-center justify-center text-center p-6 text-gray-400">
844
- <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="mb-3 opacity-50">
845
- <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z" />
846
- </svg>
847
- <p class="text-sm">Start a conversation</p>
1296
+ <!-- Messages (SmartStore pattern: flex-1 overflow-auto) -->
1297
+ <div
1298
+ class="flex-1 overflow-auto p-4 space-y-3"
1299
+ id="chat-messages"
1300
+ style="background: #f5f3ef"
1301
+ >
1302
+ <div class="flex flex-col items-center justify-center h-full text-center px-6">
1303
+ <div
1304
+ class="w-12 h-12 rounded-lg bg-mama-yellow/10 flex items-center justify-center mb-4"
1305
+ >
1306
+ <svg
1307
+ class="w-6 h-6 text-mama-yellow"
1308
+ fill="none"
1309
+ stroke="currentColor"
1310
+ stroke-width="1.5"
1311
+ viewBox="0 0 24 24"
1312
+ >
1313
+ <path
1314
+ stroke-linecap="round"
1315
+ stroke-linejoin="round"
1316
+ d="M9.813 15.904L9 18.75l-.813-2.846a4.5 4.5 0 00-3.09-3.09L2.25 12l2.846-.813a4.5 4.5 0 003.09-3.09L9 5.25l.813 2.846a4.5 4.5 0 003.09 3.09L15.75 12l-2.846.813a4.5 4.5 0 00-3.09 3.09z"
1317
+ />
1318
+ </svg>
1319
+ </div>
1320
+ <p class="text-[13px] text-gray-500 font-medium">Ask MAMA anything</p>
1321
+ <p class="text-[11px] text-gray-400 mt-1">
1322
+ Current page context is sent automatically
1323
+ </p>
848
1324
  </div>
849
1325
  </div>
850
1326
 
851
- <!-- Input -->
852
- <div class="border-t border-gray-300 p-3 space-y-2 shrink-0">
1327
+ <!-- Attachment preview -->
1328
+ <div
1329
+ id="chat-attachment-preview"
1330
+ class="hidden px-3 pt-2 border-t border-gray-200 flex items-center gap-2 flex-wrap flex-shrink-0"
1331
+ >
1332
+ <img
1333
+ id="chat-attachment-thumb"
1334
+ class="w-12 h-12 object-cover rounded-lg border border-gray-200"
1335
+ src="data:,"
1336
+ alt="preview"
1337
+ />
1338
+ <span
1339
+ id="chat-attachment-name"
1340
+ class="text-[11px] text-gray-400 truncate max-w-[100px]"
1341
+ ></span>
1342
+ <button
1343
+ class="text-gray-400 hover:text-red-500"
1344
+ onclick="clearAttachment()"
1345
+ title="Remove"
1346
+ >
1347
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
1348
+ <line x1="18" y1="6" x2="6" y2="18" stroke-width="2" stroke-linecap="round" />
1349
+ <line x1="6" y1="6" x2="18" y2="18" stroke-width="2" stroke-linecap="round" />
1350
+ </svg>
1351
+ </button>
1352
+ </div>
1353
+
1354
+ <!-- Input (SmartStore ChatPanel pattern: inline row) -->
1355
+ <div class="p-3 border-t border-gray-200 bg-white flex-shrink-0">
1356
+ <input
1357
+ type="file"
1358
+ id="chat-file-input"
1359
+ class="hidden"
1360
+ accept="image/*,.pdf,.doc,.docx,.txt,.csv,.xls,.xlsx,.md,.json,.html,.htm,.xml,.zip,.gz,.ppt,.pptx"
1361
+ onchange="handleFileSelect(event)"
1362
+ />
853
1363
  <div class="flex gap-2">
854
- <textarea
855
- class="flex-1 px-3 py-2 border border-gray-300 rounded-lg bg-gray-50 text-mama-black placeholder-gray-500 resize-none focus:outline-none focus:ring-2 focus:ring-mama-yellow disabled:opacity-50 disabled:cursor-not-allowed text-sm"
856
- id="chat-input" placeholder="Type your message..." rows="1" disabled
857
- ></textarea>
858
1364
  <button
859
- class="px-3 py-2 bg-mama-yellow hover:bg-mama-yellow-hover disabled:bg-gray-400 text-mama-black rounded-lg font-medium transition-colors disabled:cursor-not-allowed flex items-center"
860
- id="chat-send" onclick="sendChatMessage()" disabled>
861
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
862
- <line x1="22" y1="2" x2="11" y2="13" stroke-width="2" stroke-linecap="round"/>
863
- <polygon points="22 2 15 22 11 13 2 9 22 2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
1365
+ class="w-9 h-9 rounded-lg flex items-center justify-center flex-shrink-0 bg-gray-100 text-gray-400 hover:text-mama-black hover:bg-gray-200 transition-all"
1366
+ id="chat-attach"
1367
+ onclick="document.getElementById('chat-file-input').click()"
1368
+ title="Attach file"
1369
+ >
1370
+ <svg
1371
+ class="w-4 h-4"
1372
+ fill="none"
1373
+ stroke="currentColor"
1374
+ stroke-width="1.5"
1375
+ viewBox="0 0 24 24"
1376
+ >
1377
+ <path
1378
+ stroke-linecap="round"
1379
+ stroke-linejoin="round"
1380
+ d="M18.375 12.739l-7.693 7.693a4.5 4.5 0 01-6.364-6.364l10.94-10.94A3 3 0 1119.5 7.372L8.552 18.32m.009-.01l-.01.01m5.699-9.941l-7.81 7.81a1.5 1.5 0 002.112 2.13"
1381
+ />
864
1382
  </svg>
865
1383
  </button>
866
- </div>
867
- <!-- Attachment preview -->
868
- <div id="chat-attachment-preview" class="hidden flex items-center gap-2 px-2 py-1 bg-gray-50 rounded-lg border border-gray-300">
869
- <img id="chat-attachment-thumb" class="w-10 h-10 object-cover rounded" src="" alt="preview" />
870
- <span id="chat-attachment-name" class="text-xs text-gray-600 truncate max-w-[120px]"></span>
871
- <button class="text-gray-400 hover:text-red-500 ml-auto" onclick="clearAttachment()" title="Remove">
872
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><line x1="18" y1="6" x2="6" y2="18" stroke-width="2" stroke-linecap="round"/><line x1="6" y1="6" x2="18" y2="18" stroke-width="2" stroke-linecap="round"/></svg>
873
- </button>
874
- </div>
875
- <div class="flex items-center gap-1">
876
- <input type="file" id="chat-file-input" class="hidden" accept="image/*,.pdf,.doc,.docx,.txt,.csv,.xls,.xlsx,.md,.json,.html,.htm,.xml,.zip,.gz,.ppt,.pptx" onchange="handleFileSelect(event)" />
877
- <button class="p-1.5 rounded-lg hover:bg-gray-100 transition-colors" id="chat-attach" onclick="document.getElementById('chat-file-input').click()" title="Attach file">
878
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
879
- <path d="M21.44 11.05l-9.19 9.19a6 6 0 01-8.49-8.49l9.19-9.19a4 4 0 015.66 5.66l-9.2 9.19a2 2 0 01-2.83-2.83l8.49-8.48" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
1384
+ <button
1385
+ class="w-9 h-9 rounded-lg flex items-center justify-center flex-shrink-0 bg-gray-100 text-gray-400 hover:text-mama-black hover:bg-gray-200 transition-all relative"
1386
+ id="chat-mic"
1387
+ onclick="toggleVoiceInput()"
1388
+ title="Voice input"
1389
+ >
1390
+ <svg
1391
+ class="w-4 h-4"
1392
+ fill="none"
1393
+ stroke="currentColor"
1394
+ stroke-width="1.5"
1395
+ viewBox="0 0 24 24"
1396
+ >
1397
+ <path
1398
+ stroke-linecap="round"
1399
+ stroke-linejoin="round"
1400
+ d="M12 18.75a6 6 0 006-6v-1.5m-6 7.5a6 6 0 01-6-6v-1.5m6 7.5v3.75m-3.75 0h7.5M12 15.75a3 3 0 01-3-3V4.5a3 3 0 116 0v8.25a3 3 0 01-3 3z"
1401
+ />
880
1402
  </svg>
1403
+ <span
1404
+ class="recording-dot absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full hidden"
1405
+ ></span>
881
1406
  </button>
882
- <button class="p-1.5 rounded-lg hover:bg-gray-100 transition-colors relative" id="chat-mic" onclick="toggleVoiceInput()" title="Voice input">
883
- <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
884
- <path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3Z" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
885
- <path d="M19 10v2a7 7 0 0 1-14 0v-2" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
886
- <line x1="12" y1="19" x2="12" y2="22" stroke-width="2" stroke-linecap="round"/>
1407
+ <input
1408
+ type="text"
1409
+ id="chat-input"
1410
+ placeholder="Type your message..."
1411
+ class="flex-1 bg-gray-100 border-0 rounded-lg px-3.5 py-2 text-[13px] text-mama-black placeholder:text-gray-400 focus:outline-none focus:ring-2 focus:ring-mama-yellow/20 transition-all disabled:opacity-50"
1412
+ disabled
1413
+ />
1414
+ <button
1415
+ id="chat-send"
1416
+ onclick="sendChatMessage()"
1417
+ disabled
1418
+ class="w-9 h-9 rounded-lg bg-mama-yellow text-mama-black flex items-center justify-center flex-shrink-0 hover:bg-mama-yellow-hover disabled:opacity-40 transition-all"
1419
+ >
1420
+ <svg
1421
+ class="w-4 h-4"
1422
+ fill="none"
1423
+ stroke="currentColor"
1424
+ stroke-width="2"
1425
+ viewBox="0 0 24 24"
1426
+ >
1427
+ <path
1428
+ stroke-linecap="round"
1429
+ stroke-linejoin="round"
1430
+ d="M6 12L3.269 3.126A59.768 59.768 0 0121.485 12 59.77 59.77 0 013.27 20.876L5.999 12zm0 0h7.5"
1431
+ />
887
1432
  </svg>
888
- <span class="recording-dot absolute top-0 right-0 w-2 h-2 bg-red-500 rounded-full hidden"></span>
889
1433
  </button>
890
- <span class="text-[10px] text-gray-400 ml-1">Enter to send, Shift+Enter for new line</span>
891
1434
  </div>
892
1435
  </div>
893
- <div id="chat-resize-handle" class="chat-resize-handle" aria-label="Resize chat panel"></div>
894
1436
  </div>
895
1437
  </div>
896
- </main>
1438
+
1439
+ <!-- Chat Collapsed Bar (visible when chat is collapsed) -->
1440
+ <div
1441
+ id="chat-collapsed-bar"
1442
+ class="hidden flex-shrink-0 border-l border-gray-200 bg-white flex flex-col items-center py-3 w-10"
1443
+ >
1444
+ <button
1445
+ onclick="toggleChatPanel()"
1446
+ class="w-8 h-8 rounded-lg flex items-center justify-center text-gray-400 hover:text-mama-yellow hover:bg-mama-yellow/10 transition-colors"
1447
+ title="Open chat"
1448
+ >
1449
+ <svg
1450
+ class="w-4 h-4"
1451
+ fill="none"
1452
+ stroke="currentColor"
1453
+ strokeWidth="1.5"
1454
+ viewBox="0 0 24 24"
1455
+ >
1456
+ <path
1457
+ d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"
1458
+ stroke-width="2"
1459
+ stroke-linecap="round"
1460
+ stroke-linejoin="round"
1461
+ />
1462
+ </svg>
1463
+ </button>
1464
+ <span
1465
+ id="chat-badge-collapsed"
1466
+ class="hidden w-2 h-2 bg-red-500 rounded-full mt-1 animate-pulse"
1467
+ ></span>
1468
+ </div>
1469
+ </div>
1470
+ <!-- /content-area -->
897
1471
 
898
1472
  <!-- Mobile Bottom Tab Bar -->
899
1473
  <div id="mama-mobile-tabs">
900
- <button class="mama-mobile-tab mama-nav-active" data-tab="dashboard" onclick="window.switchTab && window.switchTab('dashboard')">
1474
+ <button
1475
+ class="mama-mobile-tab mama-nav-active"
1476
+ data-tab="dashboard"
1477
+ onclick="window.switchTab && window.switchTab('dashboard')"
1478
+ >
901
1479
  <i data-lucide="layout-dashboard"></i>
902
- <span>Dashboard</span>
1480
+ <span>Home</span>
903
1481
  </button>
904
- <button class="mama-mobile-tab" data-tab="feed" onclick="window.switchTab && window.switchTab('feed')">
905
- <i data-lucide="rss"></i>
906
- <span>Feed</span>
1482
+ <button
1483
+ class="mama-mobile-tab"
1484
+ data-tab="chat"
1485
+ onclick="window.switchTab && window.switchTab('chat')"
1486
+ >
1487
+ <i data-lucide="message-circle"></i>
1488
+ <span>Chat</span>
907
1489
  </button>
908
- <button class="mama-mobile-tab" data-tab="wiki" onclick="window.switchTab && window.switchTab('wiki')">
909
- <i data-lucide="book-open"></i>
910
- <span>Wiki</span>
1490
+ <button
1491
+ class="mama-mobile-tab"
1492
+ data-tab="agents"
1493
+ onclick="window.switchTab && window.switchTab('agents')"
1494
+ >
1495
+ <i data-lucide="bot"></i>
1496
+ <span>Agents</span>
911
1497
  </button>
912
- <button class="mama-mobile-tab" data-tab="memory" onclick="window.switchTab && window.switchTab('memory')">
1498
+ <button
1499
+ class="mama-mobile-tab"
1500
+ data-tab="memory"
1501
+ onclick="window.switchTab && window.switchTab('memory')"
1502
+ >
913
1503
  <i data-lucide="brain"></i>
914
1504
  <span>Memory</span>
915
1505
  </button>
916
- <button class="mama-mobile-tab" data-tab="logs" onclick="window.switchTab && window.switchTab('logs')">
917
- <i data-lucide="terminal"></i>
918
- <span>Logs</span>
919
- </button>
920
- <button class="mama-mobile-tab" data-tab="settings" onclick="window.switchTab && window.switchTab('settings')">
921
- <i data-lucide="settings"></i>
922
- <span>Settings</span>
1506
+ <button
1507
+ class="mama-mobile-tab"
1508
+ data-tab="more"
1509
+ onclick="window.toggleMobileMore && window.toggleMobileMore()"
1510
+ >
1511
+ <i data-lucide="menu"></i>
1512
+ <span>More</span>
923
1513
  </button>
924
1514
  </div>
1515
+ <!-- Mobile "More" overflow menu -->
1516
+ <div
1517
+ id="mama-mobile-more"
1518
+ style="
1519
+ display: none;
1520
+ position: fixed;
1521
+ bottom: 64px;
1522
+ left: 0;
1523
+ right: 0;
1524
+ background: #fff;
1525
+ border-top: 1px solid #ede9e1;
1526
+ z-index: 99;
1527
+ padding: 8px;
1528
+ "
1529
+ >
1530
+ <div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 4px">
1531
+ <button
1532
+ class="mama-mobile-tab"
1533
+ data-tab="feed"
1534
+ onclick="window.switchTab && window.switchTab('feed')"
1535
+ >
1536
+ <i data-lucide="rss"></i><span>Feed</span>
1537
+ </button>
1538
+ <button
1539
+ class="mama-mobile-tab"
1540
+ data-tab="wiki"
1541
+ onclick="window.switchTab && window.switchTab('wiki')"
1542
+ >
1543
+ <i data-lucide="book-open"></i><span>Wiki</span>
1544
+ </button>
1545
+ <button
1546
+ class="mama-mobile-tab"
1547
+ data-tab="logs"
1548
+ onclick="window.switchTab && window.switchTab('logs')"
1549
+ >
1550
+ <i data-lucide="scroll-text"></i><span>Logs</span>
1551
+ </button>
1552
+ <button
1553
+ class="mama-mobile-tab"
1554
+ data-tab="settings"
1555
+ onclick="window.switchTab && window.switchTab('settings')"
1556
+ >
1557
+ <i data-lucide="settings"></i><span>Settings</span>
1558
+ </button>
1559
+ </div>
1560
+ </div>
925
1561
 
926
- <div class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center" id="save-decision-modal">
927
- <div class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-xl shadow-2xl w-[90%] max-w-md max-h-[90vh] flex flex-col overflow-hidden">
928
- <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 border-gray-300 bg-gray-50 bg-gray-50/50">
929
- <h3 class="flex items-center gap-2 text-base font-semibold text-gray-900 text-mama-black">
930
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1562
+ <div
1563
+ class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center"
1564
+ id="save-decision-modal"
1565
+ >
1566
+ <div
1567
+ class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-xl shadow-2xl w-[90%] max-w-md max-h-[90vh] flex flex-col overflow-hidden"
1568
+ >
1569
+ <div
1570
+ class="flex items-center justify-between px-4 py-3 border-b border-gray-200 border-gray-300 bg-gray-50 bg-gray-50/50"
1571
+ >
1572
+ <h3
1573
+ class="flex items-center gap-2 text-base font-semibold text-gray-900 text-mama-black"
1574
+ >
1575
+ <svg
1576
+ xmlns="http://www.w3.org/2000/svg"
1577
+ width="18"
1578
+ height="18"
1579
+ viewBox="0 0 24 24"
1580
+ fill="none"
1581
+ stroke="currentColor"
1582
+ stroke-width="2"
1583
+ stroke-linecap="round"
1584
+ stroke-linejoin="round"
1585
+ >
931
1586
  <path d="M19 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11l5 5v11a2 2 0 0 1-2 2z" />
932
1587
  <polyline points="17 21 17 13 7 13 7 21" />
933
1588
  <polyline points="7 3 7 8 15 8" />
934
1589
  </svg>
935
1590
  Save Decision
936
1591
  </h3>
937
- <button class="p-1.5 rounded-lg hover:bg-gray-200 hover:bg-gray-100 transition-colors text-gray-500 hover:text-gray-700 hover:text-mama-black" onclick="hideSaveDecisionForm()">
938
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1592
+ <button
1593
+ class="p-1.5 rounded-lg hover:bg-gray-200 hover:bg-gray-100 transition-colors text-gray-500 hover:text-gray-700 hover:text-mama-black"
1594
+ onclick="hideSaveDecisionForm()"
1595
+ >
1596
+ <svg
1597
+ xmlns="http://www.w3.org/2000/svg"
1598
+ width="18"
1599
+ height="18"
1600
+ viewBox="0 0 24 24"
1601
+ fill="none"
1602
+ stroke="currentColor"
1603
+ stroke-width="2"
1604
+ stroke-linecap="round"
1605
+ stroke-linejoin="round"
1606
+ >
939
1607
  <line x1="18" y1="6" x2="6" y2="18" />
940
1608
  <line x1="6" y1="6" x2="18" y2="18" />
941
1609
  </svg>
@@ -943,7 +1611,9 @@
943
1611
  </div>
944
1612
  <div class="p-4 space-y-4">
945
1613
  <div class="flex flex-col gap-2">
946
- <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-topic">Topic</label>
1614
+ <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-topic"
1615
+ >Topic</label
1616
+ >
947
1617
  <input
948
1618
  type="text"
949
1619
  class="w-full px-3 py-2 bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-lg text-sm text-gray-900 text-mama-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-mama-yellow transition-colors"
@@ -953,7 +1623,9 @@
953
1623
  />
954
1624
  </div>
955
1625
  <div class="flex flex-col gap-2">
956
- <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-decision">Decision</label>
1626
+ <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-decision"
1627
+ >Decision</label
1628
+ >
957
1629
  <textarea
958
1630
  class="w-full px-3 py-2 bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-lg text-sm text-gray-900 text-mama-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-mama-yellow transition-colors resize-y min-h-[60px]"
959
1631
  id="save-decision"
@@ -963,7 +1635,9 @@
963
1635
  ></textarea>
964
1636
  </div>
965
1637
  <div class="flex flex-col gap-2">
966
- <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-reasoning">Reasoning</label>
1638
+ <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-reasoning"
1639
+ >Reasoning</label
1640
+ >
967
1641
  <textarea
968
1642
  class="w-full px-3 py-2 bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-lg text-sm text-gray-900 text-mama-black placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-mama-yellow transition-colors resize-y min-h-[100px]"
969
1643
  id="save-reasoning"
@@ -973,7 +1647,9 @@
973
1647
  ></textarea>
974
1648
  </div>
975
1649
  <div class="flex flex-col gap-2">
976
- <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-confidence">Confidence (0.0 - 1.0)</label>
1650
+ <label class="text-sm font-medium text-gray-900 text-mama-black" for="save-confidence"
1651
+ >Confidence (0.0 - 1.0)</label
1652
+ >
977
1653
  <input
978
1654
  type="number"
979
1655
  class="w-28 px-3 py-2 bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-lg text-sm text-gray-900 text-mama-black focus:outline-none focus:ring-2 focus:ring-mama-yellow transition-colors"
@@ -985,31 +1661,74 @@
985
1661
  />
986
1662
  </div>
987
1663
  </div>
988
- <div class="flex justify-end gap-2 px-4 py-3 border-t border-gray-200 border-gray-300 bg-gray-50 bg-gray-50/50">
989
- <button class="px-4 py-2 text-sm font-medium text-gray-700 text-gray-700 bg-gray-100 bg-white hover:bg-gray-200 hover:bg-gray-100 rounded-lg transition-colors" onclick="hideSaveDecisionForm()">Cancel</button>
1664
+ <div
1665
+ class="flex justify-end gap-2 px-4 py-3 border-t border-gray-200 border-gray-300 bg-gray-50 bg-gray-50/50"
1666
+ >
1667
+ <button
1668
+ class="px-4 py-2 text-sm font-medium text-gray-700 text-gray-700 bg-gray-100 bg-white hover:bg-gray-200 hover:bg-gray-100 rounded-lg transition-colors"
1669
+ onclick="hideSaveDecisionForm()"
1670
+ >
1671
+ Cancel
1672
+ </button>
990
1673
  <button
991
1674
  class="px-4 py-2 text-sm font-medium text-white bg-mama-yellow hover:bg-mama-yellow-hover rounded-lg transition-colors"
992
1675
  id="save-form-submit"
993
1676
  onclick="submitSaveDecision()"
994
- >Save</button>
1677
+ >
1678
+ Save
1679
+ </button>
995
1680
  </div>
996
1681
  <div id="save-form-status" class="px-5 pb-4 text-center text-sm"></div>
997
1682
  </div>
998
1683
  </div>
999
1684
 
1000
- <div class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center" id="tunnel-setup-modal">
1001
- <div class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-xl shadow-2xl w-[90%] max-w-[600px] max-h-[90vh] flex flex-col overflow-hidden">
1002
- <div class="flex items-center justify-between px-4 py-3 border-b border-gray-200 border-gray-300 bg-gray-50 bg-gray-50/50">
1003
- <h3 class="flex items-center gap-2 text-base font-semibold text-gray-900 text-mama-black">
1004
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1685
+ <div
1686
+ class="modal-overlay fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center"
1687
+ id="tunnel-setup-modal"
1688
+ >
1689
+ <div
1690
+ class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded-xl shadow-2xl w-[90%] max-w-[600px] max-h-[90vh] flex flex-col overflow-hidden"
1691
+ >
1692
+ <div
1693
+ class="flex items-center justify-between px-4 py-3 border-b border-gray-200 border-gray-300 bg-gray-50 bg-gray-50/50"
1694
+ >
1695
+ <h3
1696
+ class="flex items-center gap-2 text-base font-semibold text-gray-900 text-mama-black"
1697
+ >
1698
+ <svg
1699
+ xmlns="http://www.w3.org/2000/svg"
1700
+ width="18"
1701
+ height="18"
1702
+ viewBox="0 0 24 24"
1703
+ fill="none"
1704
+ stroke="currentColor"
1705
+ stroke-width="2"
1706
+ stroke-linecap="round"
1707
+ stroke-linejoin="round"
1708
+ >
1005
1709
  <circle cx="12" cy="12" r="10" />
1006
1710
  <line x1="2" y1="12" x2="22" y2="12" />
1007
- <path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" />
1711
+ <path
1712
+ d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z"
1713
+ />
1008
1714
  </svg>
1009
1715
  External Access Setup
1010
1716
  </h3>
1011
- <button class="p-1.5 rounded-lg hover:bg-gray-200 hover:bg-gray-100 transition-colors text-gray-500 hover:text-gray-700 hover:text-mama-black" onclick="hideTunnelSetup()">
1012
- <svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1717
+ <button
1718
+ class="p-1.5 rounded-lg hover:bg-gray-200 hover:bg-gray-100 transition-colors text-gray-500 hover:text-gray-700 hover:text-mama-black"
1719
+ onclick="hideTunnelSetup()"
1720
+ >
1721
+ <svg
1722
+ xmlns="http://www.w3.org/2000/svg"
1723
+ width="18"
1724
+ height="18"
1725
+ viewBox="0 0 24 24"
1726
+ fill="none"
1727
+ stroke="currentColor"
1728
+ stroke-width="2"
1729
+ stroke-linecap="round"
1730
+ stroke-linejoin="round"
1731
+ >
1013
1732
  <line x1="18" y1="6" x2="6" y2="18" />
1014
1733
  <line x1="6" y1="6" x2="18" y2="18" />
1015
1734
  </svg>
@@ -1019,23 +1738,67 @@
1019
1738
  <!-- ngrok section -->
1020
1739
  <div class="bg-gray-100 bg-white border border-gray-200 border-gray-300 rounded-lg p-4">
1021
1740
  <h4 class="text-sm text-mama-black text-mama-black mb-2 flex items-center gap-1.5">
1022
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1741
+ <svg
1742
+ xmlns="http://www.w3.org/2000/svg"
1743
+ width="16"
1744
+ height="16"
1745
+ viewBox="0 0 24 24"
1746
+ fill="none"
1747
+ stroke="currentColor"
1748
+ stroke-width="2"
1749
+ stroke-linecap="round"
1750
+ stroke-linejoin="round"
1751
+ >
1023
1752
  <polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
1024
1753
  </svg>
1025
1754
  ngrok (Quick Setup)
1026
1755
  </h4>
1027
- <p class="text-xs text-gray-500 text-gray-600 mb-3">Tunnel your local server to a public URL</p>
1028
- <div class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded px-3 py-2 flex items-center justify-between gap-2 mb-2">
1029
- <code id="ngrok-command" class="font-mono text-sm text-green-600 text-green-600">ngrok http 3847</code>
1030
- <button class="p-1.5 rounded hover:bg-gray-100 transition-colors" onclick="copyNgrokCommand()">
1031
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1756
+ <p class="text-xs text-gray-500 text-gray-600 mb-3">
1757
+ Tunnel your local server to a public URL
1758
+ </p>
1759
+ <div
1760
+ class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded px-3 py-2 flex items-center justify-between gap-2 mb-2"
1761
+ >
1762
+ <code id="ngrok-command" class="font-mono text-sm text-green-600 text-green-600"
1763
+ >ngrok http 3847</code
1764
+ >
1765
+ <button
1766
+ class="p-1.5 rounded hover:bg-gray-100 transition-colors"
1767
+ onclick="copyNgrokCommand()"
1768
+ >
1769
+ <svg
1770
+ xmlns="http://www.w3.org/2000/svg"
1771
+ width="14"
1772
+ height="14"
1773
+ viewBox="0 0 24 24"
1774
+ fill="none"
1775
+ stroke="currentColor"
1776
+ stroke-width="2"
1777
+ stroke-linecap="round"
1778
+ stroke-linejoin="round"
1779
+ >
1032
1780
  <rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
1033
1781
  <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
1034
1782
  </svg>
1035
1783
  </button>
1036
1784
  </div>
1037
- <a href="https://ngrok.com/download" target="_blank" rel="noopener noreferrer" class="text-xs text-mama-black text-mama-black hover:underline flex items-center gap-1">
1038
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1785
+ <a
1786
+ href="https://ngrok.com/download"
1787
+ target="_blank"
1788
+ rel="noopener noreferrer"
1789
+ class="text-xs text-mama-black text-mama-black hover:underline flex items-center gap-1"
1790
+ >
1791
+ <svg
1792
+ xmlns="http://www.w3.org/2000/svg"
1793
+ width="14"
1794
+ height="14"
1795
+ viewBox="0 0 24 24"
1796
+ fill="none"
1797
+ stroke="currentColor"
1798
+ stroke-width="2"
1799
+ stroke-linecap="round"
1800
+ stroke-linejoin="round"
1801
+ >
1039
1802
  <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
1040
1803
  <polyline points="7 10 12 15 17 10" />
1041
1804
  <line x1="12" y1="15" x2="12" y2="3" />
@@ -1047,14 +1810,41 @@
1047
1810
  <!-- Cloudflare section -->
1048
1811
  <div class="bg-gray-100 bg-white border border-gray-200 border-gray-300 rounded-lg p-4">
1049
1812
  <h4 class="text-sm text-mama-black text-mama-black mb-2 flex items-center gap-1.5">
1050
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1813
+ <svg
1814
+ xmlns="http://www.w3.org/2000/svg"
1815
+ width="16"
1816
+ height="16"
1817
+ viewBox="0 0 24 24"
1818
+ fill="none"
1819
+ stroke="currentColor"
1820
+ stroke-width="2"
1821
+ stroke-linecap="round"
1822
+ stroke-linejoin="round"
1823
+ >
1051
1824
  <path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" />
1052
1825
  </svg>
1053
1826
  Cloudflare Tunnel (Production)
1054
1827
  </h4>
1055
- <p class="text-xs text-gray-500 text-gray-600 mb-2">Secure tunnel with custom domain support</p>
1056
- <a href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/" target="_blank" rel="noopener noreferrer" class="text-xs text-mama-black text-mama-black hover:underline flex items-center gap-1">
1057
- <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1828
+ <p class="text-xs text-gray-500 text-gray-600 mb-2">
1829
+ Secure tunnel with custom domain support
1830
+ </p>
1831
+ <a
1832
+ href="https://developers.cloudflare.com/cloudflare-one/connections/connect-apps/"
1833
+ target="_blank"
1834
+ rel="noopener noreferrer"
1835
+ class="text-xs text-mama-black text-mama-black hover:underline flex items-center gap-1"
1836
+ >
1837
+ <svg
1838
+ xmlns="http://www.w3.org/2000/svg"
1839
+ width="14"
1840
+ height="14"
1841
+ viewBox="0 0 24 24"
1842
+ fill="none"
1843
+ stroke="currentColor"
1844
+ stroke-width="2"
1845
+ stroke-linecap="round"
1846
+ stroke-linejoin="round"
1847
+ >
1058
1848
  <path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
1059
1849
  <path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
1060
1850
  </svg>
@@ -1063,23 +1853,58 @@
1063
1853
  </div>
1064
1854
 
1065
1855
  <!-- Security warning -->
1066
- <div class="bg-amber-50 bg-amber-100/20 border border-amber-200 border-amber-300/50 rounded-lg p-3 flex gap-3">
1067
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-amber-500 flex-shrink-0 mt-0.5">
1068
- <path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z" />
1856
+ <div
1857
+ class="bg-amber-50 bg-amber-100/20 border border-amber-200 border-amber-300/50 rounded-lg p-3 flex gap-3"
1858
+ >
1859
+ <svg
1860
+ xmlns="http://www.w3.org/2000/svg"
1861
+ width="16"
1862
+ height="16"
1863
+ viewBox="0 0 24 24"
1864
+ fill="none"
1865
+ stroke="currentColor"
1866
+ stroke-width="2"
1867
+ stroke-linecap="round"
1868
+ stroke-linejoin="round"
1869
+ class="text-amber-500 flex-shrink-0 mt-0.5"
1870
+ >
1871
+ <path
1872
+ d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3Z"
1873
+ />
1069
1874
  <line x1="12" y1="9" x2="12" y2="13" />
1070
1875
  <line x1="12" y1="17" x2="12.01" y2="17" />
1071
1876
  </svg>
1072
1877
  <div class="text-xs">
1073
1878
  <strong class="text-amber-600 text-amber-600 block mb-1">Security Notice</strong>
1074
- <span class="text-gray-700 text-gray-700">Always set MAMA_AUTH_TOKEN environment variable before exposing your server externally.</span>
1075
- <pre class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded px-2 py-1.5 mt-2 text-[11px] text-green-600 text-green-600 overflow-x-auto">export MAMA_AUTH_TOKEN="your-secure-token-here"</pre>
1879
+ <span class="text-gray-700 text-gray-700"
1880
+ >Always set MAMA_AUTH_TOKEN environment variable before exposing your server
1881
+ externally.</span
1882
+ >
1883
+ <pre
1884
+ class="bg-white bg-gray-50 border border-gray-200 border-gray-300 rounded px-2 py-1.5 mt-2 text-[11px] text-green-600 text-green-600 overflow-x-auto"
1885
+ >
1886
+ export MAMA_AUTH_TOKEN="your-secure-token-here"</pre
1887
+ >
1076
1888
  </div>
1077
1889
  </div>
1078
1890
 
1079
1891
  <!-- Test connection -->
1080
1892
  <div class="border-t border-gray-200 border-gray-300 pt-4">
1081
- <button class="w-full px-4 py-2 text-sm font-medium text-white bg-mama-yellow hover:bg-mama-yellow-hover rounded-lg transition-colors flex items-center justify-center gap-2" onclick="testConnection()">
1082
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
1893
+ <button
1894
+ class="w-full px-4 py-2 text-sm font-medium text-white bg-mama-yellow hover:bg-mama-yellow-hover rounded-lg transition-colors flex items-center justify-center gap-2"
1895
+ onclick="testConnection()"
1896
+ >
1897
+ <svg
1898
+ xmlns="http://www.w3.org/2000/svg"
1899
+ width="16"
1900
+ height="16"
1901
+ viewBox="0 0 24 24"
1902
+ fill="none"
1903
+ stroke="currentColor"
1904
+ stroke-width="2"
1905
+ stroke-linecap="round"
1906
+ stroke-linejoin="round"
1907
+ >
1083
1908
  <path d="M5 12.55a11 11 0 0 1 14.08 0" />
1084
1909
  <path d="M1.42 9a16 16 0 0 1 21.16 0" />
1085
1910
  <path d="M8.53 16.11a6 6 0 0 1 6.95 0" />
@@ -1104,19 +1929,93 @@
1104
1929
  import { MemoryModule } from '/viewer/js/modules/memory.js';
1105
1930
  import { DashboardModule } from '/viewer/js/modules/dashboard.js';
1106
1931
  import { SettingsModule } from '/viewer/js/modules/settings.js';
1107
- import { SkillsModule } from '/viewer/js/modules/skills.js';
1108
1932
  import { ConnectorFeedModule } from '/viewer/js/modules/connector-feed.js';
1109
1933
  import { WikiModule } from '/viewer/js/modules/wiki.js';
1934
+ import { AgentsModule } from '/viewer/js/modules/agents.js';
1110
1935
  import { API } from '/viewer/js/utils/api.js';
1936
+ import { startUICommandPolling, reportPageContext } from '/viewer/js/utils/ui-commands.js';
1111
1937
 
1112
1938
  const memory = new MemoryModule();
1113
1939
  const chat = new ChatModule(memory);
1114
1940
  const graph = new GraphModule();
1115
1941
  const dashboard = new DashboardModule();
1116
1942
  const settings = new SettingsModule();
1117
- const skills = SkillsModule;
1118
1943
  const connectorFeed = new ConnectorFeedModule();
1119
1944
  const wiki = new WikiModule();
1945
+ const agentsModule = new AgentsModule();
1946
+
1947
+ // ── Chat Panel: toggle, resize, tab indicator (SmartStore pattern) ──
1948
+ const CHAT_WIDTH_KEY = 'mama-chat-width';
1949
+ const CHAT_COLLAPSED_KEY = 'mama-chat-collapsed';
1950
+ const MIN_CHAT = 280;
1951
+ const MAX_CHAT = 560;
1952
+
1953
+ function toggleChatPanel() {
1954
+ const wrapper = document.getElementById('chat-panel-wrapper');
1955
+ const bar = document.getElementById('chat-collapsed-bar');
1956
+ const handle = document.getElementById('chat-resize-handle-bar');
1957
+ if (!wrapper || !bar) return;
1958
+ const isCollapsed = wrapper.style.display === 'none';
1959
+ if (isCollapsed) {
1960
+ wrapper.style.display = '';
1961
+ bar.classList.add('hidden');
1962
+ if (handle) handle.classList.add('chat-resize-visible');
1963
+ localStorage.setItem(CHAT_COLLAPSED_KEY, 'false');
1964
+ } else {
1965
+ wrapper.style.display = 'none';
1966
+ bar.classList.remove('hidden');
1967
+ if (handle) handle.classList.remove('chat-resize-visible');
1968
+ localStorage.setItem(CHAT_COLLAPSED_KEY, 'true');
1969
+ }
1970
+ }
1971
+ window.toggleChatPanel = toggleChatPanel;
1972
+
1973
+ // Restore collapsed state
1974
+ if (localStorage.getItem(CHAT_COLLAPSED_KEY) === 'true') {
1975
+ toggleChatPanel();
1976
+ }
1977
+
1978
+ // Restore width
1979
+ {
1980
+ const saved = localStorage.getItem(CHAT_WIDTH_KEY);
1981
+ if (saved) {
1982
+ const w = Math.max(MIN_CHAT, Math.min(MAX_CHAT, Number(saved)));
1983
+ const wrapper = document.getElementById('chat-panel-wrapper');
1984
+ if (wrapper) wrapper.style.width = w + 'px';
1985
+ }
1986
+ }
1987
+
1988
+ // Chat resize handle (SmartStore Layout.tsx:158-176 pattern)
1989
+ (function initChatResize() {
1990
+ const handle = document.getElementById('chat-resize-handle-bar');
1991
+ const wrapper = document.getElementById('chat-panel-wrapper');
1992
+ if (!handle || !wrapper) return;
1993
+
1994
+ handle.addEventListener('mousedown', (e) => {
1995
+ e.preventDefault();
1996
+ const startX = e.clientX;
1997
+ const startWidth = wrapper.offsetWidth;
1998
+ const onMove = (ev) => {
1999
+ const delta = startX - ev.clientX;
2000
+ const w = Math.max(MIN_CHAT, Math.min(MAX_CHAT, startWidth + delta));
2001
+ wrapper.style.width = w + 'px';
2002
+ };
2003
+ const onUp = () => {
2004
+ document.removeEventListener('mousemove', onMove);
2005
+ document.removeEventListener('mouseup', onUp);
2006
+ document.body.style.cursor = '';
2007
+ document.body.style.userSelect = '';
2008
+ localStorage.setItem(CHAT_WIDTH_KEY, String(wrapper.offsetWidth));
2009
+ };
2010
+ document.body.style.cursor = 'col-resize';
2011
+ document.body.style.userSelect = 'none';
2012
+ document.addEventListener('mousemove', onMove);
2013
+ document.addEventListener('mouseup', onUp);
2014
+ });
2015
+ })();
2016
+
2017
+ // Start UI command polling (SmartStore Layout pattern)
2018
+ startUICommandPolling(switchTab);
1120
2019
 
1121
2020
  // Expose modules globally for debugging and onclick handlers
1122
2021
  window.chatModule = chat;
@@ -1124,7 +2023,6 @@
1124
2023
  window.memoryModule = memory;
1125
2024
  window.dashboardModule = dashboard;
1126
2025
  window.settingsModule = settings;
1127
- window.skillsModule = skills;
1128
2026
 
1129
2027
  // Quiz choice button handler
1130
2028
  window.sendQuizChoice = (choice) => {
@@ -1134,24 +2032,44 @@
1134
2032
  const STATE = {
1135
2033
  currentTab: 'dashboard',
1136
2034
  theme: 'dark',
1137
- graphLoaded: false
2035
+ graphLoaded: false,
1138
2036
  };
1139
2037
 
1140
- async function switchTab(tabName) {
2038
+ async function switchTab(tabName, params) {
1141
2039
  STATE.currentTab = tabName;
1142
2040
 
2041
+ // Close mobile More menu if open
2042
+ const moreMenu = document.getElementById('mama-mobile-more');
2043
+ if (moreMenu) moreMenu.style.display = 'none';
2044
+ const moreBtn = document.querySelector('#mama-mobile-tabs [data-tab="more"]');
2045
+ const overflowTabs = ['feed', 'wiki', 'logs', 'settings'];
2046
+ if (moreBtn) {
2047
+ moreBtn.classList.toggle('mama-nav-active', overflowTabs.includes(tabName));
2048
+ }
2049
+
2050
+ // Also highlight the source tab in More menu if it was selected from there
2051
+ document.querySelectorAll('#mama-mobile-more .mama-mobile-tab').forEach((btn) => {
2052
+ btn.classList.toggle('mama-nav-active', btn.dataset.tab === tabName);
2053
+ });
2054
+
2055
+ // Update chat panel tab indicator
2056
+ const tabIndicator = document.getElementById('chat-tab-indicator');
2057
+ if (tabIndicator) {
2058
+ tabIndicator.textContent = tabName.charAt(0).toUpperCase() + tabName.slice(1);
2059
+ }
2060
+
1143
2061
  // Update sidebar nav items
1144
- document.querySelectorAll('#mama-sidebar .mama-nav-item').forEach(btn => {
2062
+ document.querySelectorAll('#mama-sidebar .mama-nav-item').forEach((btn) => {
1145
2063
  btn.classList.toggle('mama-nav-active', btn.dataset.tab === tabName);
1146
2064
  });
1147
2065
 
1148
2066
  // Update mobile tab bar
1149
- document.querySelectorAll('#mama-mobile-tabs .mama-mobile-tab').forEach(btn => {
2067
+ document.querySelectorAll('#mama-mobile-tabs .mama-mobile-tab').forEach((btn) => {
1150
2068
  btn.classList.toggle('mama-nav-active', btn.dataset.tab === tabName);
1151
2069
  });
1152
2070
 
1153
2071
  // Legacy: update any remaining [data-tab] buttons outside sidebar/mobile
1154
- document.querySelectorAll('[data-tab]').forEach(btn => {
2072
+ document.querySelectorAll('[data-tab]').forEach((btn) => {
1155
2073
  if (btn.closest('#mama-sidebar') || btn.closest('#mama-mobile-tabs')) return;
1156
2074
  const isActive = btn.dataset.tab === tabName;
1157
2075
  btn.classList.toggle('bg-indigo-100', isActive);
@@ -1159,15 +2077,37 @@
1159
2077
  btn.classList.toggle('text-mama-black', isActive);
1160
2078
  });
1161
2079
 
1162
- document.querySelectorAll('.tab-content').forEach(content => {
2080
+ document.querySelectorAll('.tab-content').forEach((content) => {
1163
2081
  content.classList.toggle('active', content.id === `tab-${tabName}`);
1164
2082
  });
1165
2083
 
2084
+ // Mobile chat: toggle full-screen chat overlay
2085
+ const chatWrapper = document.getElementById('chat-panel-wrapper');
2086
+ if (chatWrapper) {
2087
+ if (tabName === 'chat') {
2088
+ chatWrapper.classList.add('mobile-chat-active');
2089
+ } else {
2090
+ chatWrapper.classList.remove('mobile-chat-active');
2091
+ }
2092
+ }
2093
+
2094
+ const publishBasicTabContext = () => {
2095
+ const selectedItem =
2096
+ tabName === 'agents' && params?.id ? { type: 'agent', id: params.id } : undefined;
2097
+ reportPageContext(
2098
+ tabName,
2099
+ { pageType: 'tab-switch', tab: tabName, ...(params || {}) },
2100
+ selectedItem
2101
+ );
2102
+ };
2103
+
1166
2104
  if (tabName === 'dashboard') {
1167
2105
  dashboard.init();
2106
+ publishBasicTabContext();
1168
2107
  } else if (tabName === 'settings') {
1169
2108
  settings.init();
1170
2109
  loadConnectorsList();
2110
+ publishBasicTabContext();
1171
2111
  } else if (tabName === 'memory') {
1172
2112
  // Initialize sidebar state for current screen size
1173
2113
  updateCheckpointSidebarForScreenSize();
@@ -1189,18 +2129,35 @@
1189
2129
  } catch (error) {
1190
2130
  console.error('[MAMA] Failed to load graph:', error);
1191
2131
  const loadingEl = document.getElementById('graph-loading');
1192
- if (loadingEl) loadingEl.innerHTML = '<span class="text-red-500">Failed to load graph data</span>';
2132
+ if (loadingEl)
2133
+ loadingEl.innerHTML = '<span class="text-red-500">Failed to load graph data</span>';
1193
2134
  }
1194
2135
  }
2136
+ publishBasicTabContext();
1195
2137
  } else if (tabName === 'feed') {
1196
2138
  connectorFeed.init();
2139
+ publishBasicTabContext();
2140
+ } else if (tabName === 'agents') {
2141
+ agentsModule.init();
2142
+ // Handle deep navigation: params.id opens agent detail, params.tab selects tab
2143
+ if (params?.id) {
2144
+ await agentsModule.navigateTo(params.id, params.tab);
2145
+ } else {
2146
+ agentsModule.showList();
2147
+ }
1197
2148
  } else if (tabName === 'wiki') {
1198
2149
  wiki.init();
2150
+ await wiki.navigateTo(params?.path);
1199
2151
  } else if (tabName === 'logs') {
1200
2152
  const iframe = document.getElementById('logs-iframe');
1201
2153
  if (iframe && !iframe.getAttribute('src')) {
1202
2154
  iframe.src = '/viewer/log-viewer.html';
1203
2155
  }
2156
+ publishBasicTabContext();
2157
+ } else if (tabName === 'chat') {
2158
+ publishBasicTabContext();
2159
+ } else {
2160
+ publishBasicTabContext();
1204
2161
  }
1205
2162
  }
1206
2163
 
@@ -1263,35 +2220,44 @@
1263
2220
  });
1264
2221
 
1265
2222
  // Touch drag for mobile
1266
- header.addEventListener('touchstart', (e) => {
1267
- if (e.target.tagName === 'BUTTON') return;
1268
- const touch = e.touches[0];
1269
- if (!touch) return;
1270
- e.preventDefault();
1271
- e.stopPropagation();
1272
- document.body.classList.add('no-scroll');
1273
- isDragging = true;
1274
- startX = touch.clientX;
1275
- startY = touch.clientY;
1276
- const rect = modal.getBoundingClientRect();
1277
- startLeft = rect.left;
1278
- startTop = rect.top;
1279
- modal.style.transition = 'none';
1280
- }, { passive: false });
1281
-
1282
- document.addEventListener('touchmove', (e) => {
1283
- if (!isDragging) return;
1284
- const touch = e.touches[0];
1285
- if (!touch) return;
1286
- e.preventDefault();
1287
- e.stopPropagation();
1288
- const dx = touch.clientX - startX;
1289
- const dy = touch.clientY - startY;
1290
- modal.style.left = Math.max(0, Math.min(window.innerWidth - 100, startLeft + dx)) + 'px';
1291
- modal.style.top = Math.max(0, Math.min(window.innerHeight - 50, startTop + dy)) + 'px';
1292
- modal.style.right = 'auto';
1293
- modal.style.transform = 'none';
1294
- }, { passive: false });
2223
+ header.addEventListener(
2224
+ 'touchstart',
2225
+ (e) => {
2226
+ if (e.target.tagName === 'BUTTON') return;
2227
+ const touch = e.touches[0];
2228
+ if (!touch) return;
2229
+ e.preventDefault();
2230
+ e.stopPropagation();
2231
+ document.body.classList.add('no-scroll');
2232
+ isDragging = true;
2233
+ startX = touch.clientX;
2234
+ startY = touch.clientY;
2235
+ const rect = modal.getBoundingClientRect();
2236
+ startLeft = rect.left;
2237
+ startTop = rect.top;
2238
+ modal.style.transition = 'none';
2239
+ },
2240
+ { passive: false }
2241
+ );
2242
+
2243
+ document.addEventListener(
2244
+ 'touchmove',
2245
+ (e) => {
2246
+ if (!isDragging) return;
2247
+ const touch = e.touches[0];
2248
+ if (!touch) return;
2249
+ e.preventDefault();
2250
+ e.stopPropagation();
2251
+ const dx = touch.clientX - startX;
2252
+ const dy = touch.clientY - startY;
2253
+ modal.style.left =
2254
+ Math.max(0, Math.min(window.innerWidth - 100, startLeft + dx)) + 'px';
2255
+ modal.style.top = Math.max(0, Math.min(window.innerHeight - 50, startTop + dy)) + 'px';
2256
+ modal.style.right = 'auto';
2257
+ modal.style.transform = 'none';
2258
+ },
2259
+ { passive: false }
2260
+ );
1295
2261
 
1296
2262
  document.addEventListener('touchend', () => {
1297
2263
  if (isDragging) {
@@ -1339,22 +2305,30 @@
1339
2305
  document.addEventListener('mousemove', (e) => doResize(e.clientX, e.clientY));
1340
2306
  document.addEventListener('mouseup', endResize);
1341
2307
 
1342
- resizeHandle.addEventListener('touchstart', (e) => {
1343
- const touch = e.touches[0];
1344
- if (!touch) return;
1345
- e.preventDefault();
1346
- e.stopPropagation();
1347
- startResize(touch.clientX, touch.clientY);
1348
- document.body.classList.add('no-scroll');
1349
- }, { passive: false });
1350
- document.addEventListener('touchmove', (e) => {
1351
- const touch = e.touches[0];
1352
- if (!touch) return;
1353
- if (!resizing) return;
1354
- e.preventDefault();
1355
- e.stopPropagation();
1356
- doResize(touch.clientX, touch.clientY);
1357
- }, { passive: false });
2308
+ resizeHandle.addEventListener(
2309
+ 'touchstart',
2310
+ (e) => {
2311
+ const touch = e.touches[0];
2312
+ if (!touch) return;
2313
+ e.preventDefault();
2314
+ e.stopPropagation();
2315
+ startResize(touch.clientX, touch.clientY);
2316
+ document.body.classList.add('no-scroll');
2317
+ },
2318
+ { passive: false }
2319
+ );
2320
+ document.addEventListener(
2321
+ 'touchmove',
2322
+ (e) => {
2323
+ const touch = e.touches[0];
2324
+ if (!touch) return;
2325
+ if (!resizing) return;
2326
+ e.preventDefault();
2327
+ e.stopPropagation();
2328
+ doResize(touch.clientX, touch.clientY);
2329
+ },
2330
+ { passive: false }
2331
+ );
1358
2332
  document.addEventListener('touchend', () => {
1359
2333
  endResize();
1360
2334
  document.body.classList.remove('no-scroll');
@@ -1555,9 +2529,12 @@
1555
2529
  const result = document.getElementById('tunnel-test-result');
1556
2530
  result.innerHTML = '<span class="text-gray-500">Testing...</span>';
1557
2531
  fetch('/health')
1558
- .then(r => r.ok ? result.innerHTML = '<span class="text-green-500">Connection OK</span>'
1559
- : result.innerHTML = '<span class="text-red-500">Connection failed</span>')
1560
- .catch(() => result.innerHTML = '<span class="text-red-500">Connection failed</span>');
2532
+ .then((r) =>
2533
+ r.ok
2534
+ ? (result.innerHTML = '<span class="text-green-500">Connection OK</span>')
2535
+ : (result.innerHTML = '<span class="text-red-500">Connection failed</span>')
2536
+ )
2537
+ .catch(() => (result.innerHTML = '<span class="text-red-500">Connection failed</span>'));
1561
2538
  }
1562
2539
 
1563
2540
  async function sendChatMessage() {
@@ -1585,21 +2562,31 @@
1585
2562
  const message = input.value.trim() || defaultMsg;
1586
2563
 
1587
2564
  const mediaUrl = '/api/media/' + encodeURIComponent(data.filename);
1588
- const attachMeta = { mediaUrl, filename: data.filename, originalName: file.name, contentType: file.type, isImage };
2565
+ const attachMeta = {
2566
+ mediaUrl,
2567
+ filename: data.filename,
2568
+ originalName: file.name,
2569
+ contentType: file.type,
2570
+ isImage,
2571
+ };
1589
2572
 
1590
2573
  // Save with attachment metadata for history persistence
1591
2574
  chat.addUserMessageWithAttachment(message, attachMeta);
1592
2575
 
1593
2576
  chat.enableSend(false);
1594
- chat.ws.send(JSON.stringify({
1595
- type: 'send',
1596
- sessionId: chat.sessionId,
1597
- content: message,
1598
- attachments: [{
1599
- filename: data.filename,
1600
- contentType: data.contentType,
1601
- }],
1602
- }));
2577
+ chat.ws.send(
2578
+ JSON.stringify({
2579
+ type: 'send',
2580
+ sessionId: chat.sessionId,
2581
+ content: message,
2582
+ attachments: [
2583
+ {
2584
+ filename: data.filename,
2585
+ contentType: data.contentType,
2586
+ },
2587
+ ],
2588
+ })
2589
+ );
1603
2590
 
1604
2591
  // Clear attachment only after successful send
1605
2592
  clearAttachment();
@@ -1614,7 +2601,7 @@
1614
2601
  }
1615
2602
 
1616
2603
  // Send quiz choice as message
1617
- window.sendQuizChoice = function(choice) {
2604
+ window.sendQuizChoice = function (choice) {
1618
2605
  if (chat && chat.ws && chat.ws.readyState === WebSocket.OPEN) {
1619
2606
  // Set the input to the choice and send
1620
2607
  const input = document.getElementById('chat-input');
@@ -1669,8 +2656,9 @@
1669
2656
  if (!response.ok) throw new Error('Export failed');
1670
2657
 
1671
2658
  const blob = await response.blob();
1672
- const filename = response.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1]
1673
- || `mama-export.${format}`;
2659
+ const filename =
2660
+ response.headers.get('Content-Disposition')?.match(/filename="(.+)"/)?.[1] ||
2661
+ `mama-export.${format}`;
1674
2662
 
1675
2663
  const url = URL.createObjectURL(blob);
1676
2664
  const a = document.createElement('a');
@@ -1744,9 +2732,10 @@
1744
2732
  console.log('[MAMA] Loaded', checkpointsData.length, 'checkpoints');
1745
2733
 
1746
2734
  if (checkpointsData.length > 0) {
1747
- container.innerHTML = checkpointsData.map((cp, idx) => {
1748
- const fullSummary = renderMarkdown(cp.summary || '');
1749
- return `
2735
+ container.innerHTML = checkpointsData
2736
+ .map((cp, idx) => {
2737
+ const fullSummary = renderMarkdown(cp.summary || '');
2738
+ return `
1750
2739
  <div class="checkpoint-item p-3 bg-white bg-white border border-gray-200 border-gray-300 rounded-lg cursor-pointer hover:border-indigo-500 transition-colors" onclick="toggleCheckpoint(${idx})">
1751
2740
  <div class="text-xs font-semibold text-mama-black text-mama-black mb-1">${new Date(cp.timestamp).toLocaleString()}</div>
1752
2741
  <div class="checkpoint-summary text-sm text-gray-700 text-gray-700 markdown-content line-clamp-3 overflow-hidden">${fullSummary}</div>
@@ -1754,13 +2743,17 @@
1754
2743
  <div class="text-sm text-gray-700 text-gray-700 markdown-content max-h-64 overflow-y-auto">${fullSummary}</div>
1755
2744
  </div>
1756
2745
  </div>
1757
- `}).join('');
2746
+ `;
2747
+ })
2748
+ .join('');
1758
2749
  } else {
1759
- container.innerHTML = '<div class="text-center text-sm text-gray-500 py-4">No checkpoints found</div>';
2750
+ container.innerHTML =
2751
+ '<div class="text-center text-sm text-gray-500 py-4">No checkpoints found</div>';
1760
2752
  }
1761
2753
  } catch (error) {
1762
2754
  console.error('[MAMA] Failed to load checkpoints:', error);
1763
- container.innerHTML = '<div class="text-center text-sm text-red-500 py-4">Failed to load checkpoints</div>';
2755
+ container.innerHTML =
2756
+ '<div class="text-center text-sm text-red-500 py-4">Failed to load checkpoints</div>';
1764
2757
  }
1765
2758
  }
1766
2759
 
@@ -1783,13 +2776,27 @@
1783
2776
  }
1784
2777
 
1785
2778
  // Connector list loader for Settings
1786
- const CONNECTOR_NAMES = ['slack','telegram','discord','chatwork','gmail','calendar','notion','obsidian','kagemusha','sheets','trello','drive','imessage'];
2779
+ const CONNECTOR_NAMES = [
2780
+ 'slack',
2781
+ 'telegram',
2782
+ 'discord',
2783
+ 'chatwork',
2784
+ 'gmail',
2785
+ 'calendar',
2786
+ 'notion',
2787
+ 'obsidian',
2788
+ 'kagemusha',
2789
+ 'sheets',
2790
+ 'trello',
2791
+ 'drive',
2792
+ 'imessage',
2793
+ ];
1787
2794
  const CONNECTOR_CATEGORIES = {
1788
- 'Messaging': ['slack','telegram','discord','chatwork'],
1789
- 'Google': ['gmail','calendar','drive','sheets'],
1790
- 'Knowledge': ['notion','obsidian','kagemusha'],
2795
+ Messaging: ['slack', 'telegram', 'discord', 'chatwork'],
2796
+ Google: ['gmail', 'calendar', 'drive', 'sheets'],
2797
+ Knowledge: ['notion', 'obsidian', 'kagemusha'],
1791
2798
  'Dev Tools': ['claude-code'],
1792
- 'Other': ['trello','imessage'],
2799
+ Other: ['trello', 'imessage'],
1793
2800
  };
1794
2801
  async function loadConnectorsList() {
1795
2802
  const container = document.getElementById('settings-connectors-list');
@@ -1799,7 +2806,7 @@
1799
2806
  const res = await fetch('/api/connectors/status');
1800
2807
  if (res.ok) {
1801
2808
  const data = await res.json();
1802
- for (const c of (data.connectors || [])) connectorStatus[c.name] = c;
2809
+ for (const c of data.connectors || []) connectorStatus[c.name] = c;
1803
2810
  }
1804
2811
  } catch {}
1805
2812
  // Load config
@@ -1817,17 +2824,24 @@
1817
2824
  for (const name of names) {
1818
2825
  const status = connectorStatus[name];
1819
2826
  const cfg = config[name];
1820
- const enabled = cfg?.enabled || (status?.enabled) || false;
2827
+ const enabled = cfg?.enabled || status?.enabled || false;
1821
2828
  const healthy = status?.healthy || false;
1822
- const lastPoll = status?.lastPollTime ? new Date(status.lastPollTime).toLocaleTimeString('ko-KR', {hour:'2-digit',minute:'2-digit'}) : null;
2829
+ const lastPoll = status?.lastPollTime
2830
+ ? new Date(status.lastPollTime).toLocaleTimeString('ko-KR', {
2831
+ hour: '2-digit',
2832
+ minute: '2-digit',
2833
+ })
2834
+ : null;
1823
2835
  const channels = status?.channelCount || Object.keys(cfg?.channels || {}).length || 0;
1824
2836
  const statusDot = enabled ? (healthy ? '#3A9E7E' : '#E8A040') : '#9E9891';
1825
2837
  html += `<div style="background:#fff;border:1px solid #EDE9E1;border-radius:4px;padding:8px 10px;font-size:12px;display:flex;align-items:center;gap:8px">`;
1826
2838
  html += `<span style="display:inline-block;width:6px;height:6px;background:${statusDot};border-radius:1px;flex-shrink:0"></span>`;
1827
2839
  html += `<span style="font-weight:600;color:#1A1A1A;flex:1">${name}</span>`;
1828
- if (channels > 0) html += `<span style="font-size:10px;color:#9E9891">${channels}ch</span>`;
2840
+ if (channels > 0)
2841
+ html += `<span style="font-size:10px;color:#9E9891">${channels}ch</span>`;
1829
2842
  if (lastPoll) html += `<span style="font-size:10px;color:#9E9891">${lastPoll}</span>`;
1830
- if (!enabled) html += `<span style="font-size:9px;color:#9E9891;background:#F5F2ED;padding:1px 4px;border-radius:2px">off</span>`;
2843
+ if (!enabled)
2844
+ html += `<span style="font-size:9px;color:#9E9891;background:#F5F2ED;padding:1px 4px;border-radius:2px">off</span>`;
1831
2845
  html += `</div>`;
1832
2846
  }
1833
2847
  html += `</div></div>`;
@@ -1835,8 +2849,27 @@
1835
2849
  container.innerHTML = html;
1836
2850
  }
1837
2851
 
2852
+ // Mobile "More" menu
2853
+ function toggleMobileMore() {
2854
+ const more = document.getElementById('mama-mobile-more');
2855
+ if (!more) return;
2856
+ const isVisible = more.style.display !== 'none';
2857
+ more.style.display = isVisible ? 'none' : 'block';
2858
+ // Update More button active state
2859
+ const moreBtn = document.querySelector('#mama-mobile-tabs [data-tab="more"]');
2860
+ if (moreBtn) moreBtn.classList.toggle('mama-nav-active', !isVisible);
2861
+ }
2862
+ function closeMobileMore() {
2863
+ const more = document.getElementById('mama-mobile-more');
2864
+ if (more) more.style.display = 'none';
2865
+ const moreBtn = document.querySelector('#mama-mobile-tabs [data-tab="more"]');
2866
+ if (moreBtn) moreBtn.classList.remove('mama-nav-active');
2867
+ }
2868
+
1838
2869
  // Expose to window for onclick handlers
1839
2870
  window.switchTab = switchTab;
2871
+ window.toggleMobileMore = toggleMobileMore;
2872
+ window.closeMobileMore = closeMobileMore;
1840
2873
  window.toggleTheme = toggleTheme;
1841
2874
  window.toggleLegend = toggleLegend;
1842
2875
  window.closeDetailModal = closeDetailModal;
@@ -1882,7 +2915,9 @@
1882
2915
 
1883
2916
  if (file.type.startsWith('image/')) {
1884
2917
  const reader = new FileReader();
1885
- reader.onload = (e) => { thumb.src = e.target.result; };
2918
+ reader.onload = (e) => {
2919
+ thumb.src = e.target.result;
2920
+ };
1886
2921
  reader.readAsDataURL(file);
1887
2922
  thumb.style.display = '';
1888
2923
  } else {
@@ -1905,7 +2940,7 @@
1905
2940
  document.getElementById('chat-attachment-preview').classList.add('hidden');
1906
2941
  document.getElementById('chat-file-input').value = '';
1907
2942
  const thumb = document.getElementById('chat-attachment-thumb');
1908
- thumb.src = '';
2943
+ thumb.src = 'data:,';
1909
2944
  thumb.style.display = '';
1910
2945
  }
1911
2946
 
@@ -1916,38 +2951,76 @@
1916
2951
  const chatMessages = document.getElementById('chat-messages');
1917
2952
  if (chatMessages) {
1918
2953
  const panel = chatMessages.closest('#chat-panel') || chatMessages.parentElement;
1919
- ['dragenter', 'dragover'].forEach(evt => {
1920
- panel.addEventListener(evt, (e) => { e.preventDefault(); e.stopPropagation(); panel.classList.add('ring-2', 'ring-mama-yellow'); });
2954
+ ['dragenter', 'dragover'].forEach((evt) => {
2955
+ panel.addEventListener(evt, (e) => {
2956
+ e.preventDefault();
2957
+ e.stopPropagation();
2958
+ panel.classList.add('ring-2', 'ring-mama-yellow');
2959
+ });
1921
2960
  });
1922
- ['dragleave', 'drop'].forEach(evt => {
1923
- panel.addEventListener(evt, (e) => { e.preventDefault(); e.stopPropagation(); panel.classList.remove('ring-2', 'ring-mama-yellow'); });
2961
+ ['dragleave', 'drop'].forEach((evt) => {
2962
+ panel.addEventListener(evt, (e) => {
2963
+ e.preventDefault();
2964
+ e.stopPropagation();
2965
+ panel.classList.remove('ring-2', 'ring-mama-yellow');
2966
+ });
1924
2967
  });
1925
2968
  panel.addEventListener('drop', (e) => {
1926
2969
  const file = e.dataTransfer.files[0];
1927
2970
  if (!file) return;
1928
2971
 
1929
2972
  // Allowed MIME types and extensions (match file input accept attribute)
1930
- const allowedTypes = ['image/', 'application/pdf', 'application/msword',
2973
+ const allowedTypes = [
2974
+ 'image/',
2975
+ 'application/pdf',
2976
+ 'application/msword',
1931
2977
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
1932
- 'text/plain', 'text/csv', 'text/markdown', 'text/html', 'text/xml',
1933
- 'application/json', 'application/xml',
2978
+ 'text/plain',
2979
+ 'text/csv',
2980
+ 'text/markdown',
2981
+ 'text/html',
2982
+ 'text/xml',
2983
+ 'application/json',
2984
+ 'application/xml',
1934
2985
  'application/vnd.ms-excel',
1935
2986
  'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
1936
2987
  'application/vnd.ms-powerpoint',
1937
2988
  'application/vnd.openxmlformats-officedocument.presentationml.presentation',
1938
- 'application/zip', 'application/gzip'];
1939
- const allowedExts = ['.pdf', '.doc', '.docx', '.txt', '.csv', '.xls', '.xlsx',
1940
- '.md', '.json', '.html', '.htm', '.xml', '.zip', '.gz', '.ppt', '.pptx'];
2989
+ 'application/zip',
2990
+ 'application/gzip',
2991
+ ];
2992
+ const allowedExts = [
2993
+ '.pdf',
2994
+ '.doc',
2995
+ '.docx',
2996
+ '.txt',
2997
+ '.csv',
2998
+ '.xls',
2999
+ '.xlsx',
3000
+ '.md',
3001
+ '.json',
3002
+ '.html',
3003
+ '.htm',
3004
+ '.xml',
3005
+ '.zip',
3006
+ '.gz',
3007
+ '.ppt',
3008
+ '.pptx',
3009
+ ];
1941
3010
  const ext = '.' + (file.name.split('.').pop() || '').toLowerCase();
1942
3011
 
1943
- const isAllowed = allowedTypes.some(t => file.type.startsWith(t)) || allowedExts.includes(ext);
3012
+ const isAllowed =
3013
+ allowedTypes.some((t) => file.type.startsWith(t)) || allowedExts.includes(ext);
1944
3014
  if (isAllowed) {
1945
3015
  const dt = new DataTransfer();
1946
3016
  dt.items.add(file);
1947
3017
  document.getElementById('chat-file-input').files = dt.files;
1948
3018
  handleFileSelect({ target: { files: [file] } });
1949
3019
  } else {
1950
- chat.addSystemMessage('Unsupported file type. Allowed: images, PDF, DOC, TXT, CSV, XLS, PPT, MD, JSON, HTML, ZIP', 'error');
3020
+ chat.addSystemMessage(
3021
+ 'Unsupported file type. Allowed: images, PDF, DOC, TXT, CSV, XLS, PPT, MD, JSON, HTML, ZIP',
3022
+ 'error'
3023
+ );
1951
3024
  }
1952
3025
  });
1953
3026
  }
@@ -1978,10 +3051,16 @@
1978
3051
  window.addEventListener('message', (event) => {
1979
3052
  if (!event.data) return;
1980
3053
 
1981
- // skill:deployedrefresh Skills Tab list
1982
- if (event.data.type === 'skill:deployed') {
1983
- if (skills && skills.loadSkills) {
1984
- skills.loadSkills().then(() => skills.render());
3054
+ // viewer:sendToChatgeneric child iframe → chat handoff
3055
+ if (event.data.type === 'viewer:sendToChat') {
3056
+ const message = typeof event.data.message === 'string' ? event.data.message : '';
3057
+ if (!message) {
3058
+ return;
3059
+ }
3060
+ const input = document.getElementById('chat-input');
3061
+ if (input && chat) {
3062
+ input.value = message;
3063
+ setTimeout(() => chat.send(), 50);
1985
3064
  }
1986
3065
  }
1987
3066
  });
@@ -2010,15 +3089,6 @@ Please explain what this tab shows:
2010
3089
  - Scheduled jobs (Cron) status
2011
3090
  Explain what each section means and what actions the user can take. Be friendly and concise.`,
2012
3091
 
2013
- skills: `The user clicked the help button on the Skills tab.
2014
- Please explain this tab's features:
2015
- - Installed: List of installed skills (toggle enable/disable, edit, remove)
2016
- - Available: Skills from the Cowork marketplace that can be installed
2017
- - Install from GitHub URL for external plugins
2018
- - [+ New Skill] button to create skills in Skill Lab
2019
- - [Edit] button to modify existing skills in Skill Lab
2020
- Explain what skills are and how they enhance the agent's capabilities. Be friendly and concise.`,
2021
-
2022
3092
  memory: `The user clicked the help button on the Memory tab.
2023
3093
  Please explain this tab's features:
2024
3094
  - Decision Graph: Visual graph showing relationships between decisions (click nodes for details)
@@ -2044,19 +3114,21 @@ Please explain this tab's features:
2044
3114
  - Filter by log level (ERROR, WARN, INFO, DEBUG)
2045
3115
  - Search logs by keyword
2046
3116
  - Pause/Resume auto-refresh
2047
- Be friendly and concise.`
3117
+ Be friendly and concise.`,
2048
3118
  };
2049
3119
 
2050
- window.askContextHelp = function() {
3120
+ window.askContextHelp = function () {
2051
3121
  const currentTab = STATE.currentTab || 'dashboard';
2052
3122
  const prompt = TAB_HELP_PROMPTS[currentTab];
2053
3123
  if (!prompt) return;
2054
3124
 
2055
- // Open floating chat if closed
2056
- const panel = document.getElementById('floating-chat-panel');
2057
- if (panel && panel.classList.contains('hidden')) {
2058
- const toggle = document.getElementById('floating-chat-toggle');
2059
- if (toggle) toggle.click();
3125
+ if (window.innerWidth < 768) {
3126
+ switchTab('chat');
3127
+ } else {
3128
+ const wrapper = document.getElementById('chat-panel-wrapper');
3129
+ if (wrapper && wrapper.style.display === 'none') {
3130
+ toggleChatPanel();
3131
+ }
2060
3132
  }
2061
3133
 
2062
3134
  // Set input and send
@@ -2070,17 +3142,31 @@ Be friendly and concise.`
2070
3142
  // Register Service Worker for PWA
2071
3143
  if ('serviceWorker' in navigator) {
2072
3144
  window.addEventListener('load', () => {
2073
- navigator.serviceWorker.register('/viewer/sw.js')
2074
- .then(reg => console.log('[PWA] Service Worker registered:', reg.scope))
2075
- .catch(err => console.warn('[PWA] Service Worker registration failed:', err));
3145
+ navigator.serviceWorker
3146
+ .register('/viewer/sw.js')
3147
+ .then((reg) => console.log('[PWA] Service Worker registered:', reg.scope))
3148
+ .catch((err) => console.warn('[PWA] Service Worker registration failed:', err));
2076
3149
  });
2077
3150
  }
2078
3151
  </script>
2079
3152
 
2080
3153
  <!-- Image Lightbox -->
2081
- <div id="image-lightbox" class="fixed inset-0 z-[9999] hidden items-center justify-center bg-black/80 backdrop-blur-sm cursor-pointer" onclick="closeLightbox()">
2082
- <img id="lightbox-img" class="max-w-[90vw] max-h-[90vh] rounded-lg shadow-2xl" onclick="event.stopPropagation()" />
2083
- <button class="absolute top-4 right-4 text-white text-3xl font-bold hover:text-gray-300" onclick="closeLightbox()">&times;</button>
3154
+ <div
3155
+ id="image-lightbox"
3156
+ class="fixed inset-0 z-[9999] hidden items-center justify-center bg-black/80 backdrop-blur-sm cursor-pointer"
3157
+ onclick="closeLightbox()"
3158
+ >
3159
+ <img
3160
+ id="lightbox-img"
3161
+ class="max-w-[90vw] max-h-[90vh] rounded-lg shadow-2xl"
3162
+ onclick="event.stopPropagation()"
3163
+ />
3164
+ <button
3165
+ class="absolute top-4 right-4 text-white text-3xl font-bold hover:text-gray-300"
3166
+ onclick="closeLightbox()"
3167
+ >
3168
+ &times;
3169
+ </button>
2084
3170
  </div>
2085
3171
  <script>
2086
3172
  function openLightbox(src) {
@@ -2094,11 +3180,16 @@ Be friendly and concise.`
2094
3180
  lb.classList.add('hidden');
2095
3181
  lb.classList.remove('flex');
2096
3182
  }
2097
- document.addEventListener('keydown', e => { if (e.key === 'Escape') closeLightbox(); });
3183
+ document.addEventListener('keydown', (e) => {
3184
+ if (e.key === 'Escape') closeLightbox();
3185
+ });
2098
3186
  // Event delegation for lightbox — avoids inline onclick XSS
2099
- document.addEventListener('click', e => {
3187
+ document.addEventListener('click', (e) => {
2100
3188
  const el = e.target.closest('[data-lightbox]');
2101
- if (el) { e.stopPropagation(); openLightbox(el.dataset.lightbox); }
3189
+ if (el) {
3190
+ e.stopPropagation();
3191
+ openLightbox(el.dataset.lightbox);
3192
+ }
2102
3193
  });
2103
3194
  </script>
2104
3195
  </body>