aimeat 1.16.2 → 1.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.env.example +6 -1
- package/dist/locales/en.json +166 -4
- package/dist/locales/fi.json +166 -4
- package/dist/public/components/Card.js +17 -3
- package/dist/public/components/Collapsible.js +39 -0
- package/dist/public/components/CopyButton.js +55 -24
- package/dist/public/components/DataTable.js +57 -0
- package/dist/public/components/EmptyState.js +28 -0
- package/dist/public/components/KeyValueRow.js +37 -0
- package/dist/public/components/Modal.js +22 -5
- package/dist/public/components/Pagination.js +70 -0
- package/dist/public/components/SearchBar.js +44 -0
- package/dist/public/components/StatusDot.js +29 -0
- package/dist/public/components/TagList.js +53 -0
- package/dist/public/components/ToggleSwitch.js +28 -0
- package/dist/public/components/index.js +41 -1
- package/dist/public/css/components/tags.css +24 -0
- package/dist/public/css/style-testbed.css +135 -0
- package/dist/public/css/theme.css +556 -34
- package/dist/public/css/views/admin-agent-integration.css +2 -33
- package/dist/public/css/views/admin.css +730 -686
- package/dist/public/css/views/agents-detail.css +402 -14
- package/dist/public/css/views/help.css +2 -1
- package/dist/public/css/views/hobbies.css +53 -33
- package/dist/public/css/views/marketplace.css +73 -60
- package/dist/public/css/views/openclaw.css +12 -6
- package/dist/public/css/views/portal-classic.css +34 -26
- package/dist/public/css/views/portal-dev.css +41 -32
- package/dist/public/css/views/portal.css +14 -18
- package/dist/public/css/views/profile.css +252 -164
- package/dist/public/css/views/public-knowledge-viewer.css +18 -6
- package/dist/public/css/views/scheduler.css +100 -0
- package/dist/public/js/format.js +41 -0
- package/dist/public/js/services/agent-tasks.js +14 -1
- package/dist/public/js/services/generator-spec-validate.js +5 -1
- package/dist/public/js/services/schedules.js +53 -0
- package/dist/public/js/style-testbed.js +170 -0
- package/dist/public/js/theme.js +114 -0
- package/dist/public/spa.html +74 -16
- package/dist/public/style-testbed.html +420 -0
- package/dist/public/views/admin/agent-integration-tab.js +23 -38
- package/dist/public/views/admin/agent-tasks-tab.js +14 -21
- package/dist/public/views/admin/chat-instances-tab.js +1 -1
- package/dist/public/views/admin/config-tab.js +7 -2
- package/dist/public/views/admin/cortex-tab.js +20 -14
- package/dist/public/views/admin/csm-tab.js +22 -18
- package/dist/public/views/admin/directory-tab.js +3 -3
- package/dist/public/views/admin/email-tab.js +23 -28
- package/dist/public/views/admin/federation-tab.js +15 -5
- package/dist/public/views/admin/generator-debug-tab.js +10 -8
- package/dist/public/views/admin/genesis-tab.js +4 -4
- package/dist/public/views/admin/ghii-tab.js +3 -3
- package/dist/public/views/admin/knowledge-tab.js +15 -5
- package/dist/public/views/admin/matching-tab.js +3 -3
- package/dist/public/views/admin/memory-tab.js +16 -20
- package/dist/public/views/admin/msm-tab.js +30 -17
- package/dist/public/views/admin/overview-tab.js +7 -7
- package/dist/public/views/admin/packages-tab.js +10 -9
- package/dist/public/views/admin/portal-tab.js +14 -4
- package/dist/public/views/admin/prompts-tab.js +5 -5
- package/dist/public/views/admin/push-tab.js +15 -4
- package/dist/public/views/admin/realtime-tab.js +3 -3
- package/dist/public/views/admin/services-tab.js +18 -27
- package/dist/public/views/admin/shared.js +48 -58
- package/dist/public/views/admin/sharing-groups-tab.js +5 -10
- package/dist/public/views/admin/stats-tab.js +3 -3
- package/dist/public/views/guides.js +9 -13
- package/dist/public/views/help.js +3 -24
- package/dist/public/views/hobbies.js +47 -41
- package/dist/public/views/marketplace.js +42 -34
- package/dist/public/views/openclaw.js +0 -1
- package/dist/public/views/portal-classic.js +4 -15
- package/dist/public/views/portal-dev.js +28 -30
- package/dist/public/views/portal.js +16 -21
- package/dist/public/views/portfolio.js +11 -12
- package/dist/public/views/profile/access-tab.js +250 -1
- package/dist/public/views/profile/agents/agent-card.js +75 -4
- package/dist/public/views/profile/agents/tab-activity.js +15 -13
- package/dist/public/views/profile/agents/tab-agent-config.js +60 -6
- package/dist/public/views/profile/agents/tab-integration.js +37 -60
- package/dist/public/views/profile/agents/tab-messages.js +10 -1
- package/dist/public/views/profile/agents/tab-quality.js +25 -19
- package/dist/public/views/profile/agents/tab-schedules.js +110 -0
- package/dist/public/views/profile/agents-activity-subtab.js +4 -4
- package/dist/public/views/profile/agents-services-subtab.js +5 -4
- package/dist/public/views/profile/agents-tab.js +141 -37
- package/dist/public/views/profile/agents-tasks-subtab.js +221 -26
- package/dist/public/views/profile/calibrator-batch.js +5 -11
- package/dist/public/views/profile/calibrator-tab.js +4 -9
- package/dist/public/views/profile/chat-sessions-tab.js +9 -5
- package/dist/public/views/profile/email-tab.js +2 -2
- package/dist/public/views/profile/extensions-tab.js +27 -22
- package/dist/public/views/profile/federation-tab.js +4 -1
- package/dist/public/views/profile/foundry-detail.js +22 -23
- package/dist/public/views/profile/foundry-tab.js +12 -13
- package/dist/public/views/profile/generator-dashboard/DebugPanel.js +4 -7
- package/dist/public/views/profile/generator-dashboard/use-dashboard-core.js +4 -1
- package/dist/public/views/profile/generator-dashboard/use-edit-mode.js +3 -2
- package/dist/public/views/profile/generator-detail.js +26 -30
- package/dist/public/views/profile/generator-tab.js +5 -5
- package/dist/public/views/profile/knowledge-tab.js +12 -12
- package/dist/public/views/profile/landing-page.js +100 -149
- package/dist/public/views/profile/memory-tab.js +15 -11
- package/dist/public/views/profile/node-stats-tab.js +3 -3
- package/dist/public/views/profile/nodes-tab.js +10 -3
- package/dist/public/views/profile/notifications-tab.js +3 -3
- package/dist/public/views/profile/packages-tab.js +14 -14
- package/dist/public/views/profile/scheduler-tab.js +340 -0
- package/dist/public/views/profile/services-tab.js +1 -1
- package/dist/public/views/profile/shared.js +16 -12
- package/dist/public/views/profile/wallet-tab.js +27 -50
- package/dist/public/views/profile/work-tab.js +29 -31
- package/dist/public/views/profile.js +24 -0
- package/dist/public/views/public-knowledge-viewer.js +9 -18
- package/dist/src/auth/middleware.d.ts +3 -2
- package/dist/src/auth/middleware.d.ts.map +1 -1
- package/dist/src/auth/middleware.js +78 -3
- package/dist/src/auth/middleware.js.map +1 -1
- package/dist/src/cli/init-wizard.d.ts.map +1 -1
- package/dist/src/cli/init-wizard.js +13 -1
- package/dist/src/cli/init-wizard.js.map +1 -1
- package/dist/src/config.d.ts +6 -0
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +12 -1
- package/dist/src/config.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +708 -8
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/mcp/agent-schedules.d.ts +23 -0
- package/dist/src/mcp/agent-schedules.d.ts.map +1 -0
- package/dist/src/mcp/agent-schedules.js +175 -0
- package/dist/src/mcp/agent-schedules.js.map +1 -0
- package/dist/src/mcp/annotations.d.ts.map +1 -1
- package/dist/src/mcp/annotations.js +6 -0
- package/dist/src/mcp/annotations.js.map +1 -1
- package/dist/src/mcp/catalog/definitions.d.ts.map +1 -1
- package/dist/src/mcp/catalog/definitions.js +55 -0
- package/dist/src/mcp/catalog/definitions.js.map +1 -1
- package/dist/src/mcp/catalog/scopes.d.ts.map +1 -1
- package/dist/src/mcp/catalog/scopes.js +12 -0
- package/dist/src/mcp/catalog/scopes.js.map +1 -1
- package/dist/src/mcp/catalog/surfaces.d.ts.map +1 -1
- package/dist/src/mcp/catalog/surfaces.js +2 -0
- package/dist/src/mcp/catalog/surfaces.js.map +1 -1
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +2 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/models/agent-task-schemas.d.ts +6 -0
- package/dist/src/models/agent-task-schemas.d.ts.map +1 -1
- package/dist/src/models/agent-task-schemas.js +6 -0
- package/dist/src/models/agent-task-schemas.js.map +1 -1
- package/dist/src/models/schemas.d.ts +1 -0
- package/dist/src/models/schemas.d.ts.map +1 -1
- package/dist/src/models/schemas.js +4 -0
- package/dist/src/models/schemas.js.map +1 -1
- package/dist/src/routes/access-tokens.d.ts +17 -0
- package/dist/src/routes/access-tokens.d.ts.map +1 -0
- package/dist/src/routes/access-tokens.js +164 -0
- package/dist/src/routes/access-tokens.js.map +1 -0
- package/dist/src/routes/agent-tasks.d.ts +2 -0
- package/dist/src/routes/agent-tasks.d.ts.map +1 -1
- package/dist/src/routes/agent-tasks.js +118 -23
- package/dist/src/routes/agent-tasks.js.map +1 -1
- package/dist/src/routes/agents.d.ts.map +1 -1
- package/dist/src/routes/agents.js +46 -0
- package/dist/src/routes/agents.js.map +1 -1
- package/dist/src/routes/ai.d.ts +6 -9
- package/dist/src/routes/ai.d.ts.map +1 -1
- package/dist/src/routes/ai.js +26 -175
- package/dist/src/routes/ai.js.map +1 -1
- package/dist/src/routes/apps.d.ts.map +1 -1
- package/dist/src/routes/apps.js +104 -34
- package/dist/src/routes/apps.js.map +1 -1
- package/dist/src/routes/auth.d.ts.map +1 -1
- package/dist/src/routes/auth.js +77 -4
- package/dist/src/routes/auth.js.map +1 -1
- package/dist/src/routes/cortex.d.ts +2 -1
- package/dist/src/routes/cortex.d.ts.map +1 -1
- package/dist/src/routes/cortex.js +13 -8
- package/dist/src/routes/cortex.js.map +1 -1
- package/dist/src/routes/extensions.d.ts.map +1 -1
- package/dist/src/routes/extensions.js +85 -51
- package/dist/src/routes/extensions.js.map +1 -1
- package/dist/src/routes/generator.d.ts.map +1 -1
- package/dist/src/routes/generator.js +109 -43
- package/dist/src/routes/generator.js.map +1 -1
- package/dist/src/routes/ghii.d.ts.map +1 -1
- package/dist/src/routes/ghii.js +34 -13
- package/dist/src/routes/ghii.js.map +1 -1
- package/dist/src/routes/libs.d.ts +15 -0
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +178 -55
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/portal.d.ts.map +1 -1
- package/dist/src/routes/portal.js +1 -0
- package/dist/src/routes/portal.js.map +1 -1
- package/dist/src/routes/schedules.d.ts +33 -0
- package/dist/src/routes/schedules.d.ts.map +1 -0
- package/dist/src/routes/schedules.js +380 -0
- package/dist/src/routes/schedules.js.map +1 -0
- package/dist/src/server-bootstrap/config-init.js +2 -2
- package/dist/src/server-bootstrap/config-init.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +7 -0
- package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
- package/dist/src/server-bootstrap/service-init.d.ts.map +1 -1
- package/dist/src/server-bootstrap/service-init.js +2 -1
- package/dist/src/server-bootstrap/service-init.js.map +1 -1
- package/dist/src/services/access-token.d.ts +28 -0
- package/dist/src/services/access-token.d.ts.map +1 -0
- package/dist/src/services/access-token.js +37 -0
- package/dist/src/services/access-token.js.map +1 -0
- package/dist/src/services/ai-completion.d.ts +83 -0
- package/dist/src/services/ai-completion.d.ts.map +1 -0
- package/dist/src/services/ai-completion.js +187 -0
- package/dist/src/services/ai-completion.js.map +1 -0
- package/dist/src/services/config-schema.d.ts.map +1 -1
- package/dist/src/services/config-schema.js +3 -0
- package/dist/src/services/config-schema.js.map +1 -1
- package/dist/src/services/generator-prompt-seeds.d.ts.map +1 -1
- package/dist/src/services/generator-prompt-seeds.js +6 -3
- package/dist/src/services/generator-prompt-seeds.js.map +1 -1
- package/dist/src/services/generator-prompts/spec-validate.d.ts.map +1 -1
- package/dist/src/services/generator-prompts/spec-validate.js +6 -2
- package/dist/src/services/generator-prompts/spec-validate.js.map +1 -1
- package/dist/src/services/generator-registration.d.ts +12 -4
- package/dist/src/services/generator-registration.d.ts.map +1 -1
- package/dist/src/services/generator-registration.js +71 -0
- package/dist/src/services/generator-registration.js.map +1 -1
- package/dist/src/services/generator-validate.d.ts +10 -0
- package/dist/src/services/generator-validate.d.ts.map +1 -1
- package/dist/src/services/generator-validate.js +180 -36
- package/dist/src/services/generator-validate.js.map +1 -1
- package/dist/src/services/owner-session.d.ts +43 -0
- package/dist/src/services/owner-session.d.ts.map +1 -0
- package/dist/src/services/owner-session.js +175 -0
- package/dist/src/services/owner-session.js.map +1 -0
- package/dist/src/services/schedule-constraints.d.ts +65 -0
- package/dist/src/services/schedule-constraints.d.ts.map +1 -0
- package/dist/src/services/schedule-constraints.js +106 -0
- package/dist/src/services/schedule-constraints.js.map +1 -0
- package/dist/src/services/scheduler.d.ts +34 -0
- package/dist/src/services/scheduler.d.ts.map +1 -1
- package/dist/src/services/scheduler.js +266 -22
- package/dist/src/services/scheduler.js.map +1 -1
- package/dist/src/services/skill-bundle/generator.js +19 -0
- package/dist/src/services/skill-bundle/generator.js.map +1 -1
- package/dist/src/storage/interface.d.ts +48 -4
- package/dist/src/storage/interface.d.ts.map +1 -1
- package/dist/src/storage/interface.js.map +1 -1
- package/dist/src/storage/providers/mongodb/index.d.ts +31 -0
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +187 -9
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +31 -0
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +131 -8
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-task.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/agent-task.js +6 -4
- package/dist/src/storage/providers/sqlite/repos/agent-task.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +79 -8
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/pat.repository.d.ts +40 -0
- package/dist/src/storage/repositories/pat.repository.d.ts.map +1 -0
- package/dist/src/storage/repositories/pat.repository.js +14 -0
- package/dist/src/storage/repositories/pat.repository.js.map +1 -0
- package/dist/src/storage/repositories/scheduler.repository.d.ts +2 -0
- package/dist/src/storage/repositories/scheduler.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/session.repository.d.ts +51 -0
- package/dist/src/storage/repositories/session.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/session.repository.js +12 -0
- package/dist/src/storage/repositories/session.repository.js.map +1 -1
- package/dist/src/utils/env-config.d.ts.map +1 -1
- package/dist/src/utils/env-config.js +25 -1
- package/dist/src/utils/env-config.js.map +1 -1
- package/dist/src/utils/env-validator.d.ts.map +1 -1
- package/dist/src/utils/env-validator.js +4 -0
- package/dist/src/utils/env-validator.js.map +1 -1
- package/package.json +1 -1
- package/prisma/schema.prisma +48 -0
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* @file services-tab.js
|
|
3
|
+
* @description Admin Dashboard — Services Tab. Manage installed service
|
|
4
|
+
* extensions and their instances, install bundled extensions one-click,
|
|
5
|
+
* edit action scripts and instance translations.
|
|
6
|
+
* @version-history
|
|
7
|
+
* v1.1.0 — 2026-06-02 — Admin design unification: raw script/JSON textareas
|
|
8
|
+
* → adm-textarea adm-input-full (drop inline mono/resize/border styles).
|
|
5
9
|
*/
|
|
6
10
|
import { h } from 'preact';
|
|
7
11
|
import { useState, useEffect } from 'preact/hooks';
|
|
@@ -17,6 +21,7 @@ import {
|
|
|
17
21
|
getDiskScript, saveDiskScript, addDiskAction, reinstallExtension,
|
|
18
22
|
} from '/js/services/admin.js';
|
|
19
23
|
import { useConfirm } from '/components/Modal.js';
|
|
24
|
+
import { CopyButton } from '/components/CopyButton.js';
|
|
20
25
|
|
|
21
26
|
const inputStyle = 'adm-input';
|
|
22
27
|
const labelStyle = 'adm-text-sm adm-text-dim';
|
|
@@ -212,22 +217,6 @@ function TranslationEditor({ extName, inst, onSave }) {
|
|
|
212
217
|
setSaving(false);
|
|
213
218
|
}
|
|
214
219
|
|
|
215
|
-
async function copyAiPrompt() {
|
|
216
|
-
const prompt = buildTranslationAiPrompt(extName, inst.id, allKeys.length > 0 ? allKeys : ['(no keys detected — add categories to config first)'], locale, translations);
|
|
217
|
-
try {
|
|
218
|
-
await navigator.clipboard.writeText(prompt);
|
|
219
|
-
setMsg({ ok: true, text: t('dashboard.servicesTlPromptCopied') });
|
|
220
|
-
} catch {
|
|
221
|
-
const ta = document.createElement('textarea');
|
|
222
|
-
ta.value = prompt;
|
|
223
|
-
document.body.appendChild(ta);
|
|
224
|
-
ta.select();
|
|
225
|
-
document.execCommand('copy');
|
|
226
|
-
document.body.removeChild(ta);
|
|
227
|
-
setMsg({ ok: true, text: t('dashboard.servicesTlPromptCopied') });
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
220
|
const displayKeys = [...new Set([...allKeys, ...Object.keys(translations)])].sort();
|
|
232
221
|
|
|
233
222
|
return html`
|
|
@@ -241,13 +230,15 @@ function TranslationEditor({ extName, inst, onSave }) {
|
|
|
241
230
|
</select>
|
|
242
231
|
<button class="adm-btn-sm" onClick=${() => { setJsonMode(!jsonMode); if (!jsonMode) setJsonText(JSON.stringify(translations, null, 2)); }}
|
|
243
232
|
style="font-size:.75rem">${jsonMode ? t('dashboard.servicesTlFormMode') : 'JSON'}</button>
|
|
244
|
-
|
|
245
|
-
|
|
233
|
+
<${CopyButton} className="adm-btn-sm"
|
|
234
|
+
text=${buildTranslationAiPrompt(extName, inst.id, allKeys.length > 0 ? allKeys : ['(no keys detected — add categories to config first)'], locale, translations)}
|
|
235
|
+
label=${t('dashboard.servicesTlAiPrompt')} copiedLabel=${t('dashboard.servicesTlAiPrompt')}
|
|
236
|
+
onCopied=${() => setMsg({ ok: true, text: t('dashboard.servicesTlPromptCopied') })} />
|
|
246
237
|
</div>
|
|
247
238
|
|
|
248
239
|
${jsonMode ? html`
|
|
249
|
-
<textarea value=${jsonText} onInput=${e => setJsonText(e.target.value)}
|
|
250
|
-
style="
|
|
240
|
+
<textarea class="adm-textarea adm-input-full" value=${jsonText} onInput=${e => setJsonText(e.target.value)}
|
|
241
|
+
style="height:200px;font-size:12px"
|
|
251
242
|
spellcheck="false" />
|
|
252
243
|
` : html`
|
|
253
244
|
<div class="adm-flex-col" style="gap:4px;max-height:300px;overflow-y:auto">
|
|
@@ -328,8 +319,8 @@ function ActionScriptEditor({ extName, actions, onUpdated }) {
|
|
|
328
319
|
<div class="adm-flex-between adm-mb-xs">
|
|
329
320
|
<code style="font-size:.8rem;color:var(--text-dim)">${extName}/${selectedAction}</code>
|
|
330
321
|
</div>
|
|
331
|
-
<textarea value=${script} onInput=${e => setScript(e.target.value)}
|
|
332
|
-
style="
|
|
322
|
+
<textarea class="adm-textarea adm-input-full" value=${script} onInput=${e => setScript(e.target.value)}
|
|
323
|
+
style="height:320px;font-size:12px"
|
|
333
324
|
spellcheck="false"
|
|
334
325
|
onKeyDown=${e => {
|
|
335
326
|
if (e.key === 'Tab') {
|
|
@@ -791,8 +782,8 @@ function AvailableExtCard({ ext, isInstalled, isInstalling, onInstall, onReinsta
|
|
|
791
782
|
${selectedAction && !loading && html`
|
|
792
783
|
<div>
|
|
793
784
|
<code class="adm-text-sm adm-text-dim">${ext.name}/actions/${selectedAction}.js</code>
|
|
794
|
-
<textarea value=${script} onInput=${e => setScript(e.target.value)}
|
|
795
|
-
style="
|
|
785
|
+
<textarea class="adm-textarea adm-input-full" value=${script} onInput=${e => setScript(e.target.value)}
|
|
786
|
+
style="height:320px;font-size:12px;margin-top:4px"
|
|
796
787
|
spellcheck="false"
|
|
797
788
|
onKeyDown=${e => {
|
|
798
789
|
if (e.key === 'Tab') {
|
|
@@ -1,48 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* @file shared.js
|
|
3
|
+
* @description Admin Dashboard shared UI helpers — the admin design system's own
|
|
4
|
+
* primitives (Badge, StatCard, StatsGrid, Spinner, Empty, ErrorBox, DataTable,
|
|
5
|
+
* ExpandableHelp, useToast/Toast, EconRow/HealthRow) + formatters. Admin is a
|
|
6
|
+
* self-contained design system (adm-* scoped); these are intentionally separate
|
|
7
|
+
* from the main /components primitives.
|
|
8
|
+
* @version-history
|
|
9
|
+
* v1.2.0 — 2026-06-02 — Component unification (#13 tables): DataTable is now a
|
|
10
|
+
* thin wrapper that renders `.adm-card` around the canonical
|
|
11
|
+
* /components/DataTable.js (imported, not bare-re-exported). Admin keeps its
|
|
12
|
+
* card wrapper and unchanged appearance; the table body is shared.
|
|
13
|
+
* v1.1.0 — 2026-06-02 — i18n the Spinner/ErrorBox defaults (t('common.loading') /
|
|
14
|
+
* t('common.error')) — were hardcoded English (Rule 4/7.8).
|
|
4
15
|
*/
|
|
5
16
|
import { h } from 'preact';
|
|
6
17
|
import { useState } from 'preact/hooks';
|
|
7
18
|
import htm from 'htm';
|
|
8
19
|
const html = htm.bind(h);
|
|
9
20
|
import { escHtml } from '/js/utils.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Format uptime seconds to human-readable */
|
|
22
|
-
export function fmtUp(s) {
|
|
23
|
-
const d = Math.floor(s / 86400);
|
|
24
|
-
const hr = Math.floor((s % 86400) / 3600);
|
|
25
|
-
const m = Math.floor((s % 3600) / 60);
|
|
26
|
-
return (d ? d + 'd ' : '') + (hr ? hr + 'h ' : '') + (m ? m + 'm' : '<1m');
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/** Format bytes to human-readable */
|
|
30
|
-
export function fmtBytes(b) {
|
|
31
|
-
if (b < 1024) return b + ' B';
|
|
32
|
-
if (b < 1048576) return (b / 1024).toFixed(1) + ' KB';
|
|
33
|
-
return (b / 1048576).toFixed(1) + ' MB';
|
|
34
|
-
}
|
|
21
|
+
import { t } from '/js/i18n.js';
|
|
22
|
+
import { EmptyState } from '/components/EmptyState.js';
|
|
23
|
+
// Import (not bare re-export) so we hold a local binding to wrap below — a
|
|
24
|
+
// `export { DataTable } from ...` would NOT create a usable local reference.
|
|
25
|
+
import { DataTable as GenericDataTable } from '/components/DataTable.js';
|
|
26
|
+
|
|
27
|
+
// Display formatters now live in the shared /js/format.js. Import them into local
|
|
28
|
+
// scope (StatCard etc. call num() directly) AND re-export so the existing admin
|
|
29
|
+
// importers (`import { num, dt, fmtUp, fmtBytes } from './shared.js'`) keep working.
|
|
30
|
+
import { num, dt, fmtUp, fmtBytes } from '/js/format.js';
|
|
31
|
+
export { num, dt, fmtUp, fmtBytes };
|
|
35
32
|
|
|
36
33
|
/** Render a badge */
|
|
37
34
|
export function Badge({ type }) {
|
|
38
35
|
return html`<span class="adm-badge adm-badge-${type}">${type}</span>`;
|
|
39
36
|
}
|
|
40
37
|
|
|
41
|
-
/**
|
|
42
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Render a stat card.
|
|
40
|
+
* @param {{ label: string, value: any, sub?: string, tone?: string, color?: string }} props
|
|
41
|
+
* tone — theme-aware modifier class (indigo|mint|green|cyan|amber|purple|blue|red). Preferred.
|
|
42
|
+
* color — legacy inline color (still honored if no tone); migrate callers to `tone`.
|
|
43
|
+
*/
|
|
44
|
+
export function StatCard({ label, value, sub, tone, color }) {
|
|
45
|
+
const toneClass = tone ? ` ${tone}` : '';
|
|
46
|
+
const style = !tone && color ? `color:${color}` : '';
|
|
43
47
|
return html`<div class="adm-card">
|
|
44
48
|
<h2>${label}</h2>
|
|
45
|
-
<div class="adm-stat" style
|
|
49
|
+
<div class="adm-stat${toneClass}" style=${style}>${num(value)}</div>
|
|
46
50
|
${sub && html`<div class="adm-stat-label">${sub}</div>`}
|
|
47
51
|
</div>`;
|
|
48
52
|
}
|
|
@@ -50,7 +54,7 @@ export function StatCard({ label, value, sub, color }) {
|
|
|
50
54
|
/** Render a stats grid (4-column) */
|
|
51
55
|
export function StatsGrid({ items }) {
|
|
52
56
|
return html`<div class="adm-grid adm-grid-4">
|
|
53
|
-
${items.map(i => html`<${StatCard} label=${i.label} value=${i.value} sub=${i.sub} color=${i.color} />`)}
|
|
57
|
+
${items.map(i => html`<${StatCard} label=${i.label} value=${i.value} sub=${i.sub} tone=${i.tone} color=${i.color} />`)}
|
|
54
58
|
</div>`;
|
|
55
59
|
}
|
|
56
60
|
|
|
@@ -72,17 +76,17 @@ export function HealthRow({ label, obj }) {
|
|
|
72
76
|
|
|
73
77
|
/** Loading spinner */
|
|
74
78
|
export function Spinner({ text }) {
|
|
75
|
-
return html`<div class="empty"><div class="spinner"></div> ${text || '
|
|
79
|
+
return html`<div class="empty"><div class="spinner"></div> ${text || t('common.loading')}</div>`;
|
|
76
80
|
}
|
|
77
81
|
|
|
78
|
-
/** Empty state */
|
|
82
|
+
/** Empty state — delegates to the canonical /components/EmptyState.js. */
|
|
79
83
|
export function Empty({ text }) {
|
|
80
|
-
return html
|
|
84
|
+
return html`<${EmptyState} text=${text} />`;
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
/** Error box */
|
|
84
88
|
export function ErrorBox({ message }) {
|
|
85
|
-
return html`<div class="error-box"><strong
|
|
89
|
+
return html`<div class="error-box"><strong>${t('common.error')}</strong><br/>${escHtml(message)}</div>`;
|
|
86
90
|
}
|
|
87
91
|
|
|
88
92
|
/** Expandable/collapsible help section — reusable across all tabs and portal pages */
|
|
@@ -94,33 +98,19 @@ export function ExpandableHelp({ title, children }) {
|
|
|
94
98
|
}
|
|
95
99
|
|
|
96
100
|
/**
|
|
97
|
-
* DataTable —
|
|
101
|
+
* DataTable (admin) — thin wrapper around the canonical
|
|
102
|
+
* /components/DataTable.js that adds admin's `.adm-card` container. The 36
|
|
103
|
+
* admin importers keep the same `{ headers, rows, scroll }` signature and the
|
|
104
|
+
* same admin appearance (the `.adm table` / `.adm .scrollable` / `.adm .mono`
|
|
105
|
+
* scoped CSS still wins over the generic `.data-table` inside `.adm`).
|
|
98
106
|
*
|
|
99
|
-
* SECURITY:
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* Only use `_html` for trusted, server-generated markup like badges.
|
|
107
|
+
* SECURITY: cell objects with `_html: true` render `cell.text` as raw HTML;
|
|
108
|
+
* callers MUST sanitize (escHtml()) any user-generated content. See the
|
|
109
|
+
* generic DataTable for the full cell protocol.
|
|
103
110
|
*/
|
|
104
111
|
export function DataTable({ headers, rows, scroll }) {
|
|
105
|
-
const table = html`<table>
|
|
106
|
-
<thead><tr>${headers.map(h => html`<th>${h}</th>`)}</tr></thead>
|
|
107
|
-
<tbody>
|
|
108
|
-
${rows.map(row => html`<tr>
|
|
109
|
-
${row.map(cell => {
|
|
110
|
-
if (cell && typeof cell === 'object' && cell._html) {
|
|
111
|
-
return html`<td class=${cell.mono ? 'mono' : ''} title=${cell.title || ''}
|
|
112
|
-
dangerouslySetInnerHTML=${{ __html: cell.text }}></td>`;
|
|
113
|
-
}
|
|
114
|
-
if (cell && typeof cell === 'object' && cell.mono) {
|
|
115
|
-
return html`<td class="mono" title=${cell.title || ''}>${cell.text}</td>`;
|
|
116
|
-
}
|
|
117
|
-
return html`<td>${cell}</td>`;
|
|
118
|
-
})}
|
|
119
|
-
</tr>`)}
|
|
120
|
-
</tbody>
|
|
121
|
-
</table>`;
|
|
122
112
|
return html`<div class="adm-card">
|
|
123
|
-
|
|
113
|
+
<${GenericDataTable} headers=${headers} rows=${rows} scroll=${scroll} />
|
|
124
114
|
</div>`;
|
|
125
115
|
}
|
|
126
116
|
|
|
@@ -11,7 +11,7 @@ import htm from 'htm';
|
|
|
11
11
|
const html = htm.bind(h);
|
|
12
12
|
import { t } from '/js/i18n.js';
|
|
13
13
|
import { escHtml } from '/js/utils.js';
|
|
14
|
-
import { num, dt, Empty } from './shared.js';
|
|
14
|
+
import { num, dt, Empty, StatsGrid } from './shared.js';
|
|
15
15
|
import { apiGet } from '/js/api.js';
|
|
16
16
|
|
|
17
17
|
export default function SharingGroupsTab({ data, reload, session }) {
|
|
@@ -19,8 +19,8 @@ export default function SharingGroupsTab({ data, reload, session }) {
|
|
|
19
19
|
const [total, setTotal] = useState(0);
|
|
20
20
|
const [loading, setLoading] = useState(false);
|
|
21
21
|
|
|
22
|
-
const loadGroups = useCallback(async () => {
|
|
23
|
-
setLoading(true);
|
|
22
|
+
const loadGroups = useCallback(async ({ showSpinner = true } = {}) => {
|
|
23
|
+
if (showSpinner) setLoading(true);
|
|
24
24
|
try {
|
|
25
25
|
const r = await apiGet('/v1/admin/sharing-groups');
|
|
26
26
|
if (r.data) {
|
|
@@ -37,7 +37,7 @@ export default function SharingGroupsTab({ data, reload, session }) {
|
|
|
37
37
|
|
|
38
38
|
// Listen for live updates
|
|
39
39
|
useEffect(() => {
|
|
40
|
-
const handler = () => { loadGroups(); };
|
|
40
|
+
const handler = () => { loadGroups({ showSpinner: false }); }; // silent: no flash
|
|
41
41
|
window.addEventListener('aimeat-live-update', handler);
|
|
42
42
|
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
43
43
|
}, [loadGroups]);
|
|
@@ -45,12 +45,7 @@ export default function SharingGroupsTab({ data, reload, session }) {
|
|
|
45
45
|
return html`
|
|
46
46
|
<div>
|
|
47
47
|
<!-- Stats summary -->
|
|
48
|
-
|
|
49
|
-
<div class="adm-mem-stat">
|
|
50
|
-
<span class="adm-mem-stat-value">${num(total)}</span>
|
|
51
|
-
<span class="adm-mem-stat-label">${t('dashboard.sharingGroupsTotal')}</span>
|
|
52
|
-
</div>
|
|
53
|
-
</div>
|
|
48
|
+
<${StatsGrid} items=${[{ label: t('dashboard.sharingGroupsTotal'), value: total }]} />
|
|
54
49
|
|
|
55
50
|
<!-- Table -->
|
|
56
51
|
${groups.length === 0 && !loading && html`<${Empty} text=${t('dashboard.sharingGroupsEmpty')} />`}
|
|
@@ -137,8 +137,8 @@ export default function StatsTab({ data }) {
|
|
|
137
137
|
const [loading, setLoading] = useState(false);
|
|
138
138
|
const chartRefs = useRef({});
|
|
139
139
|
|
|
140
|
-
const fetchStats = useCallback(async (p, cf, ct) => {
|
|
141
|
-
setLoading(true);
|
|
140
|
+
const fetchStats = useCallback(async (p, cf, ct, { showSpinner = true } = {}) => {
|
|
141
|
+
if (showSpinner) setLoading(true);
|
|
142
142
|
try {
|
|
143
143
|
const range = p === 'custom'
|
|
144
144
|
? (cf && ct ? { from: cf, to: ct } : null)
|
|
@@ -155,7 +155,7 @@ export default function StatsTab({ data }) {
|
|
|
155
155
|
|
|
156
156
|
// Listen for live updates
|
|
157
157
|
useEffect(() => {
|
|
158
|
-
const handler = () => { fetchStats(period, customFrom, customTo); };
|
|
158
|
+
const handler = () => { fetchStats(period, customFrom, customTo, { showSpinner: false }); }; // silent: no flash
|
|
159
159
|
window.addEventListener('aimeat-live-update', handler);
|
|
160
160
|
return () => window.removeEventListener('aimeat-live-update', handler);
|
|
161
161
|
}, [period, customFrom, customTo]);
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* @file guides.js
|
|
3
|
+
* @description Guides — interactive guide index + detail views with
|
|
4
|
+
* copy-to-clipboard (markdown) for pasting a whole guide into an AI chat.
|
|
5
|
+
* @version-history
|
|
6
|
+
* v1.1.0 — 2026-06-02 — Migrate bespoke copy button to canonical CopyButton component
|
|
4
7
|
*/
|
|
5
8
|
import { h } from 'preact';
|
|
6
9
|
import { useState, useEffect, useCallback, useRef } from 'preact/hooks';
|
|
7
10
|
import htm from 'htm';
|
|
8
11
|
import { t } from '/js/i18n.js';
|
|
9
|
-
import { escHtml
|
|
12
|
+
import { escHtml } from '/js/utils.js';
|
|
13
|
+
import { CopyButton } from '/components/CopyButton.js';
|
|
10
14
|
|
|
11
15
|
const html = htm.bind(h);
|
|
12
16
|
const NODE_URL = window.location.origin;
|
|
@@ -294,7 +298,6 @@ function GuideIndex({ locale, onSelectGuide, navigate }) {
|
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
function GuideDetail({ slug, locale, onBack }) {
|
|
297
|
-
const [copied, setCopied] = useState(false);
|
|
298
301
|
const labels = LABELS[locale] || LABELS.en;
|
|
299
302
|
const guide = GUIDES[slug];
|
|
300
303
|
if (!guide) return html`<div class="gd-container"><p>Guide not found.</p></div>`;
|
|
@@ -312,12 +315,6 @@ function GuideDetail({ slug, locale, onBack }) {
|
|
|
312
315
|
return md;
|
|
313
316
|
}, [data]);
|
|
314
317
|
|
|
315
|
-
const handleCopy = useCallback(() => {
|
|
316
|
-
copyToClipboard(buildMarkdown());
|
|
317
|
-
setCopied(true);
|
|
318
|
-
setTimeout(() => setCopied(false), 2000);
|
|
319
|
-
}, [buildMarkdown]);
|
|
320
|
-
|
|
321
318
|
useEffect(() => {
|
|
322
319
|
document.title = data.title + ' | AIME AT';
|
|
323
320
|
window.scrollTo(0, 0);
|
|
@@ -334,9 +331,8 @@ function GuideDetail({ slug, locale, onBack }) {
|
|
|
334
331
|
|
|
335
332
|
<div class="gd-copy-section">
|
|
336
333
|
<div class="gd-copy-instruction">${labels.copyInstruction}</div>
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
</button>
|
|
334
|
+
<${CopyButton} text=${buildMarkdown()} className="gd-copy-btn"
|
|
335
|
+
label=${'\uD83D\uDCCB ' + labels.copyBtnLabel} copiedLabel=${'\u2714 ' + labels.copiedLabel} />
|
|
340
336
|
</div>
|
|
341
337
|
|
|
342
338
|
<div class="gd-tech-warning">${labels.techWarning}</div>
|
|
@@ -7,12 +7,14 @@
|
|
|
7
7
|
* @usage import HelpView from '/views/help.js'
|
|
8
8
|
* @version-history
|
|
9
9
|
* v1.0.0 — 2026-03-23 — Initial implementation
|
|
10
|
+
* v1.1.0 — 2026-06-02 — Migrate bespoke copy button to canonical CopyButton component
|
|
10
11
|
*/
|
|
11
12
|
import { h } from 'preact';
|
|
12
13
|
import { useState, useEffect } from 'preact/hooks';
|
|
13
14
|
import htm from 'htm';
|
|
14
15
|
import { t } from '/js/i18n.js';
|
|
15
16
|
import { useViewCSS } from '/components/useViewCSS.js';
|
|
17
|
+
import { CopyButton } from '/components/CopyButton.js';
|
|
16
18
|
|
|
17
19
|
const html = htm.bind(h);
|
|
18
20
|
|
|
@@ -22,7 +24,6 @@ export default function HelpView() {
|
|
|
22
24
|
const [prompt, setPrompt] = useState('');
|
|
23
25
|
const [loading, setLoading] = useState(true);
|
|
24
26
|
const [error, setError] = useState(false);
|
|
25
|
-
const [copied, setCopied] = useState(false);
|
|
26
27
|
|
|
27
28
|
useEffect(() => {
|
|
28
29
|
fetch('/v1/help/prompt')
|
|
@@ -34,26 +35,6 @@ export default function HelpView() {
|
|
|
34
35
|
.catch(() => { setError(true); setLoading(false); });
|
|
35
36
|
}, []);
|
|
36
37
|
|
|
37
|
-
const handleCopy = async () => {
|
|
38
|
-
try {
|
|
39
|
-
await navigator.clipboard.writeText(prompt);
|
|
40
|
-
setCopied(true);
|
|
41
|
-
setTimeout(() => setCopied(false), 2000);
|
|
42
|
-
} catch {
|
|
43
|
-
// Fallback for older browsers
|
|
44
|
-
const ta = document.createElement('textarea');
|
|
45
|
-
ta.value = prompt;
|
|
46
|
-
ta.style.position = 'fixed';
|
|
47
|
-
ta.style.left = '-9999px';
|
|
48
|
-
document.body.appendChild(ta);
|
|
49
|
-
ta.select();
|
|
50
|
-
document.execCommand('copy');
|
|
51
|
-
document.body.removeChild(ta);
|
|
52
|
-
setCopied(true);
|
|
53
|
-
setTimeout(() => setCopied(false), 2000);
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
38
|
return html`
|
|
58
39
|
<div class="hlp-container">
|
|
59
40
|
<div class="hlp-card">
|
|
@@ -69,9 +50,7 @@ export default function HelpView() {
|
|
|
69
50
|
</div>
|
|
70
51
|
|
|
71
52
|
${!loading && !error && html`
|
|
72
|
-
|
|
73
|
-
${copied ? t('help.copied') : t('help.copyBtn')}
|
|
74
|
-
</button>
|
|
53
|
+
<${CopyButton} text=${prompt} label=${t('help.copyBtn')} copiedLabel=${t('help.copied')} className="hlp-copy-btn" />
|
|
75
54
|
`}
|
|
76
55
|
</div>
|
|
77
56
|
</div>
|