aimeat 1.33.2 → 1.34.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/locales/en.json +388 -14
- package/dist/locales/fi.json +388 -14
- package/dist/public/cortex-bundled/aimeat-charts.js +3 -3
- package/dist/public/cortex-bundled/aimeat-charts.yaml +3 -3
- package/dist/public/cortex-bundled/aimeat-i18n.js +123 -0
- package/dist/public/cortex-bundled/aimeat-i18n.yaml +64 -0
- package/dist/public/css/views/landing.css +2 -0
- package/dist/public/css/views/secretary.css +412 -2
- package/dist/public/js/services/apps.js +4 -2
- package/dist/public/js/services/memory.js +9 -3
- package/dist/public/js/services/offers.js +1 -1
- package/dist/public/js/services/organisms.js +49 -17
- package/dist/public/js/services/secretary-helpers.js +262 -17
- package/dist/public/js/services/secretary-policy.js +1 -0
- package/dist/public/js/services/secretary-rules.js +70 -0
- package/dist/public/js/theme.js +20 -0
- package/dist/public/lib/VENDORED-STYLING.md +31 -0
- package/dist/public/lib/aimeat-daisyui-bridge.css +48 -0
- package/dist/public/lib/chartjs@4.js +14 -0
- package/dist/public/lib/daisyui@5.css +7 -0
- package/dist/public/lib/tailwindcss@4.js +8 -0
- package/dist/public/llms-template.txt +9 -6
- package/dist/public/showroom.html +104 -0
- package/dist/public/showroom.js +247 -0
- package/dist/public/spa.html +18 -16
- package/dist/public/views/aimeat-os.js +3 -2
- package/dist/public/views/landing.js +50 -6
- package/dist/public/views/profile/agents/agent-card.js +7 -0
- package/dist/public/views/profile/agents/state-detector.js +6 -0
- package/dist/public/views/profile/agents/tab-integration.js +6 -0
- package/dist/public/views/profile/apps-tab.js +46 -0
- package/dist/public/views/profile/extensions-tab.js +4 -0
- package/dist/public/views/profile/inbox-tab.js +1 -1
- package/dist/public/views/profile/landing-page.js +8 -3
- package/dist/public/views/profile/memory-tab.js +37 -6
- package/dist/public/views/profile/organisms/home.js +32 -0
- package/dist/public/views/profile/organisms/workspace-list.js +36 -5
- package/dist/public/views/profile/organisms/workspace.js +29 -4
- package/dist/public/views/profile/organisms-tab.js +61 -8
- package/dist/public/views/secretary/calendar.js +327 -0
- package/dist/public/views/secretary/cards-reach.js +16 -17
- package/dist/public/views/secretary/cards.js +173 -66
- package/dist/public/views/secretary/dashboard.js +551 -0
- package/dist/public/views/secretary/quality.js +156 -0
- package/dist/public/views/secretary/use-autonomy.js +17 -2
- package/dist/public/views/secretary/use-freshness.js +50 -0
- package/dist/public/views/secretary/use-intake.js +23 -4
- package/dist/public/views/secretary/use-layout.js +122 -0
- package/dist/public/views/secretary/use-quick-actions.js +98 -0
- package/dist/public/views/secretary/use-specialists.js +103 -0
- package/dist/public/views/secretary/use-triggers.js +159 -0
- package/dist/public/views/secretary/use-whats-next.js +340 -0
- package/dist/public/views/secretary/workflow-design.js +112 -0
- package/dist/public/views/secretary.js +663 -39
- package/dist/scripts/check-no-max-tokens.d.ts +2 -0
- package/dist/scripts/check-no-max-tokens.d.ts.map +1 -0
- package/dist/scripts/check-no-max-tokens.js +71 -0
- package/dist/scripts/check-no-max-tokens.js.map +1 -0
- package/dist/src/data/aimeat-iam-package.d.ts +25 -0
- package/dist/src/data/aimeat-iam-package.d.ts.map +1 -0
- package/dist/src/data/aimeat-iam-package.js +312 -0
- package/dist/src/data/aimeat-iam-package.js.map +1 -0
- package/dist/src/data/aimeat-marketplace-package.d.ts +25 -0
- package/dist/src/data/aimeat-marketplace-package.d.ts.map +1 -0
- package/dist/src/data/aimeat-marketplace-package.js +231 -0
- package/dist/src/data/aimeat-marketplace-package.js.map +1 -0
- package/dist/src/data/app-templates.d.ts +48 -0
- package/dist/src/data/app-templates.d.ts.map +1 -0
- package/dist/src/data/app-templates.js +856 -0
- package/dist/src/data/app-templates.js.map +1 -0
- package/dist/src/data/example-packages.d.ts.map +1 -1
- package/dist/src/data/example-packages.js +3 -1
- package/dist/src/data/example-packages.js.map +1 -1
- package/dist/src/generated/api-types.d.ts +380 -5
- package/dist/src/generated/api-types.d.ts.map +1 -1
- package/dist/src/generated/prisma-postgres/edge.js +10 -3
- package/dist/src/generated/prisma-postgres/index-browser.js +7 -0
- package/dist/src/generated/prisma-postgres/index.d.ts +205 -2
- package/dist/src/generated/prisma-postgres/index.js +10 -3
- package/dist/src/generated/prisma-postgres/package.json +1 -1
- package/dist/src/generated/prisma-postgres/schema.prisma +23 -13
- package/dist/src/generated/prisma-postgres/wasm.js +10 -3
- package/dist/src/mcp/annotations.d.ts.map +1 -1
- package/dist/src/mcp/annotations.js +1 -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 +16 -1
- package/dist/src/mcp/catalog/definitions.js.map +1 -1
- package/dist/src/mcp/catalog/surfaces.js +3 -3
- package/dist/src/mcp/catalog/surfaces.js.map +1 -1
- package/dist/src/mcp/index.d.ts +3 -0
- package/dist/src/mcp/index.d.ts.map +1 -1
- package/dist/src/mcp/index.js +11 -0
- package/dist/src/mcp/index.js.map +1 -1
- package/dist/src/mcp/organisms.d.ts.map +1 -1
- package/dist/src/mcp/organisms.js +48 -8
- package/dist/src/mcp/organisms.js.map +1 -1
- package/dist/src/mcp/workspaces.d.ts.map +1 -1
- package/dist/src/mcp/workspaces.js +6 -4
- package/dist/src/mcp/workspaces.js.map +1 -1
- package/dist/src/models/offer-schemas.d.ts +8 -8
- package/dist/src/routes/ai.d.ts +8 -4
- package/dist/src/routes/ai.d.ts.map +1 -1
- package/dist/src/routes/ai.js +38 -14
- package/dist/src/routes/ai.js.map +1 -1
- package/dist/src/routes/app-grants.d.ts +2 -0
- package/dist/src/routes/app-grants.d.ts.map +1 -1
- package/dist/src/routes/app-grants.js +7 -1
- package/dist/src/routes/app-grants.js.map +1 -1
- package/dist/src/routes/app-templates.d.ts +16 -0
- package/dist/src/routes/app-templates.d.ts.map +1 -0
- package/dist/src/routes/app-templates.js +40 -0
- package/dist/src/routes/app-templates.js.map +1 -0
- package/dist/src/routes/apps.d.ts.map +1 -1
- package/dist/src/routes/apps.js +55 -5
- package/dist/src/routes/apps.js.map +1 -1
- package/dist/src/routes/auth.d.ts +3 -0
- package/dist/src/routes/auth.d.ts.map +1 -1
- package/dist/src/routes/auth.js +9 -4
- package/dist/src/routes/auth.js.map +1 -1
- package/dist/src/routes/bootstrap.d.ts.map +1 -1
- package/dist/src/routes/bootstrap.js +1 -0
- package/dist/src/routes/bootstrap.js.map +1 -1
- package/dist/src/routes/generator.d.ts.map +1 -1
- package/dist/src/routes/generator.js +3 -2
- package/dist/src/routes/generator.js.map +1 -1
- package/dist/src/routes/lib-ai.d.ts +0 -1
- package/dist/src/routes/lib-ai.d.ts.map +1 -1
- package/dist/src/routes/lib-ai.js +13 -6
- package/dist/src/routes/lib-ai.js.map +1 -1
- package/dist/src/routes/lib-header.d.ts +2 -0
- package/dist/src/routes/lib-header.d.ts.map +1 -1
- package/dist/src/routes/lib-header.js +7 -16
- package/dist/src/routes/lib-header.js.map +1 -1
- package/dist/src/routes/lib-markdown.d.ts +18 -0
- package/dist/src/routes/lib-markdown.d.ts.map +1 -0
- package/dist/src/routes/lib-markdown.js +236 -0
- package/dist/src/routes/lib-markdown.js.map +1 -0
- package/dist/src/routes/libs.d.ts +22 -0
- package/dist/src/routes/libs.d.ts.map +1 -1
- package/dist/src/routes/libs.js +133 -10
- package/dist/src/routes/libs.js.map +1 -1
- package/dist/src/routes/memory.d.ts.map +1 -1
- package/dist/src/routes/memory.js +21 -2
- package/dist/src/routes/memory.js.map +1 -1
- package/dist/src/routes/openrouter.d.ts +4 -0
- package/dist/src/routes/openrouter.d.ts.map +1 -1
- package/dist/src/routes/openrouter.js +17 -0
- package/dist/src/routes/openrouter.js.map +1 -1
- package/dist/src/routes/organisms.d.ts.map +1 -1
- package/dist/src/routes/organisms.js +107 -7
- package/dist/src/routes/organisms.js.map +1 -1
- package/dist/src/routes/portal.d.ts +9 -0
- package/dist/src/routes/portal.d.ts.map +1 -1
- package/dist/src/routes/portal.js +1 -1
- package/dist/src/routes/portal.js.map +1 -1
- package/dist/src/routes/public-stats.d.ts +3 -0
- package/dist/src/routes/public-stats.d.ts.map +1 -1
- package/dist/src/routes/public-stats.js +9 -2
- package/dist/src/routes/public-stats.js.map +1 -1
- package/dist/src/routes/secretary.d.ts +29 -0
- package/dist/src/routes/secretary.d.ts.map +1 -0
- package/dist/src/routes/secretary.js +220 -0
- package/dist/src/routes/secretary.js.map +1 -0
- package/dist/src/routes/specialists.d.ts.map +1 -1
- package/dist/src/routes/specialists.js +21 -0
- package/dist/src/routes/specialists.js.map +1 -1
- package/dist/src/routes/subdomains.js +1 -1
- package/dist/src/routes/subdomains.js.map +1 -1
- package/dist/src/routes/workflows.d.ts +9 -3
- package/dist/src/routes/workflows.d.ts.map +1 -1
- package/dist/src/routes/workflows.js +9 -3
- package/dist/src/routes/workflows.js.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
- package/dist/src/server-bootstrap/routes-loader.js +4 -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 +7 -0
- package/dist/src/server-bootstrap/service-init.js.map +1 -1
- package/dist/src/server-bootstrap/static-files.d.ts +4 -0
- package/dist/src/server-bootstrap/static-files.d.ts.map +1 -1
- package/dist/src/server-bootstrap/static-files.js +18 -0
- package/dist/src/server-bootstrap/static-files.js.map +1 -1
- package/dist/src/services/archive.d.ts +74 -0
- package/dist/src/services/archive.d.ts.map +1 -0
- package/dist/src/services/archive.js +132 -0
- package/dist/src/services/archive.js.map +1 -0
- package/dist/src/services/cortex-seeder.d.ts +4 -1
- package/dist/src/services/cortex-seeder.d.ts.map +1 -1
- package/dist/src/services/cortex-seeder.js +25 -6
- package/dist/src/services/cortex-seeder.js.map +1 -1
- package/dist/src/services/directory.d.ts.map +1 -1
- package/dist/src/services/directory.js +3 -2
- package/dist/src/services/directory.js.map +1 -1
- package/dist/src/services/generator-prompt-seeds.d.ts.map +1 -1
- package/dist/src/services/generator-prompt-seeds.js +5 -4
- package/dist/src/services/generator-prompt-seeds.js.map +1 -1
- package/dist/src/services/message-send.d.ts.map +1 -1
- package/dist/src/services/message-send.js +14 -0
- package/dist/src/services/message-send.js.map +1 -1
- package/dist/src/services/organism-search.d.ts +6 -2
- package/dist/src/services/organism-search.d.ts.map +1 -1
- package/dist/src/services/organism-search.js +2 -2
- package/dist/src/services/organism-search.js.map +1 -1
- package/dist/src/services/owner-memory.d.ts +1 -0
- package/dist/src/services/owner-memory.d.ts.map +1 -1
- package/dist/src/services/owner-memory.js.map +1 -1
- package/dist/src/services/owner-session.d.ts +1 -0
- package/dist/src/services/owner-session.d.ts.map +1 -1
- package/dist/src/services/owner-session.js +10 -2
- package/dist/src/services/owner-session.js.map +1 -1
- package/dist/src/services/package-seeder.d.ts +22 -0
- package/dist/src/services/package-seeder.d.ts.map +1 -0
- package/dist/src/services/package-seeder.js +30 -0
- package/dist/src/services/package-seeder.js.map +1 -0
- package/dist/src/services/presence.d.ts +3 -0
- package/dist/src/services/presence.d.ts.map +1 -1
- package/dist/src/services/presence.js +10 -0
- package/dist/src/services/presence.js.map +1 -1
- package/dist/src/services/prompt-defaults.d.ts.map +1 -1
- package/dist/src/services/prompt-defaults.js +8 -3
- package/dist/src/services/prompt-defaults.js.map +1 -1
- package/dist/src/services/scheduler.d.ts +41 -2
- package/dist/src/services/scheduler.d.ts.map +1 -1
- package/dist/src/services/scheduler.js +429 -26
- package/dist/src/services/scheduler.js.map +1 -1
- package/dist/src/services/secretary-authoring.d.ts +71 -0
- package/dist/src/services/secretary-authoring.d.ts.map +1 -0
- package/dist/src/services/secretary-authoring.js +336 -0
- package/dist/src/services/secretary-authoring.js.map +1 -0
- package/dist/src/services/secretary-clarify.d.ts +57 -0
- package/dist/src/services/secretary-clarify.d.ts.map +1 -0
- package/dist/src/services/secretary-clarify.js +104 -0
- package/dist/src/services/secretary-clarify.js.map +1 -0
- package/dist/src/services/secretary-feed.d.ts +11 -0
- package/dist/src/services/secretary-feed.d.ts.map +1 -0
- package/dist/src/services/secretary-feed.js +28 -0
- package/dist/src/services/secretary-feed.js.map +1 -0
- package/dist/src/services/secretary-tick.d.ts +243 -3
- package/dist/src/services/secretary-tick.d.ts.map +1 -1
- package/dist/src/services/secretary-tick.js +273 -3
- package/dist/src/services/secretary-tick.js.map +1 -1
- package/dist/src/services/specialist-runner.d.ts +36 -0
- package/dist/src/services/specialist-runner.d.ts.map +1 -0
- package/dist/src/services/specialist-runner.js +91 -0
- package/dist/src/services/specialist-runner.js.map +1 -0
- package/dist/src/services/specialist.d.ts +9 -0
- package/dist/src/services/specialist.d.ts.map +1 -1
- package/dist/src/services/specialist.js +42 -0
- package/dist/src/services/specialist.js.map +1 -1
- package/dist/src/services/structure-overview.d.ts +13 -0
- package/dist/src/services/structure-overview.d.ts.map +1 -1
- package/dist/src/services/structure-overview.js +34 -11
- package/dist/src/services/structure-overview.js.map +1 -1
- package/dist/src/services/system-agent-responder.d.ts +36 -0
- package/dist/src/services/system-agent-responder.d.ts.map +1 -0
- package/dist/src/services/system-agent-responder.js +107 -0
- package/dist/src/services/system-agent-responder.js.map +1 -0
- package/dist/src/services/system-agent.d.ts +34 -1
- package/dist/src/services/system-agent.d.ts.map +1 -1
- package/dist/src/services/system-agent.js +73 -0
- package/dist/src/services/system-agent.js.map +1 -1
- package/dist/src/storage/interface.d.ts +33 -0
- 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 +37 -0
- package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
- package/dist/src/storage/providers/mongodb/index.js +132 -12
- package/dist/src/storage/providers/mongodb/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/index.d.ts +23 -1
- package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/index.js +60 -8
- package/dist/src/storage/providers/sqlite/index.js.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/memory.d.ts +29 -1
- package/dist/src/storage/providers/sqlite/repos/memory.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/repos/memory.js +109 -10
- package/dist/src/storage/providers/sqlite/repos/memory.js.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.d.ts +3 -0
- package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
- package/dist/src/storage/providers/sqlite/schema.js +64 -9
- package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
- package/dist/src/storage/repositories/app.repository.d.ts +15 -0
- package/dist/src/storage/repositories/app.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/memory.repository.d.ts +41 -1
- package/dist/src/storage/repositories/memory.repository.d.ts.map +1 -1
- package/dist/src/storage/repositories/organism.repository.d.ts +2 -1
- package/dist/src/storage/repositories/organism.repository.d.ts.map +1 -1
- package/dist/static/app-catalog.html +469 -122
- package/package.json +2 -1
- package/prisma/schema.postgres.prisma +10 -0
- package/prisma/schema.prisma +12 -0
|
@@ -33,6 +33,17 @@
|
|
|
33
33
|
header (aimeat-home-link, label set from location.host) and a "Publish your own app" CTA in the
|
|
34
34
|
iframe app-viewer toolbar (.aimeat-cta). Pairs with the inline-app badge injected server-side
|
|
35
35
|
(apps.ts injectAimeatBadge) so visitors who open a shared app always have a way home + a publish hint.
|
|
36
|
+
v2.5.0 — 2026-06-26 — Add inline name + description editing in the Detail view's About section
|
|
37
|
+
(detailAboutEdit/Cancel/Save): edit in place where the details are shown, save PATCHes
|
|
38
|
+
/v1/apps/:filename { name, description } when published (and persists the local record) — the
|
|
39
|
+
app link (owner/filename) is unchanged, no re-publish and no new version.
|
|
40
|
+
v2.5.1 — 2026-06-26 — Fix: app names containing an apostrophe (e.g. "Fatalii's …") or other JS/HTML
|
|
41
|
+
delimiters broke the View/Fork buttons ("missing ) after argument list") because escapeHtml does
|
|
42
|
+
not escape quotes for an onclick JS-string context. Add jsArg() and use it for the name/filename
|
|
43
|
+
arguments interpolated into on* attributes.
|
|
44
|
+
v2.5.2 — 2026-06-26 — Add a pencil (✏️) next to the app name — on the detail-view title and on the
|
|
45
|
+
owner's own published + parked cards — that opens the inline name/description editor right where
|
|
46
|
+
the name is shown (editAppDetails opens the detail view with the About editor pre-opened).
|
|
36
47
|
-->
|
|
37
48
|
<html lang="en">
|
|
38
49
|
<head>
|
|
@@ -699,6 +710,7 @@
|
|
|
699
710
|
background: var(--surface);
|
|
700
711
|
color: var(--text);
|
|
701
712
|
}
|
|
713
|
+
.pb-template-select { margin-bottom: 8px; }
|
|
702
714
|
|
|
703
715
|
.modal-fields {
|
|
704
716
|
display: flex;
|
|
@@ -1467,6 +1479,21 @@
|
|
|
1467
1479
|
white-space: nowrap;
|
|
1468
1480
|
}
|
|
1469
1481
|
|
|
1482
|
+
/* Small inline pencil shown next to an editable app name (own cards + the detail
|
|
1483
|
+
title). Pressing it opens the name/description editor where the name shows. */
|
|
1484
|
+
.rename-pencil {
|
|
1485
|
+
background: none;
|
|
1486
|
+
border: none;
|
|
1487
|
+
cursor: pointer;
|
|
1488
|
+
font-size: .8rem;
|
|
1489
|
+
opacity: .5;
|
|
1490
|
+
padding: 0 2px;
|
|
1491
|
+
line-height: 1;
|
|
1492
|
+
vertical-align: middle;
|
|
1493
|
+
transition: opacity var(--transition);
|
|
1494
|
+
}
|
|
1495
|
+
.rename-pencil:hover { opacity: 1; }
|
|
1496
|
+
|
|
1470
1497
|
.published-card-meta {
|
|
1471
1498
|
font-size: .72rem;
|
|
1472
1499
|
color: var(--text-muted);
|
|
@@ -1725,8 +1752,8 @@
|
|
|
1725
1752
|
.prompt-builder-modal {
|
|
1726
1753
|
background: var(--surface);
|
|
1727
1754
|
border: 1px solid var(--border-subtle); border-radius: 16px;
|
|
1728
|
-
padding: 28px; max-width:
|
|
1729
|
-
display: flex; flex-direction: column; overflow:
|
|
1755
|
+
padding: 28px; max-width: 720px; width: 96vw; max-height: 90vh;
|
|
1756
|
+
display: flex; flex-direction: column; overflow-y: auto;
|
|
1730
1757
|
box-shadow: 0 8px 40px rgba(0,0,0,.12);
|
|
1731
1758
|
}
|
|
1732
1759
|
.prompt-builder-modal h2 { margin: 0 0 16px; font-size: 1.1rem; }
|
|
@@ -1757,13 +1784,26 @@
|
|
|
1757
1784
|
.pb-ext-chip.selected { background: rgba(232,86,74,.08); border-color: rgba(232,86,74,.35); }
|
|
1758
1785
|
.pb-ext-chip input { accent-color: #E8564A; pointer-events: none; }
|
|
1759
1786
|
.pb-preview {
|
|
1760
|
-
|
|
1787
|
+
min-height: 90px; max-height: 150px; overflow-y: auto; margin-bottom: 12px; padding: 12px;
|
|
1761
1788
|
background: var(--bg-subtle); border: 1px solid var(--border-subtle);
|
|
1762
1789
|
border-radius: 10px; font-family: 'Consolas','Monaco','Courier New',monospace;
|
|
1763
1790
|
font-size: .75rem; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
|
|
1764
1791
|
color: var(--text-muted);
|
|
1765
1792
|
}
|
|
1766
1793
|
.pb-preview-label { font-size: .75rem; text-transform: uppercase; letter-spacing: .1em; color: var(--text-muted); margin-bottom: 6px; }
|
|
1794
|
+
/* Guided-steps layout for "Generate App with AI" */
|
|
1795
|
+
.pb-intro { font-size: .82rem; color: var(--text-muted); line-height: 1.55; margin: 0 0 18px; }
|
|
1796
|
+
.pb-step { border-top: 1px solid var(--border-subtle); padding-top: 16px; margin-bottom: 16px; }
|
|
1797
|
+
.pb-step:first-of-type { border-top: none; padding-top: 0; }
|
|
1798
|
+
.pb-step-head { display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: .98rem; margin-bottom: 12px; }
|
|
1799
|
+
.pb-step-num {
|
|
1800
|
+
display: inline-flex; align-items: center; justify-content: center;
|
|
1801
|
+
width: 24px; height: 24px; border-radius: 50%; flex: 0 0 auto;
|
|
1802
|
+
background: #E8564A; color: #fff; font-size: .82rem; font-weight: 700;
|
|
1803
|
+
}
|
|
1804
|
+
.pb-step-hint { font-size: .8rem; color: var(--text-muted); line-height: 1.55; margin: 0 0 12px; }
|
|
1805
|
+
.pb-improving { font-size: .85rem; font-weight: 600; color: #E8564A; margin: 0 0 12px; }
|
|
1806
|
+
.pb-step-btn { width: 100%; justify-content: center; }
|
|
1767
1807
|
</style>
|
|
1768
1808
|
</head>
|
|
1769
1809
|
<body>
|
|
@@ -2200,22 +2240,41 @@
|
|
|
2200
2240
|
</div>
|
|
2201
2241
|
</div>
|
|
2202
2242
|
|
|
2203
|
-
<!--
|
|
2243
|
+
<!-- Generate App with AI — guided steps -->
|
|
2204
2244
|
<div id="prompt-builder-overlay">
|
|
2205
2245
|
<div class="prompt-builder-modal">
|
|
2206
|
-
<h2 id="pb-title" data-i18n="pb.title">✨ Generate App
|
|
2207
|
-
<
|
|
2208
|
-
|
|
2209
|
-
|
|
2246
|
+
<h2 id="pb-title" data-i18n="pb.title">✨ Generate App with AI</h2>
|
|
2247
|
+
<p class="pb-intro" data-i18n="pb.intro">No coding. Describe your idea, copy the prompt, and paste it into any AI chat (Claude, ChatGPT…). The AI asks a few questions, builds a ready-to-use app, and gives you one HTML file — then add it here to put it live.</p>
|
|
2248
|
+
|
|
2249
|
+
<div class="pb-step">
|
|
2250
|
+
<div class="pb-step-head"><span class="pb-step-num">1</span><span data-i18n="pb.step1">Describe your app</span></div>
|
|
2251
|
+
<!-- Mode is implicit (new vs improve) — radios kept hidden for state; an "Improving:" note shows instead. -->
|
|
2252
|
+
<div class="pb-mode-row" id="pb-mode-row" style="display:none">
|
|
2253
|
+
<label class="pb-mode-radio"><input type="radio" name="pb-mode" value="new" checked> <span data-i18n="pb.new">New App</span></label>
|
|
2254
|
+
<label class="pb-mode-radio"><input type="radio" name="pb-mode" value="improve"> <span data-i18n="pb.improve">Improve This App</span></label>
|
|
2255
|
+
</div>
|
|
2256
|
+
<div id="pb-improving" class="pb-improving" style="display:none"></div>
|
|
2257
|
+
<textarea class="pb-desc-area" id="pb-description" placeholder="Describe what the app should do..." data-i18n-ph="pb.descPh"></textarea>
|
|
2258
|
+
<label class="pb-ext-toggle" for="pb-template"><span>✨ <span data-i18n="pb.template">Start from a template (optional)</span></span></label>
|
|
2259
|
+
<select id="pb-template" class="modal-input pb-template-select"><option value="">(none — build from scratch)</option></select>
|
|
2260
|
+
</div>
|
|
2261
|
+
|
|
2262
|
+
<div class="pb-step">
|
|
2263
|
+
<div class="pb-step-head"><span class="pb-step-num">2</span><span data-i18n="pb.step2">Copy the prompt & paste it into your AI</span></div>
|
|
2264
|
+
<p class="pb-step-hint" data-i18n="pb.step2hint">Open Claude, ChatGPT or any AI chat, paste this in, and answer its questions. It will build your app and hand you a single HTML file.</p>
|
|
2265
|
+
<div class="pb-preview-label" data-i18n="pb.promptPreview">Prompt preview</div>
|
|
2266
|
+
<div class="pb-preview" id="pb-preview"></div>
|
|
2267
|
+
<button class="modal-btn primary pb-step-btn" id="pb-copy-btn" data-i18n="pb.copy">📋 Copy Prompt</button>
|
|
2268
|
+
</div>
|
|
2269
|
+
|
|
2270
|
+
<div class="pb-step">
|
|
2271
|
+
<div class="pb-step-head"><span class="pb-step-num">3</span><span data-i18n="pb.step3">Add & publish your app</span></div>
|
|
2272
|
+
<p class="pb-step-hint" data-i18n="pb.step3hint">Got the HTML file from the AI? Add it here — it goes live on your node and you get a link to share.</p>
|
|
2273
|
+
<button class="modal-btn pb-step-btn" id="pb-add-btn" data-i18n="pb.addBtn">➕ I have my app file — add it</button>
|
|
2210
2274
|
</div>
|
|
2211
|
-
|
|
2212
|
-
<label class="pb-ext-toggle"><input type="checkbox" id="pb-ext-checkbox"> <span data-i18n="pb.includeCortex">Include Cortex Extensions</span></label>
|
|
2213
|
-
<div class="pb-ext-list" id="pb-ext-list"></div>
|
|
2214
|
-
<div class="pb-preview-label" data-i18n="pb.livePreview">Live Preview</div>
|
|
2215
|
-
<div class="pb-preview" id="pb-preview"></div>
|
|
2275
|
+
|
|
2216
2276
|
<div class="modal-actions">
|
|
2217
|
-
<button class="modal-btn secondary" id="pb-cancel-btn" data-i18n="common.
|
|
2218
|
-
<button class="modal-btn primary" id="pb-copy-btn" data-i18n="pb.copy">Copy Prompt</button>
|
|
2277
|
+
<button class="modal-btn secondary" id="pb-cancel-btn" data-i18n="common.close">Close</button>
|
|
2219
2278
|
</div>
|
|
2220
2279
|
</div>
|
|
2221
2280
|
</div>
|
|
@@ -2322,6 +2381,9 @@
|
|
|
2322
2381
|
'publish.title': '🚀 Publish App',
|
|
2323
2382
|
'publish.filename': 'Filename',
|
|
2324
2383
|
'publish.filenameHint': 'Letters, numbers, dots, hyphens, underscores. Must end in .html',
|
|
2384
|
+
'publish.description': 'Description',
|
|
2385
|
+
'publish.descriptionPh': 'What does this app do? 1-2 sentences (your AI can write it)',
|
|
2386
|
+
'publish.descriptionHint': 'Shown in the catalogue and on the landing wall. Required for a new app.',
|
|
2325
2387
|
'publish.accessCode': 'Access Code (optional)',
|
|
2326
2388
|
'publish.accessCodePh': 'Leave empty for public',
|
|
2327
2389
|
'publish.accessCodeHint': '4-64 chars. If set, only people with the code can download.',
|
|
@@ -2334,13 +2396,25 @@
|
|
|
2334
2396
|
'versions.help': 'Every publish is kept. Restore re-publishes an older version as the new latest; Fork copies it into a new app. Older versions are never lost.',
|
|
2335
2397
|
'versions.current': 'current',
|
|
2336
2398
|
'versions.stored': 'versions stored',
|
|
2337
|
-
'pb.title': '✨ Generate App
|
|
2399
|
+
'pb.title': '✨ Generate App with AI',
|
|
2400
|
+
'pb.intro': 'No coding. Describe your idea, copy the prompt, and paste it into any AI chat (Claude, ChatGPT…). The AI asks a few questions, builds a ready-to-use app, and gives you one HTML file — then add it here to put it live.',
|
|
2401
|
+
'pb.step1': 'Describe your app',
|
|
2402
|
+
'pb.step2': 'Copy the prompt & paste it into your AI',
|
|
2403
|
+
'pb.step2hint': 'Open Claude, ChatGPT or any AI chat, paste this in, and answer its questions. It will build your app and hand you a single HTML file.',
|
|
2404
|
+
'pb.step3': 'Add & publish your app',
|
|
2405
|
+
'pb.step3hint': 'Got the code or HTML file from the AI? Paste it (or upload the file) here — it goes live on your node and you get a shareable link.',
|
|
2406
|
+
'pb.template': 'Start from a template (optional)',
|
|
2407
|
+
'pb.noneTemplate': '(none — build from scratch)',
|
|
2408
|
+
'pb.promptPreview': 'Prompt preview',
|
|
2409
|
+
'pb.addBtn': '➕ Add your app — paste it or upload the file',
|
|
2410
|
+
'pb.improving': 'Improving',
|
|
2411
|
+
'pb.copied': 'Copied!',
|
|
2338
2412
|
'pb.new': 'New App',
|
|
2339
2413
|
'pb.improve': 'Improve This App',
|
|
2340
2414
|
'pb.descPh': 'Describe what the app should do, or what changes to make...',
|
|
2341
2415
|
'pb.includeCortex': 'Include Cortex Extensions',
|
|
2342
2416
|
'pb.livePreview': 'Live Preview',
|
|
2343
|
-
'pb.copy': 'Copy Prompt',
|
|
2417
|
+
'pb.copy': '📋 Copy Prompt',
|
|
2344
2418
|
'card.view': '👁 View',
|
|
2345
2419
|
'card.remove': '🗑 Remove',
|
|
2346
2420
|
'card.versions': '🕑 Versions',
|
|
@@ -2349,6 +2423,15 @@
|
|
|
2349
2423
|
'card.subdomain': '🌐 Subdomain',
|
|
2350
2424
|
'card.park': '🅿 Park',
|
|
2351
2425
|
'card.unpark': '🚀 Publish',
|
|
2426
|
+
'detail.editDetails': '✏️ Edit',
|
|
2427
|
+
'detail.nameLabel': 'App name',
|
|
2428
|
+
'detail.descLabel': 'Description',
|
|
2429
|
+
'detail.renameHint': 'The app link stays the same — only the displayed name and description change.',
|
|
2430
|
+
'detail.nameRequired': 'Name cannot be empty.',
|
|
2431
|
+
'detail.descRequired': 'Description cannot be empty.',
|
|
2432
|
+
'detail.saveDetails': '💾 Save',
|
|
2433
|
+
'detail.cancelEdit': 'Cancel',
|
|
2434
|
+
'detail.detailsUpdated': 'App details updated.',
|
|
2352
2435
|
'card.parkHint': 'Hide from the public catalogue — moves to Parked Apps (still usable by you).',
|
|
2353
2436
|
'card.unparkHint': 'Publish again — moves back to Published Apps, visible to everyone.',
|
|
2354
2437
|
'card.parkedBadge': 'PARKED',
|
|
@@ -2559,6 +2642,9 @@
|
|
|
2559
2642
|
'publish.title': '🚀 Julkaise sovellus',
|
|
2560
2643
|
'publish.filename': 'Tiedostonimi',
|
|
2561
2644
|
'publish.filenameHint': 'Kirjaimia, numeroita, pisteitä, viivoja, alaviivoja. Päätteen oltava .html',
|
|
2645
|
+
'publish.description': 'Kuvaus',
|
|
2646
|
+
'publish.descriptionPh': 'Mitä tämä sovellus tekee? 1–2 lausetta (tekoälysi voi kirjoittaa sen)',
|
|
2647
|
+
'publish.descriptionHint': 'Näytetään katalogissa ja aloitusseinällä. Pakollinen uudelle sovellukselle.',
|
|
2562
2648
|
'publish.accessCode': 'Pääsykoodi (valinnainen)',
|
|
2563
2649
|
'publish.accessCodePh': 'Jätä tyhjäksi julkiseksi',
|
|
2564
2650
|
'publish.accessCodeHint': '4–64 merkkiä. Jos asetettu, vain koodin tietävät voivat ladata.',
|
|
@@ -2571,13 +2657,25 @@
|
|
|
2571
2657
|
'versions.help': 'Jokainen julkaisu säilytetään. Palauta julkaisee vanhan version uudelleen uusimmaksi; Haaroita kopioi sen uudeksi sovellukseksi. Vanhoja versioita ei koskaan menetetä.',
|
|
2572
2658
|
'versions.current': 'nykyinen',
|
|
2573
2659
|
'versions.stored': 'versiota tallennettu',
|
|
2574
|
-
'pb.title': '✨ Luo
|
|
2660
|
+
'pb.title': '✨ Luo sovellus tekoälyllä',
|
|
2661
|
+
'pb.intro': 'Ei koodausta. Kuvaile ideasi, kopioi prompti ja liitä se mihin tahansa tekoälykeskusteluun (Claude, ChatGPT…). Tekoäly kysyy muutaman kysymyksen, rakentaa valmiin sovelluksen ja antaa sinulle yhden HTML-tiedoston — lisää se sitten tästä julkaistaksesi.',
|
|
2662
|
+
'pb.step1': 'Kuvaile sovelluksesi',
|
|
2663
|
+
'pb.step2': 'Kopioi prompti & liitä se tekoälyysi',
|
|
2664
|
+
'pb.step2hint': 'Avaa Claude, ChatGPT tai mikä tahansa tekoälykeskustelu, liitä tämä ja vastaa kysymyksiin. Se rakentaa sovelluksesi ja antaa sinulle yhden HTML-tiedoston.',
|
|
2665
|
+
'pb.step3': 'Lisää & julkaise sovelluksesi',
|
|
2666
|
+
'pb.step3hint': 'Saitko koodin tai HTML-tiedoston tekoälyltä? Liitä se (tai lataa tiedosto) tästä — se tulee live-tilaan solmuusi ja saat jaettavan linkin.',
|
|
2667
|
+
'pb.template': 'Aloita mallipohjasta (valinnainen)',
|
|
2668
|
+
'pb.noneTemplate': '(ei mitään — rakenna tyhjästä)',
|
|
2669
|
+
'pb.promptPreview': 'Promptin esikatselu',
|
|
2670
|
+
'pb.addBtn': '➕ Lisää sovelluksesi — liitä se tai lataa tiedosto',
|
|
2671
|
+
'pb.improving': 'Parannetaan',
|
|
2672
|
+
'pb.copied': 'Kopioitu!',
|
|
2575
2673
|
'pb.new': 'Uusi sovellus',
|
|
2576
2674
|
'pb.improve': 'Paranna tätä sovellusta',
|
|
2577
2675
|
'pb.descPh': 'Kuvaile mitä sovelluksen tulisi tehdä, tai mitä muutoksia haluat...',
|
|
2578
2676
|
'pb.includeCortex': 'Sisällytä Cortex-laajennukset',
|
|
2579
2677
|
'pb.livePreview': 'Esikatselu',
|
|
2580
|
-
'pb.copy': 'Kopioi prompti',
|
|
2678
|
+
'pb.copy': '📋 Kopioi prompti',
|
|
2581
2679
|
'card.view': '👁 Näytä',
|
|
2582
2680
|
'card.remove': '🗑 Poista',
|
|
2583
2681
|
'card.versions': '🕑 Versiot',
|
|
@@ -2586,6 +2684,15 @@
|
|
|
2586
2684
|
'card.subdomain': '🌐 Alidomain',
|
|
2587
2685
|
'card.park': '🅿 Parkkiin',
|
|
2588
2686
|
'card.unpark': '🚀 Julkaise',
|
|
2687
|
+
'detail.editDetails': '✏️ Muokkaa',
|
|
2688
|
+
'detail.nameLabel': 'Sovelluksen nimi',
|
|
2689
|
+
'detail.descLabel': 'Kuvaus',
|
|
2690
|
+
'detail.renameHint': 'Sovelluksen linkki pysyy samana — vain näytettävä nimi ja kuvaus muuttuvat.',
|
|
2691
|
+
'detail.nameRequired': 'Nimi ei voi olla tyhjä.',
|
|
2692
|
+
'detail.descRequired': 'Kuvaus ei voi olla tyhjä.',
|
|
2693
|
+
'detail.saveDetails': '💾 Tallenna',
|
|
2694
|
+
'detail.cancelEdit': 'Peruuta',
|
|
2695
|
+
'detail.detailsUpdated': 'Sovelluksen tiedot päivitetty.',
|
|
2589
2696
|
'card.parkHint': 'Piilota julkisesta katalogista — siirtyy Parkissa-osioon (sinä voit yhä käyttää).',
|
|
2590
2697
|
'card.unparkHint': 'Julkaise uudelleen — siirtyy takaisin Julkaistuihin, näkyy kaikille.',
|
|
2591
2698
|
'card.parkedBadge': 'PARKISSA',
|
|
@@ -3213,7 +3320,7 @@
|
|
|
3213
3320
|
var app = {
|
|
3214
3321
|
id: generateId(),
|
|
3215
3322
|
name: name,
|
|
3216
|
-
description: '',
|
|
3323
|
+
description: parseAppMeta(sourceCode).description || '',
|
|
3217
3324
|
source: 'local',
|
|
3218
3325
|
url: null,
|
|
3219
3326
|
blob: encoded,
|
|
@@ -3237,11 +3344,50 @@
|
|
|
3237
3344
|
|
|
3238
3345
|
function showModal() {
|
|
3239
3346
|
document.getElementById('modal-overlay').hidden = false;
|
|
3240
|
-
document.getElementById('modal-title').textContent = '
|
|
3347
|
+
document.getElementById('modal-title').textContent = t('addModal.title');
|
|
3241
3348
|
// Default to URL tab
|
|
3242
3349
|
switchTab('url');
|
|
3243
3350
|
}
|
|
3244
3351
|
|
|
3352
|
+
// Step 0 for not-yet-signed-in users: adding/publishing an app needs an account, so
|
|
3353
|
+
// open the shared sign-in/register dialog (the SAME one the golden pill opens —
|
|
3354
|
+
// Google one-click or email+password, full registration) and continue once logged in.
|
|
3355
|
+
function requireSignInThen(next) {
|
|
3356
|
+
if (getCortexOwnerToken()) { next(); return; }
|
|
3357
|
+
var done = false;
|
|
3358
|
+
function onLogin() {
|
|
3359
|
+
if (done) return; done = true;
|
|
3360
|
+
try { window.AIMEAT.auth.off('login', onLogin); } catch (e) {}
|
|
3361
|
+
next();
|
|
3362
|
+
}
|
|
3363
|
+
try {
|
|
3364
|
+
window.AIMEAT.auth.on('login', onLogin);
|
|
3365
|
+
window.AIMEAT.auth.showLoginModal({});
|
|
3366
|
+
} catch (e) {
|
|
3367
|
+
// Auth lib not ready yet — fall back to the pill's own Sign In button.
|
|
3368
|
+
var b = document.querySelector('#headerAuth .aimeat-sign-btn');
|
|
3369
|
+
if (b) b.click(); else next();
|
|
3370
|
+
}
|
|
3371
|
+
}
|
|
3372
|
+
|
|
3373
|
+
// Read the app name + description the templates embed (AIMEAT App Manifest comment),
|
|
3374
|
+
// falling back to <title>, so the Add dialog can pre-fill them from pasted code.
|
|
3375
|
+
function parseAppMeta(html) {
|
|
3376
|
+
var meta = { name: '', description: '' };
|
|
3377
|
+
try {
|
|
3378
|
+
var m = (html || '').match(/AIMEAT App Manifest([\s\S]*?)-->/i);
|
|
3379
|
+
if (m) {
|
|
3380
|
+
var nm = m[1].match(/\bname:\s*(.+)/i); if (nm) meta.name = nm[1].trim();
|
|
3381
|
+
var dm = m[1].match(/\bdescription:\s*(.+)/i); if (dm) meta.description = dm[1].trim();
|
|
3382
|
+
}
|
|
3383
|
+
if (!meta.name) { var tt = (html || '').match(/<title>([^<]+)<\/title>/i); if (tt) meta.name = tt[1].trim(); }
|
|
3384
|
+
} catch (e) {}
|
|
3385
|
+
// Drop unfilled {{template}} placeholders.
|
|
3386
|
+
if (/\{\{.*\}\}/.test(meta.name)) meta.name = '';
|
|
3387
|
+
if (/\{\{.*\}\}/.test(meta.description)) meta.description = '';
|
|
3388
|
+
return meta;
|
|
3389
|
+
}
|
|
3390
|
+
|
|
3245
3391
|
function closeModal() {
|
|
3246
3392
|
document.getElementById('modal-overlay').hidden = true;
|
|
3247
3393
|
// Reset form
|
|
@@ -3417,6 +3563,23 @@
|
|
|
3417
3563
|
return div.innerHTML;
|
|
3418
3564
|
}
|
|
3419
3565
|
|
|
3566
|
+
// Escape a user-controlled string for use as a single-quoted JS argument INSIDE a
|
|
3567
|
+
// double-quoted HTML on* attribute (e.g. onclick="window._launcher.foo('<here>')").
|
|
3568
|
+
// escapeHtml only handles < > & — an apostrophe like in "Fatalii's" would otherwise
|
|
3569
|
+
// terminate the JS string and throw "missing ) after argument list", breaking the
|
|
3570
|
+
// button. JS-escape the backslash + single quote; HTML-escape the attribute/markup
|
|
3571
|
+
// delimiters (they decode back to plain chars before the JS engine sees them).
|
|
3572
|
+
function jsArg(str) {
|
|
3573
|
+
return String(str == null ? '' : str)
|
|
3574
|
+
.replace(/&/g, '&')
|
|
3575
|
+
.replace(/\\/g, '\\\\')
|
|
3576
|
+
.replace(/'/g, "\\'")
|
|
3577
|
+
.replace(/"/g, '"')
|
|
3578
|
+
.replace(/</g, '<')
|
|
3579
|
+
.replace(/>/g, '>')
|
|
3580
|
+
.replace(/[\r\n]+/g, ' ');
|
|
3581
|
+
}
|
|
3582
|
+
|
|
3420
3583
|
function sourceLabel(source) {
|
|
3421
3584
|
switch (source) {
|
|
3422
3585
|
case 'local': return '\u{1F4C4} local';
|
|
@@ -5318,8 +5481,9 @@
|
|
|
5318
5481
|
return;
|
|
5319
5482
|
}
|
|
5320
5483
|
|
|
5321
|
-
// Send the owner token when we have one: the server
|
|
5322
|
-
//
|
|
5484
|
+
// Send the owner token when we have one: the server decides visibility from who
|
|
5485
|
+
// is authenticated, so an authenticated owner gets back their OWN parked +
|
|
5486
|
+
// operator-hidden apps (badged) while everyone else never sees them.
|
|
5323
5487
|
var listHeaders = {};
|
|
5324
5488
|
var listTok = getCortexOwnerToken();
|
|
5325
5489
|
if (listTok) listHeaders['Authorization'] = 'Bearer ' + listTok;
|
|
@@ -5414,7 +5578,7 @@
|
|
|
5414
5578
|
(date ? '<span class="pcm-date">' + date + '</span>' : '') +
|
|
5415
5579
|
'</div>' +
|
|
5416
5580
|
'<div class="published-card-actions">' +
|
|
5417
|
-
'<button onclick="window._launcher.viewPublished(\'' + escapeHtml(viewUrl) + '?mode=inline\', \'' +
|
|
5581
|
+
'<button onclick="window._launcher.viewPublished(\'' + escapeHtml(viewUrl) + '?mode=inline\', \'' + jsArg(name) + '\')">' + t('card.view') + '</button>' +
|
|
5418
5582
|
'<button onclick="window._launcher.forkVersion(\'' + escapeHtml(sa.owner || '') + '\', \'' + escapeHtml(sa.filename || '') + '\', ' + (sa.version_number || 0) + ')" title="Copy into your own apps">' + t('card.fork') + '</button>' +
|
|
5419
5583
|
'</div>' +
|
|
5420
5584
|
(version ? '<div class="published-card-badgerow"><span class="pcb-version">' + escapeHtml(version) + '</span></div>' : '') +
|
|
@@ -5446,6 +5610,7 @@
|
|
|
5446
5610
|
html +=
|
|
5447
5611
|
'<div class="published-card parked">' +
|
|
5448
5612
|
'<div class="published-card-name">' + escapeHtml(name) +
|
|
5613
|
+
' <button class="rename-pencil" title="' + escapeHtml(t('detail.editDetails')) + '" onclick="window._launcher.editAppDetails(\'' + jsArg(sa.owner || '') + '\', \'' + jsArg(sa.filename || '') + '\', \'\', ' + (sa.version_number || 0) + ')">✏️</button>' +
|
|
5449
5614
|
(version ? ' <span style="opacity:.5;font-size:.85em">' + escapeHtml(version) + '</span>' : '') +
|
|
5450
5615
|
' <span class="published-source-badge parked">' + t('card.parkedBadge') + '</span>' +
|
|
5451
5616
|
'</div>' +
|
|
@@ -5456,7 +5621,7 @@
|
|
|
5456
5621
|
'<div class="published-card-actions">' +
|
|
5457
5622
|
'<button class="primary" onclick="window._launcher.toggleParkApp(\'' + escapeHtml(sa.filename || '') + '\', false)" title="' + escapeHtml(t('card.unparkHint')) + '">' + t('card.unpark') + '</button>' +
|
|
5458
5623
|
'<button onclick="window._launcher.openPublishedDetail(\'' + escapeHtml(sa.owner || '') + '\', \'' + escapeHtml(sa.filename || '') + '\', \'\', ' + (sa.version_number || 0) + ')">' + t('ctx.details') + '</button>' +
|
|
5459
|
-
'<button onclick="window._launcher.viewPublished(\'' + escapeHtml(viewUrl) + '?mode=inline\', \'' +
|
|
5624
|
+
'<button onclick="window._launcher.viewPublished(\'' + escapeHtml(viewUrl) + '?mode=inline\', \'' + jsArg(name) + '\')">' + t('card.view') + '</button>' +
|
|
5460
5625
|
'<button onclick="window._launcher.showVersionsModal(\'' + escapeHtml(sa.owner || '') + '\', \'' + escapeHtml(sa.filename || '') + '\')">' + t('card.versions') + '</button>' +
|
|
5461
5626
|
'<button onclick="window._launcher.deleteServerApp(\'' + escapeHtml(sa.filename || '') + '\')" class="danger">' + t('card.remove') + '</button>' +
|
|
5462
5627
|
'</div>' +
|
|
@@ -5558,7 +5723,9 @@
|
|
|
5558
5723
|
|
|
5559
5724
|
html +=
|
|
5560
5725
|
'<div class="published-card">' +
|
|
5561
|
-
'<div class="published-card-name">' + escapeHtml(app.name) +
|
|
5726
|
+
'<div class="published-card-name">' + escapeHtml(app.name) +
|
|
5727
|
+
((app.isOwn && app.filename) ? ' <button class="rename-pencil" title="' + escapeHtml(t('detail.editDetails')) + '" onclick="window._launcher.editAppDetails(\'' + jsArg(app.owner || '') + '\', \'' + jsArg(app.filename) + '\', \'' + jsArg(app.localId || '') + '\', ' + (app.versionNumber || 0) + ')">✏️</button>' : '') +
|
|
5728
|
+
'</div>' +
|
|
5562
5729
|
(app.operatorHidden ? '<div class="published-card-modnote"><span class="published-source-badge operator-hidden" title="' + escapeHtml(app.operatorHideReason || '') + '">' + t('card.operatorHidden') + '</span></div>' : '') +
|
|
5563
5730
|
(app.description ? '<div class="published-card-desc">' + escapeHtml(app.description) + '</div>' : '') +
|
|
5564
5731
|
'<div class="published-card-footer">' +
|
|
@@ -5569,7 +5736,7 @@
|
|
|
5569
5736
|
'</div>' +
|
|
5570
5737
|
'<div class="published-card-actions">' +
|
|
5571
5738
|
(app.isOwn && app.filename ? '<button class="primary" onclick="window._launcher.openPublishedDetail(\'' + escapeHtml(app.owner || '') + '\', \'' + escapeHtml(app.filename) + '\', \'' + escapeHtml(app.localId || '') + '\', ' + (app.versionNumber || 0) + ')">' + t('ctx.details') + '</button>' : '') +
|
|
5572
|
-
(app.viewUrl ? '<button onclick="window._launcher.viewPublished(\'' + escapeHtml(app.viewUrl) + '?mode=inline\', \'' +
|
|
5739
|
+
(app.viewUrl ? '<button onclick="window._launcher.viewPublished(\'' + escapeHtml(app.viewUrl) + '?mode=inline\', \'' + jsArg(app.name) + '\')">' + t('card.view') + '</button>' : '') +
|
|
5573
5740
|
(app.isOwn && app.filename ? '<button onclick="window._launcher.showVersionsModal(\'' + escapeHtml(app.owner || '') + '\', \'' + escapeHtml(app.filename) + '\')">' + t('card.versions') + '</button>' : '') +
|
|
5574
5741
|
parkBtnHtml +
|
|
5575
5742
|
subBtnHtml +
|
|
@@ -5682,6 +5849,8 @@
|
|
|
5682
5849
|
var detailDraftBlob = null; // base64 of the pending AI-generated draft (never overwrites app.blob until Keep)
|
|
5683
5850
|
var detailAiAvailable = false;
|
|
5684
5851
|
var detailVersionsHtml = null; // cached rendered versions list, survives re-renders
|
|
5852
|
+
var detailEditingAbout = false; // true while the About name/description inline editor is open
|
|
5853
|
+
var detailEditAboutOnOpen = false; // when set, open the About editor as soon as the detail view renders
|
|
5685
5854
|
|
|
5686
5855
|
function detailGetApp() {
|
|
5687
5856
|
for (var i = 0; i < allApps.length; i++) {
|
|
@@ -5718,6 +5887,7 @@
|
|
|
5718
5887
|
detailAppId = appId;
|
|
5719
5888
|
detailDraftBlob = null;
|
|
5720
5889
|
detailVersionsHtml = null;
|
|
5890
|
+
detailEditingAbout = false;
|
|
5721
5891
|
var app = detailGetApp();
|
|
5722
5892
|
if (!app) return;
|
|
5723
5893
|
document.getElementById('detail-view').hidden = false;
|
|
@@ -5728,6 +5898,18 @@
|
|
|
5728
5898
|
if (app.published && app.publishedFilename && owner) {
|
|
5729
5899
|
detailLoadVersions(owner, app.publishedFilename);
|
|
5730
5900
|
}
|
|
5901
|
+
// Opened via a pencil ("edit the name") → jump straight into the About editor.
|
|
5902
|
+
if (detailEditAboutOnOpen) {
|
|
5903
|
+
detailEditAboutOnOpen = false;
|
|
5904
|
+
detailAboutEdit();
|
|
5905
|
+
}
|
|
5906
|
+
}
|
|
5907
|
+
|
|
5908
|
+
// Open an app's detail view with the name/description editor already open. Used by
|
|
5909
|
+
// the pencil icons on the cards so editing starts right where the name is shown.
|
|
5910
|
+
function editAppDetails(owner, filename, localId, versionNumber) {
|
|
5911
|
+
detailEditAboutOnOpen = true;
|
|
5912
|
+
openPublishedDetail(owner, filename, localId, versionNumber);
|
|
5731
5913
|
}
|
|
5732
5914
|
|
|
5733
5915
|
function closeDetailView() {
|
|
@@ -5816,9 +5998,13 @@
|
|
|
5816
5998
|
var hasServer = !!config.aimeatUrl;
|
|
5817
5999
|
var icon = app.icon || '\u{1F4DD}';
|
|
5818
6000
|
var isUrlApp = (app.source === 'url' && !app.blob);
|
|
6001
|
+
// The name + description are editable in place (see the About section). A pencil
|
|
6002
|
+
// next to the title opens that editor right where the name is shown.
|
|
6003
|
+
var canEditAbout = !!app.id && !isUrlApp;
|
|
5819
6004
|
|
|
5820
6005
|
document.getElementById('detail-title').innerHTML =
|
|
5821
|
-
'<span style="font-size:1.3rem">' + escapeHtml(icon) + '</span> ' + escapeHtml(app.name || 'App')
|
|
6006
|
+
'<span style="font-size:1.3rem">' + escapeHtml(icon) + '</span> ' + escapeHtml(app.name || 'App') +
|
|
6007
|
+
((canEditAbout && !detailEditingAbout) ? ' <button class="rename-pencil" style="font-size:1rem" title="' + escapeHtml(t('detail.editDetails')) + '" onclick="window._launcher.detailAboutEdit()">✏️</button>' : '');
|
|
5822
6008
|
|
|
5823
6009
|
// ── STATUS ──
|
|
5824
6010
|
var localBytes = app.blob ? Math.round(app.blob.length * 0.75) : 0; // base64 → bytes approx
|
|
@@ -5856,12 +6042,32 @@
|
|
|
5856
6042
|
'</div>';
|
|
5857
6043
|
|
|
5858
6044
|
// ── ABOUT ──
|
|
6045
|
+
// The display name + description are editable in place here (inline editor) —
|
|
6046
|
+
// the app's URL is keyed off owner/filename, so a rename never moves the link.
|
|
6047
|
+
// Editing is offered for the owner's own apps (a local record always exists once
|
|
6048
|
+
// the detail view is open); Save PATCHes the server when the app is published.
|
|
5859
6049
|
var tags = (app.tags && app.tags.length) ? app.tags.join(', ') : '—';
|
|
5860
6050
|
var cortex = (app.usesCortex && app.usesCortex.length) ? app.usesCortex.join(', ') : '—';
|
|
5861
6051
|
var created = app.addedAt ? new Date(app.addedAt).toLocaleString() : '—';
|
|
5862
|
-
var
|
|
5863
|
-
'<
|
|
5864
|
-
'<
|
|
6052
|
+
var aboutHeader =
|
|
6053
|
+
'<h3 style="display:flex;align-items:center;justify-content:space-between;gap:8px">' +
|
|
6054
|
+
'<span>' + t('detail.about') + '</span>' +
|
|
6055
|
+
((canEditAbout && !detailEditingAbout) ? '<button class="dtl-btn" onclick="window._launcher.detailAboutEdit()">' + t('detail.editDetails') + '</button>' : '') +
|
|
6056
|
+
'</h3>';
|
|
6057
|
+
var aboutBody;
|
|
6058
|
+
if (detailEditingAbout) {
|
|
6059
|
+
aboutBody =
|
|
6060
|
+
'<label class="dtl-stat-label" for="detail-name-input">' + t('detail.nameLabel') + '</label>' +
|
|
6061
|
+
'<input id="detail-name-input" class="modal-input" maxlength="120" value="' + escapeHtml(app.name || '') + '" style="margin:4px 0 10px" />' +
|
|
6062
|
+
'<label class="dtl-stat-label" for="detail-desc-input">' + t('detail.descLabel') + '</label>' +
|
|
6063
|
+
'<textarea id="detail-desc-input" class="modal-input" rows="3" maxlength="2000" style="margin:4px 0 8px;resize:vertical">' + escapeHtml(app.description || '') + '</textarea>' +
|
|
6064
|
+
'<div class="dtl-sync none" style="margin:0 0 10px">' + t('detail.renameHint') + '</div>' +
|
|
6065
|
+
'<div class="dtl-btn-row">' +
|
|
6066
|
+
'<button class="dtl-btn primary" onclick="window._launcher.detailAboutSave()">' + t('detail.saveDetails') + '</button>' +
|
|
6067
|
+
'<button class="dtl-btn" onclick="window._launcher.detailAboutCancel()">' + t('detail.cancelEdit') + '</button>' +
|
|
6068
|
+
'</div>';
|
|
6069
|
+
} else {
|
|
6070
|
+
aboutBody =
|
|
5865
6071
|
(app.description ? '<p class="dtl-desc">' + escapeHtml(app.description) + '</p>' : '') +
|
|
5866
6072
|
'<div class="dtl-meta-grid">' +
|
|
5867
6073
|
metaItem(t('detail.category'), app.category || 'utility') +
|
|
@@ -5871,8 +6077,9 @@
|
|
|
5871
6077
|
metaItem(t('detail.size'), app.blob ? fmtSize(localBytes) : '—') +
|
|
5872
6078
|
metaItem(t('detail.created'), created) +
|
|
5873
6079
|
metaItem(t('detail.usesCortex'), cortex) +
|
|
5874
|
-
'</div>'
|
|
5875
|
-
|
|
6080
|
+
'</div>';
|
|
6081
|
+
}
|
|
6082
|
+
var aboutHtml = '<div class="dtl-section">' + aboutHeader + aboutBody + '</div>';
|
|
5876
6083
|
|
|
5877
6084
|
// ── EDIT WITH AI ──
|
|
5878
6085
|
var aiHtml =
|
|
@@ -5933,6 +6140,74 @@
|
|
|
5933
6140
|
statusHtml + aboutHtml + aiHtml + versionsHtml + actionsHtml;
|
|
5934
6141
|
}
|
|
5935
6142
|
|
|
6143
|
+
// ── About: inline name + description editing ──────
|
|
6144
|
+
// Edit the display name/description right where they are shown. The URL is keyed
|
|
6145
|
+
// off owner/filename, so this never changes the app link. Save updates the local
|
|
6146
|
+
// record and, when the app is published, PATCHes /v1/apps/:filename in place.
|
|
6147
|
+
function detailAboutEdit() {
|
|
6148
|
+
if (!detailGetApp()) return;
|
|
6149
|
+
detailEditingAbout = true;
|
|
6150
|
+
renderDetailView();
|
|
6151
|
+
var nameEl = document.getElementById('detail-name-input');
|
|
6152
|
+
if (nameEl) { nameEl.focus(); nameEl.select(); }
|
|
6153
|
+
}
|
|
6154
|
+
|
|
6155
|
+
function detailAboutCancel() {
|
|
6156
|
+
detailEditingAbout = false;
|
|
6157
|
+
renderDetailView();
|
|
6158
|
+
}
|
|
6159
|
+
|
|
6160
|
+
function detailAboutSave() {
|
|
6161
|
+
var app = detailGetApp();
|
|
6162
|
+
if (!app) return;
|
|
6163
|
+
var nameEl = document.getElementById('detail-name-input');
|
|
6164
|
+
var descEl = document.getElementById('detail-desc-input');
|
|
6165
|
+
var newName = nameEl ? nameEl.value.trim() : '';
|
|
6166
|
+
var newDesc = descEl ? descEl.value.trim() : '';
|
|
6167
|
+
if (!newName) { alert(t('detail.nameRequired') || 'Name cannot be empty.'); if (nameEl) nameEl.focus(); return; }
|
|
6168
|
+
if (app.published && !newDesc) { alert(t('detail.descRequired') || 'Description cannot be empty.'); if (descEl) descEl.focus(); return; }
|
|
6169
|
+
|
|
6170
|
+
function finishLocal() {
|
|
6171
|
+
app.name = newName;
|
|
6172
|
+
app.description = newDesc;
|
|
6173
|
+
saveApp(app).then(function() {
|
|
6174
|
+
detailEditingAbout = false;
|
|
6175
|
+
renderApps();
|
|
6176
|
+
loadPublishedApps();
|
|
6177
|
+
renderDetailView();
|
|
6178
|
+
});
|
|
6179
|
+
}
|
|
6180
|
+
|
|
6181
|
+
// Not published on the server → it's a local-only record; just persist locally.
|
|
6182
|
+
if (!app.published || !app.publishedFilename) { finishLocal(); return; }
|
|
6183
|
+
|
|
6184
|
+
var token = getCortexOwnerToken();
|
|
6185
|
+
if (!token) { alert(t('common.loginRequired') || 'You must be logged in. Sign in first.'); return; }
|
|
6186
|
+
var config = loadConfig();
|
|
6187
|
+
var aimeatUrl = (config.aimeatUrl || '').replace(/\/+$/, '');
|
|
6188
|
+
var owner = detailServerOwner(app);
|
|
6189
|
+
var filename = app.publishedFilename;
|
|
6190
|
+
fetch(aimeatUrl + '/v1/apps/' + encodeURIComponent(filename), {
|
|
6191
|
+
method: 'PATCH',
|
|
6192
|
+
headers: { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' },
|
|
6193
|
+
body: JSON.stringify({ name: newName, description: newDesc })
|
|
6194
|
+
})
|
|
6195
|
+
.then(function(resp) { return resp.json().then(function(j) { return { ok: resp.ok, j: j }; }); })
|
|
6196
|
+
.then(function(res) {
|
|
6197
|
+
if (res.ok && res.j && res.j.ok !== false) {
|
|
6198
|
+
// Keep the cached server manifest in sync so a re-render shows the new name.
|
|
6199
|
+
var key = owner + '\n' + filename;
|
|
6200
|
+
serverAppManifests[key] = serverAppManifests[key] || {};
|
|
6201
|
+
serverAppManifests[key].name = newName;
|
|
6202
|
+
serverAppManifests[key].description = newDesc;
|
|
6203
|
+
finishLocal();
|
|
6204
|
+
} else {
|
|
6205
|
+
alert('Failed: ' + ((res.j && res.j.error && res.j.error.message) || 'Unknown error'));
|
|
6206
|
+
}
|
|
6207
|
+
})
|
|
6208
|
+
.catch(function(err) { alert('Error: ' + (err.message || err)); });
|
|
6209
|
+
}
|
|
6210
|
+
|
|
5936
6211
|
// Manual override: upload a custom image as this published app's thumbnail. (Bulk auto-capture is
|
|
5937
6212
|
// the server-side `aimeat screenshot-worker`; the browser can't grab the cross-origin sandboxed app
|
|
5938
6213
|
// itself, so this is a file picker, not an in-page capture.)
|
|
@@ -6066,7 +6341,7 @@
|
|
|
6066
6341
|
'<div class="version-meta"><span class="version-num">v' + v.version_number + (isLatest ? ' <span class="version-current">' + t('versions.current') + '</span>' : '') + '</span> ' +
|
|
6067
6342
|
'<span class="version-sub" style="color:var(--text-muted);font-size:.8rem">' + (kb ? kb : '') + (when ? ' · ' + when : '') + '</span></div>' +
|
|
6068
6343
|
'<div class="dtl-btn-row">' +
|
|
6069
|
-
'<button class="dtl-btn" onclick="window._launcher.viewPublished(\'' + escapeHtml(viewU) + '\', \'' +
|
|
6344
|
+
'<button class="dtl-btn" onclick="window._launcher.viewPublished(\'' + escapeHtml(viewU) + '\', \'' + jsArg(filename) + '\')">' + t('card.view') + '</button>' +
|
|
6070
6345
|
(isLatest ? '' : '<button class="dtl-btn" onclick="window._launcher.restoreVersion(' + ownerArg + ', ' + fileArg + ', ' + v.version_number + ')">' + t('card.restore') + '</button>') +
|
|
6071
6346
|
'<button class="dtl-btn" onclick="window._launcher.forkVersion(' + ownerArg + ', ' + fileArg + ', ' + v.version_number + ')">' + t('card.fork') + '</button>' +
|
|
6072
6347
|
'</div>' +
|
|
@@ -6144,8 +6419,7 @@
|
|
|
6144
6419
|
body: JSON.stringify({
|
|
6145
6420
|
prompt: userPrompt,
|
|
6146
6421
|
systemPrompt: systemPrompt,
|
|
6147
|
-
app_id: 'app-catalog'
|
|
6148
|
-
max_tokens: 16000
|
|
6422
|
+
app_id: 'app-catalog'
|
|
6149
6423
|
})
|
|
6150
6424
|
})
|
|
6151
6425
|
.then(function(resp) { return resp.json(); })
|
|
@@ -6352,7 +6626,7 @@
|
|
|
6352
6626
|
'<span class="version-sub">' + escapeHtml(v.version || '') + (kb ? ' · ' + kb : '') + (when ? ' · ' + when : '') + '</span>' +
|
|
6353
6627
|
'</div>' +
|
|
6354
6628
|
'<div class="version-actions">' +
|
|
6355
|
-
'<button onclick="window._launcher.viewPublished(\'' + escapeHtml(viewU) + '\', \'' +
|
|
6629
|
+
'<button onclick="window._launcher.viewPublished(\'' + escapeHtml(viewU) + '\', \'' + jsArg(filename) + '\')">' + t('card.view') + '</button>' +
|
|
6356
6630
|
(isLatest ? '' : '<button onclick="window._launcher.restoreVersion(' + ownerArg + ', ' + fileArg + ', ' + v.version_number + ')" title="Re-publish this version as the new latest">' + t('card.restore') + '</button>') +
|
|
6357
6631
|
'<button onclick="window._launcher.forkVersion(' + ownerArg + ', ' + fileArg + ', ' + v.version_number + ')" title="Copy this version into a new app">' + t('card.fork') + '</button>' +
|
|
6358
6632
|
'</div>' +
|
|
@@ -6807,26 +7081,32 @@
|
|
|
6807
7081
|
pbSourceApp = app || null;
|
|
6808
7082
|
var overlay = document.getElementById('prompt-builder-overlay');
|
|
6809
7083
|
var desc = document.getElementById('pb-description');
|
|
6810
|
-
var extCheckbox = document.getElementById('pb-ext-checkbox');
|
|
6811
|
-
var extList = document.getElementById('pb-ext-list');
|
|
6812
7084
|
|
|
6813
7085
|
// Reset state
|
|
6814
7086
|
desc.value = '';
|
|
6815
|
-
|
|
6816
|
-
|
|
7087
|
+
window.pbTemplate = null;
|
|
7088
|
+
var tplSel0 = document.getElementById('pb-template');
|
|
7089
|
+
if (tplSel0) tplSel0.value = '';
|
|
6817
7090
|
|
|
6818
|
-
//
|
|
7091
|
+
// Mode is implicit: "improve" when opened from an app, else "new". The radios
|
|
7092
|
+
// stay in the DOM (hidden) so buildPromptFromBuilder reads the right mode; instead
|
|
7093
|
+
// of a confusing toggle we show a plain "Improving: <name>" note when relevant.
|
|
6819
7094
|
var radios = document.querySelectorAll('input[name="pb-mode"]');
|
|
7095
|
+
var improving = document.getElementById('pb-improving');
|
|
6820
7096
|
if (pbSourceApp) {
|
|
6821
|
-
radios[1].checked = true;
|
|
6822
|
-
|
|
7097
|
+
radios[1].checked = true;
|
|
7098
|
+
if (improving) {
|
|
7099
|
+
improving.textContent = '✏️ ' + (t('pb.improving') || 'Improving') + ': ' + (pbSourceApp.name || pbSourceApp.filename || 'this app');
|
|
7100
|
+
improving.style.display = '';
|
|
7101
|
+
}
|
|
6823
7102
|
} else {
|
|
6824
|
-
radios[0].checked = true;
|
|
6825
|
-
|
|
7103
|
+
radios[0].checked = true;
|
|
7104
|
+
if (improving) improving.style.display = 'none';
|
|
6826
7105
|
}
|
|
7106
|
+
desc.placeholder = t('pb.descPh');
|
|
6827
7107
|
|
|
6828
|
-
// Load
|
|
6829
|
-
|
|
7108
|
+
// Load authoring templates (booster-kit) into the "Start from a template" picker
|
|
7109
|
+
loadPbTemplates();
|
|
6830
7110
|
|
|
6831
7111
|
overlay.style.display = 'flex';
|
|
6832
7112
|
desc.focus();
|
|
@@ -6838,6 +7118,43 @@
|
|
|
6838
7118
|
pbSourceApp = null;
|
|
6839
7119
|
}
|
|
6840
7120
|
|
|
7121
|
+
// Booster-kit: populate the "Start from a template" picker from /v1/app-templates and, on
|
|
7122
|
+
// selection, fetch that template's content into window.pbTemplate (injected by buildPromptFromBuilder).
|
|
7123
|
+
function loadPbTemplates() {
|
|
7124
|
+
var sel = document.getElementById('pb-template');
|
|
7125
|
+
if (!sel) return;
|
|
7126
|
+
var config = loadConfig();
|
|
7127
|
+
if (!config.aimeatUrl) return;
|
|
7128
|
+
var base = config.aimeatUrl.replace(/\/+$/, '');
|
|
7129
|
+
fetch(base + '/v1/app-templates?lang=' + encodeURIComponent(currentLang))
|
|
7130
|
+
.then(function (r) { return r.json(); })
|
|
7131
|
+
.then(function (d) {
|
|
7132
|
+
// Starting points only: full use-case scaffolds first, then blank app-shells (not components).
|
|
7133
|
+
var list = ((d.data && d.data.templates) || []).filter(function (t) { return t.kind !== 'component'; });
|
|
7134
|
+
list.sort(function (a, b) { return (a.kind === 'use-case' ? 0 : 1) - (b.kind === 'use-case' ? 0 : 1); });
|
|
7135
|
+
sel.innerHTML = '<option value="">' + t('pb.noneTemplate') + '</option>';
|
|
7136
|
+
list.forEach(function (t) {
|
|
7137
|
+
var o = document.createElement('option');
|
|
7138
|
+
o.value = t.id;
|
|
7139
|
+
var prefix = t.kind === 'use-case' ? '★ ' : '';
|
|
7140
|
+
// Use-case titles are short ("Marketplace") so append a snippet of their description;
|
|
7141
|
+
// app-shell titles are now self-describing ("Standard app — login + saves your data"),
|
|
7142
|
+
// so don't tack on a second em-dash clause.
|
|
7143
|
+
o.textContent = prefix + t.title + (t.kind === 'use-case' && t.description ? ' — ' + t.description.slice(0, 70) : '');
|
|
7144
|
+
sel.appendChild(o);
|
|
7145
|
+
});
|
|
7146
|
+
sel.onchange = function () {
|
|
7147
|
+
var id = sel.value;
|
|
7148
|
+
if (!id) { window.pbTemplate = null; updatePbPreview(); return; }
|
|
7149
|
+
fetch(base + '/v1/app-templates/' + encodeURIComponent(id))
|
|
7150
|
+
.then(function (r) { return r.json(); })
|
|
7151
|
+
.then(function (d2) { window.pbTemplate = (d2.data && d2.data.template) || null; updatePbPreview(); })
|
|
7152
|
+
.catch(function () { window.pbTemplate = null; });
|
|
7153
|
+
};
|
|
7154
|
+
})
|
|
7155
|
+
.catch(function () { /* templates are optional */ });
|
|
7156
|
+
}
|
|
7157
|
+
|
|
6841
7158
|
function loadPbExtensions() {
|
|
6842
7159
|
var list = document.getElementById('pb-ext-list');
|
|
6843
7160
|
var config = loadConfig();
|
|
@@ -6963,10 +7280,15 @@
|
|
|
6963
7280
|
var nodeUrl = config.aimeatUrl ? config.aimeatUrl.replace(/\/+$/, '') : 'https://your-aimeat-node.example';
|
|
6964
7281
|
var mode = document.querySelector('input[name="pb-mode"]:checked').value;
|
|
6965
7282
|
var description = document.getElementById('pb-description').value.trim();
|
|
6966
|
-
var selectedExts = document.getElementById('pb-ext-checkbox').checked ? getSelectedExtensions() : [];
|
|
6967
7283
|
|
|
6968
7284
|
var prompt = '';
|
|
6969
7285
|
|
|
7286
|
+
// Converse + build the UI in the language the user is browsing in (the build
|
|
7287
|
+
// instructions below stay in English, but the conversation + app text should match).
|
|
7288
|
+
var PB_LANGS = { en: 'English', fi: 'Finnish (Suomi)' };
|
|
7289
|
+
var pbLang = PB_LANGS[currentLang] || 'English';
|
|
7290
|
+
prompt += 'Language: talk to me and write ALL user-facing text (UI labels, buttons, messages) in ' + pbLang + '. These build instructions are in English, but converse with me and build the app interface in ' + pbLang + '.\n\n';
|
|
7291
|
+
|
|
6970
7292
|
// Header
|
|
6971
7293
|
var isImprove = (mode === 'improve' && pbSourceApp);
|
|
6972
7294
|
if (isImprove) {
|
|
@@ -7008,59 +7330,40 @@
|
|
|
7008
7330
|
prompt += 'Media & misc:\n';
|
|
7009
7331
|
prompt += '- aimeat-audio.js — audio engine: instruments, synth, soundboard\n';
|
|
7010
7332
|
prompt += '- aimeat-speech.js — text-to-speech / speech helpers\n';
|
|
7333
|
+
prompt += '- aimeat-markdown.js — render safe markdown to HTML (`AIMEAT.md.render`)\n';
|
|
7011
7334
|
prompt += '- aimeat-header.js — drop-in canonical site header (nav + theme)\n';
|
|
7012
7335
|
prompt += '- aimeat-tunnel.js — personal-node tunnel client (advanced)\n\n';
|
|
7013
7336
|
|
|
7014
|
-
//
|
|
7015
|
-
|
|
7016
|
-
|
|
7017
|
-
|
|
7018
|
-
|
|
7019
|
-
|
|
7020
|
-
|
|
7021
|
-
|
|
7022
|
-
|
|
7023
|
-
|
|
7024
|
-
|
|
7025
|
-
if (c.type === 'lib' && c.filename) {
|
|
7026
|
-
prompt += 'Load the library:\n```html\n';
|
|
7027
|
-
prompt += '<script src="' + nodeUrl + '/v1/cortex/' + encodeURIComponent(ext.name) + '/libs/' + encodeURIComponent(c.filename) + '"></' + 'script>\n';
|
|
7028
|
-
prompt += '```\n';
|
|
7029
|
-
if (c.api_surface) {
|
|
7030
|
-
prompt += 'API Surface:\n```\n' + c.api_surface + '\n```\n';
|
|
7031
|
-
}
|
|
7032
|
-
} else if (c.type === 'schema') {
|
|
7033
|
-
prompt += 'Data schema: `' + (c.key_pattern || '') + '` — data is validated automatically.\n';
|
|
7034
|
-
} else if (c.type === 'prompt') {
|
|
7035
|
-
prompt += 'Prompt template: ' + (c.name || '') + '\n';
|
|
7036
|
-
}
|
|
7037
|
-
});
|
|
7038
|
-
} else {
|
|
7039
|
-
// Fallback: only list-level type info
|
|
7040
|
-
var types = ext.component_types || [];
|
|
7041
|
-
if (types.indexOf('lib') !== -1) {
|
|
7042
|
-
prompt += 'Includes a JS library (load via `<script src="' + nodeUrl + '/v1/cortex/' + encodeURIComponent(ext.name) + '/libs/...">`).\n';
|
|
7043
|
-
}
|
|
7044
|
-
if (types.indexOf('schema') !== -1) {
|
|
7045
|
-
prompt += 'Provides validated data schemas.\n';
|
|
7046
|
-
}
|
|
7047
|
-
if (types.indexOf('prompt') !== -1) {
|
|
7048
|
-
prompt += 'Includes prompt templates.\n';
|
|
7049
|
-
}
|
|
7050
|
-
}
|
|
7051
|
-
prompt += '\n';
|
|
7052
|
-
});
|
|
7053
|
-
}
|
|
7337
|
+
// Ready-made UI building blocks (node-bundled cortex) — ALWAYS offered so the AI can
|
|
7338
|
+
// reach for them instead of hand-rolling tables/forms/charts. Load only what's used.
|
|
7339
|
+
prompt += 'Ready-made UI (node-bundled — load from ' + nodeUrl + '/v1/cortex/<name>/libs/<name>.js, use only what you need):\n';
|
|
7340
|
+
prompt += '- aimeat-ui-viewers — sortable/filterable DataTable + viewers (`AIMEAT.ui.viewers`)\n';
|
|
7341
|
+
prompt += '- aimeat-ui-forms — form builder with validation (`AIMEAT.ui.forms`)\n';
|
|
7342
|
+
prompt += '- aimeat-ui-layout — responsive layout helpers, master/detail (`AIMEAT.ui.layout`)\n';
|
|
7343
|
+
prompt += '- aimeat-ui-nav — navbars, tabs, menus (`AIMEAT.ui.nav`)\n';
|
|
7344
|
+
prompt += '- aimeat-ui-dialogs — modals, toasts, confirms (`AIMEAT.ui.dialogs`)\n';
|
|
7345
|
+
prompt += '- aimeat-charts — charts / graphs (`AIMEAT.charts`)\n';
|
|
7346
|
+
prompt += '- aimeat-canvas — drawing / freeform canvas (`AIMEAT.canvas`)\n';
|
|
7347
|
+
prompt += 'Example: <script src="' + nodeUrl + '/v1/cortex/aimeat-ui-viewers/libs/aimeat-ui-viewers.js"></' + 'script>\n\n';
|
|
7054
7348
|
|
|
7055
7349
|
// Auth pattern
|
|
7056
7350
|
prompt += '### Auth Pattern\n';
|
|
7351
|
+
prompt += 'Handle BOTH login paths: a fresh sign-in click (the onLogin callback) AND a page that loads already signed in (restore the session yourself). `onLogin` fires ONLY on a fresh sign-in — it does NOT fire on reload when a session already exists, so a page that relies on onLogin alone shows nothing to an already-logged-in returning user.\n';
|
|
7057
7352
|
prompt += '```html\n';
|
|
7058
7353
|
prompt += '<script src="' + nodeUrl + '/v1/libs/aimeat-auth.js"></' + 'script>\n';
|
|
7059
7354
|
prompt += '<script>\n';
|
|
7355
|
+
prompt += 'function showApp(session) { /* session.owner, session.jwt, session.fetch() */ }\n';
|
|
7356
|
+
prompt += 'function hideApp() { /* hide content, show a "Sign in" message */ }\n';
|
|
7357
|
+
prompt += '\n';
|
|
7358
|
+
prompt += '// Path 1 — fresh sign-in / sign-out via the login button:\n';
|
|
7060
7359
|
prompt += 'AIMEAT.auth.mountLoginButton("#login", {\n';
|
|
7061
|
-
prompt += ' onLogin:
|
|
7062
|
-
prompt += ' onLogout:
|
|
7360
|
+
prompt += ' onLogin: showApp, // fires ONLY on a fresh sign-in click, NOT on reload\n';
|
|
7361
|
+
prompt += ' onLogout: hideApp\n';
|
|
7063
7362
|
prompt += '});\n';
|
|
7363
|
+
prompt += '\n';
|
|
7364
|
+
prompt += '// Path 2 — already signed in when the page loads. Restore the stored session\n';
|
|
7365
|
+
prompt += '// explicitly; login() returns the session (or null if not signed in).\n';
|
|
7366
|
+
prompt += 'AIMEAT.auth.login().then(function (session) { if (session) showApp(session); });\n';
|
|
7064
7367
|
prompt += '</' + 'script>\n';
|
|
7065
7368
|
prompt += '```\n\n';
|
|
7066
7369
|
|
|
@@ -7110,11 +7413,15 @@
|
|
|
7110
7413
|
|
|
7111
7414
|
// Design guidelines
|
|
7112
7415
|
prompt += '### Design Guidelines\n';
|
|
7113
|
-
prompt += 'Use CSS variables so the app themes cleanly.
|
|
7416
|
+
prompt += 'Use CSS variables so the app themes cleanly, and RESPECT the user\'s AIMEAT theme: the light/dark choice they made in the AIMEAT pill is saved in localStorage "aimeat-theme" ("light"|"dark"). Define light as the default and dark under [data-theme="dark"], then set that attribute from the saved choice on load (fall back to the OS preference, and live-update if it changes):\n';
|
|
7114
7417
|
prompt += '```css\n';
|
|
7115
7418
|
prompt += ':root { --bg:#fafaf8; --card:#fff; --text:#1a1a2e; --accent:#e8564a; --border:#e5e7eb; --radius:12px; }\n';
|
|
7116
|
-
prompt += '
|
|
7117
|
-
prompt += '
|
|
7419
|
+
prompt += ':root[data-theme="dark"] { --bg:#14141c; --card:#1e1e2a; --text:#ececf4; --border:#2e2e40; }\n';
|
|
7420
|
+
prompt += '```\n';
|
|
7421
|
+
prompt += '```js\n';
|
|
7422
|
+
prompt += '(function(){ function apply(t){ document.documentElement.setAttribute("data-theme", t==="dark"?"dark":"light"); }\n';
|
|
7423
|
+
prompt += ' apply(localStorage.getItem("aimeat-theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"));\n';
|
|
7424
|
+
prompt += ' addEventListener("storage", function(e){ if(e.key==="aimeat-theme" && e.newValue) apply(e.newValue); }); })();\n';
|
|
7118
7425
|
prompt += '```\n';
|
|
7119
7426
|
prompt += 'Always include <meta name="viewport" content="width=device-width, initial-scale=1.0">. Mobile-first, single self-contained HTML file with embedded CSS + JS.\n\n';
|
|
7120
7427
|
|
|
@@ -7125,15 +7432,31 @@
|
|
|
7125
7432
|
prompt += '- Keep it as a single self-contained HTML file\n';
|
|
7126
7433
|
prompt += '- Load only the libraries you actually use; load aimeat-auth before libs that need a session\n';
|
|
7127
7434
|
prompt += '- Gate AI features on AIMEAT.ai.isAvailable() and handle the logged-out / no-key case\n';
|
|
7128
|
-
prompt += '- Theme with CSS variables
|
|
7435
|
+
prompt += '- Theme with CSS variables; respect the user\'s AIMEAT light/dark choice (localStorage "aimeat-theme") with an OS-preference fallback\n';
|
|
7129
7436
|
prompt += '- Include error handling and loading states for API calls\n\n';
|
|
7130
7437
|
|
|
7438
|
+
// After-build: have the AI walk me through publishing — closes the loop for newcomers.
|
|
7439
|
+
if (!isImprove) {
|
|
7440
|
+
prompt += '## When the app is ready — tell me how to publish it\n';
|
|
7441
|
+
prompt += 'After you hand me the finished single HTML file, END your reply by telling me (in my language) to do exactly this:\n';
|
|
7442
|
+
prompt += '1. Open ' + nodeUrl + '/app-catalog.html\n';
|
|
7443
|
+
prompt += '2. Click "+ Add" → open the "Paste" tab → paste the HTML (or drop it as a file). The app name + description fill in automatically.\n';
|
|
7444
|
+
prompt += '3. Click Publish.\n';
|
|
7445
|
+
prompt += 'I will be asked to sign in first — it is fast: one click with Google, or a quick email + password, and a brand-new account is created right there in seconds.\n';
|
|
7446
|
+
prompt += 'What I get: once published, the app is LIVE on my own AIMEAT node and PUBLIC — anyone can find it in the community catalogue and use it, and I get a link to share. From my catalogue I can launch it, publish updates (older versions are always kept), park it (hide it from the public), or delete it. It keeps working with my AIMEAT login, saved data, files, AI and realtime features.\n\n';
|
|
7447
|
+
}
|
|
7448
|
+
|
|
7131
7449
|
// Source code for improve mode
|
|
7132
7450
|
if (mode === 'improve' && pbSourceApp && pbSourceApp.blob) {
|
|
7133
7451
|
var source = decodeURIComponent(escape(atob(pbSourceApp.blob)));
|
|
7134
7452
|
prompt += '--- Source Code ---\n' + source;
|
|
7135
7453
|
}
|
|
7136
7454
|
|
|
7455
|
+
// Booster-kit: if the user picked a starting template, hand the AI the skeleton to copy from.
|
|
7456
|
+
if (window.pbTemplate && window.pbTemplate.content) {
|
|
7457
|
+
prompt += '\n## Starting template (copy from this)\nUse this skeleton as your base — keep its boot, login pill, and self-hosted theme wiring intact; fill the {{...}} slots; build your views inside <main>. Return the COMPLETE single HTML file based on it.\n```html\n' + window.pbTemplate.content + '\n```\n';
|
|
7458
|
+
}
|
|
7459
|
+
|
|
7137
7460
|
return prompt;
|
|
7138
7461
|
}
|
|
7139
7462
|
|
|
@@ -7265,6 +7588,10 @@
|
|
|
7265
7588
|
unpublishApp: unpublishApp,
|
|
7266
7589
|
deleteServerApp: deleteServerApp,
|
|
7267
7590
|
toggleParkApp: toggleParkApp,
|
|
7591
|
+
detailAboutEdit: detailAboutEdit,
|
|
7592
|
+
detailAboutCancel: detailAboutCancel,
|
|
7593
|
+
detailAboutSave: detailAboutSave,
|
|
7594
|
+
editAppDetails: editAppDetails,
|
|
7268
7595
|
showSubdomainModal: showSubdomainModal,
|
|
7269
7596
|
openConsents: openConsents,
|
|
7270
7597
|
closeConsents: closeConsents,
|
|
@@ -7377,8 +7704,9 @@
|
|
|
7377
7704
|
});
|
|
7378
7705
|
|
|
7379
7706
|
// ── Add App button ──────────────────────────────
|
|
7707
|
+
// Step 0: not signed in → open the sign-in/register dialog first, then the Add dialog.
|
|
7380
7708
|
document.getElementById('add-btn').addEventListener('click', function () {
|
|
7381
|
-
showModal
|
|
7709
|
+
requireSignInThen(showModal);
|
|
7382
7710
|
});
|
|
7383
7711
|
|
|
7384
7712
|
// ── Generate with AI button ─────────────────────
|
|
@@ -7497,6 +7825,19 @@
|
|
|
7497
7825
|
var fileInput = document.getElementById('file-input');
|
|
7498
7826
|
var browseBtn = document.getElementById('browse-btn');
|
|
7499
7827
|
|
|
7828
|
+
// Auto-fill the app name from pasted code (AIMEAT manifest / <title>) when the
|
|
7829
|
+
// name field is still empty — the description rides along into the publish step.
|
|
7830
|
+
var pasteArea = document.getElementById('app-paste-code');
|
|
7831
|
+
if (pasteArea) {
|
|
7832
|
+
pasteArea.addEventListener('input', function () {
|
|
7833
|
+
var nameInput = document.getElementById('app-name');
|
|
7834
|
+
if (nameInput && !nameInput.value.trim()) {
|
|
7835
|
+
var meta = parseAppMeta(pasteArea.value);
|
|
7836
|
+
if (meta.name) nameInput.value = meta.name;
|
|
7837
|
+
}
|
|
7838
|
+
});
|
|
7839
|
+
}
|
|
7840
|
+
|
|
7500
7841
|
dropZone.addEventListener('dragover', function (e) {
|
|
7501
7842
|
e.preventDefault();
|
|
7502
7843
|
e.stopPropagation();
|
|
@@ -7700,13 +8041,15 @@
|
|
|
7700
8041
|
'Media & misc: aimeat-audio.js, aimeat-speech.js, aimeat-header.js, aimeat-tunnel.js.\n' +
|
|
7701
8042
|
'Real-time multiplayer: no lib — POST/GET ' + nodeUrl + '/v1/realtime/rooms then WebSocket /v1/realtime/ws?room=ID (ICE config at /v1/realtime/ice-servers).\n\n' +
|
|
7702
8043
|
'### Auth Pattern\n' +
|
|
8044
|
+
'Handle BOTH login paths: a fresh sign-in click (onLogin) AND a page that loads already signed in (restore the session yourself). `onLogin` fires ONLY on a fresh sign-in — NOT on reload when a session already exists.\n' +
|
|
7703
8045
|
'```html\n' +
|
|
7704
8046
|
'<script src="' + nodeUrl + '/v1/libs/aimeat-auth.js"></' + 'script>\n' +
|
|
7705
8047
|
'<script>\n' +
|
|
7706
|
-
'
|
|
7707
|
-
'
|
|
7708
|
-
'
|
|
7709
|
-
'
|
|
8048
|
+
'function showApp(session) { /* session.owner, session.jwt, session.fetch() */ }\n' +
|
|
8049
|
+
'function hideApp() { /* hide content, show a "Sign in" message */ }\n' +
|
|
8050
|
+
'AIMEAT.auth.mountLoginButton("#login", { onLogin: showApp, onLogout: hideApp });\n' +
|
|
8051
|
+
'// Already signed in on load? onLogin will NOT fire — restore the session yourself:\n' +
|
|
8052
|
+
'AIMEAT.auth.login().then(function (session) { if (session) showApp(session); });\n' +
|
|
7710
8053
|
'</' + 'script>\n' +
|
|
7711
8054
|
'```\n\n' +
|
|
7712
8055
|
'### Data Storage\n' +
|
|
@@ -7738,11 +8081,15 @@
|
|
|
7738
8081
|
'Notifications, Drag & Drop, Clipboard, Speech API, Fullscreen, Web Workers,\n' +
|
|
7739
8082
|
'CSS Animations, SVG, Gamepad API, Share API.\n\n' +
|
|
7740
8083
|
'### Design Guidelines\n' +
|
|
7741
|
-
'Use CSS variables and
|
|
8084
|
+
'Use CSS variables and RESPECT the user\'s AIMEAT theme: their light/dark choice from the AIMEAT pill is saved in localStorage "aimeat-theme" ("light"|"dark"). Light is the default; dark lives under [data-theme="dark"]; set that attribute from the saved choice on load (OS preference as fallback, live-update on change):\n' +
|
|
7742
8085
|
'```css\n' +
|
|
7743
8086
|
':root { --bg:#fafaf8; --card:#fff; --text:#1a1a2e; --accent:#e8564a; --border:#e5e7eb; --radius:12px; }\n' +
|
|
7744
|
-
'
|
|
7745
|
-
'
|
|
8087
|
+
':root[data-theme="dark"] { --bg:#14141c; --card:#1e1e2a; --text:#ececf4; --border:#2e2e40; }\n' +
|
|
8088
|
+
'```\n' +
|
|
8089
|
+
'```js\n' +
|
|
8090
|
+
'(function(){ function apply(t){ document.documentElement.setAttribute("data-theme", t==="dark"?"dark":"light"); }\n' +
|
|
8091
|
+
' apply(localStorage.getItem("aimeat-theme") || (matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"));\n' +
|
|
8092
|
+
' addEventListener("storage", function(e){ if(e.key==="aimeat-theme" && e.newValue) apply(e.newValue); }); })();\n' +
|
|
7746
8093
|
'```\n' +
|
|
7747
8094
|
'Always include: <meta name="viewport" content="width=device-width, initial-scale=1.0">\n' +
|
|
7748
8095
|
'Mobile-first responsive. Single HTML file with embedded CSS and JS.\n\n' +
|
|
@@ -7751,7 +8098,7 @@
|
|
|
7751
8098
|
'- Never use literal closing script tags in JS comments or strings (breaks HTML parser)\n' +
|
|
7752
8099
|
'- Keep it as a single self-contained HTML file\n' +
|
|
7753
8100
|
'- Gate AI features on AIMEAT.ai.isAvailable(); never hardcode an API key\n' +
|
|
7754
|
-
'- Theme with CSS variables;
|
|
8101
|
+
'- Theme with CSS variables; respect the user\'s AIMEAT light/dark choice (localStorage "aimeat-theme") with an OS-preference fallback\n' +
|
|
7755
8102
|
'- Include error handling and loading states for API calls\n\n' +
|
|
7756
8103
|
'--- Source Code ---\n' + code;
|
|
7757
8104
|
}
|
|
@@ -7850,11 +8197,10 @@
|
|
|
7850
8197
|
document.getElementById('pb-copy-btn').addEventListener('click', function() {
|
|
7851
8198
|
var prompt = buildPromptFromBuilder();
|
|
7852
8199
|
var btn = this;
|
|
8200
|
+
// Keep the dialog OPEN after copying so the user can read Step 3 (add & publish).
|
|
8201
|
+
var revert = function () { setTimeout(function () { btn.textContent = t('pb.copy'); }, 1400); };
|
|
7853
8202
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
7854
|
-
navigator.clipboard.writeText(prompt).then(function() {
|
|
7855
|
-
btn.textContent = 'Copied!';
|
|
7856
|
-
setTimeout(function() { btn.textContent = 'Copy Prompt'; closePbPanel(); }, 1200);
|
|
7857
|
-
});
|
|
8203
|
+
navigator.clipboard.writeText(prompt).then(function() { btn.textContent = '✔ ' + (t('pb.copied') || 'Copied!'); revert(); });
|
|
7858
8204
|
} else {
|
|
7859
8205
|
var ta = document.createElement('textarea');
|
|
7860
8206
|
ta.value = prompt;
|
|
@@ -7862,26 +8208,27 @@
|
|
|
7862
8208
|
ta.select();
|
|
7863
8209
|
document.execCommand('copy');
|
|
7864
8210
|
document.body.removeChild(ta);
|
|
7865
|
-
btn.textContent = 'Copied!';
|
|
7866
|
-
|
|
8211
|
+
btn.textContent = '✔ ' + (t('pb.copied') || 'Copied!');
|
|
8212
|
+
revert();
|
|
7867
8213
|
}
|
|
7868
8214
|
});
|
|
7869
8215
|
|
|
7870
|
-
|
|
7871
|
-
|
|
7872
|
-
|
|
8216
|
+
// Step 3 — close the prompt builder and open the Add App flow on the PASTE tab
|
|
8217
|
+
// (the AI hands back ready-to-paste code; the File tab is right there for a file).
|
|
8218
|
+
document.getElementById('pb-add-btn').addEventListener('click', function() {
|
|
8219
|
+
// Step 0 (sign-in/register) if needed, then open Add on the Paste tab.
|
|
8220
|
+
requireSignInThen(function () {
|
|
8221
|
+
closePbPanel();
|
|
8222
|
+
showModal();
|
|
8223
|
+
try { switchTab('paste'); } catch (e) { /* tab switch is best-effort */ }
|
|
8224
|
+
});
|
|
7873
8225
|
});
|
|
7874
8226
|
|
|
8227
|
+
// Mode radios are hidden (mode is implicit) and the cortex-extensions toggle was
|
|
8228
|
+
// removed — the helpful aimeat-* cortex UI libs are now always documented in the
|
|
8229
|
+
// prompt. Keep the mode radios in sync only for buildPromptFromBuilder's state.
|
|
7875
8230
|
document.querySelectorAll('input[name="pb-mode"]').forEach(function(radio) {
|
|
7876
|
-
radio.addEventListener('change',
|
|
7877
|
-
var desc = document.getElementById('pb-description');
|
|
7878
|
-
if (this.value === 'improve' && pbSourceApp) {
|
|
7879
|
-
desc.placeholder = 'Describe what changes to make to "' + (pbSourceApp.name || 'this app') + '"...';
|
|
7880
|
-
} else {
|
|
7881
|
-
desc.placeholder = 'Describe what the app should do...';
|
|
7882
|
-
}
|
|
7883
|
-
updatePbPreview();
|
|
7884
|
-
});
|
|
8231
|
+
radio.addEventListener('change', updatePbPreview);
|
|
7885
8232
|
});
|
|
7886
8233
|
|
|
7887
8234
|
document.getElementById('pb-description').addEventListener('input', updatePbPreview);
|