aimeat 1.33.2 → 1.35.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 +437 -15
- package/dist/locales/fi.json +437 -15
- 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 +464 -2
- package/dist/public/js/i18n.js +9 -0
- 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 +266 -17
- package/dist/public/js/services/secretary-policy.js +28 -2
- 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 +13 -4
- package/dist/public/views/profile/agents/state-detector.js +6 -0
- package/dist/public/views/profile/agents/tab-integration.js +11 -2
- 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 +217 -79
- package/dist/public/views/secretary/dashboard.js +617 -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 +836 -44
- 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/cli/connect/mcp/tools/organisms.d.ts +2 -0
- package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
- package/dist/src/cli/connect/mcp/tools/organisms.js +9 -1
- package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
- package/dist/src/cli/connect/onboarding-prompt.d.ts +3 -0
- package/dist/src/cli/connect/onboarding-prompt.d.ts.map +1 -1
- package/dist/src/cli/connect/onboarding-prompt.js +10 -3
- package/dist/src/cli/connect/onboarding-prompt.js.map +1 -1
- 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 +689 -7
- 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/agent-onboarding.d.ts +4 -0
- package/dist/src/mcp/agent-onboarding.d.ts.map +1 -1
- package/dist/src/mcp/agent-onboarding.js +28 -2
- package/dist/src/mcp/agent-onboarding.js.map +1 -1
- 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/agent-onboarding-schemas.d.ts +47 -0
- package/dist/src/models/agent-onboarding-schemas.d.ts.map +1 -1
- package/dist/src/models/agent-onboarding-schemas.js +117 -0
- package/dist/src/models/agent-onboarding-schemas.js.map +1 -1
- package/dist/src/models/offer-schemas.d.ts +8 -8
- package/dist/src/routes/agent-onboarding.d.ts +5 -0
- package/dist/src/routes/agent-onboarding.d.ts.map +1 -1
- package/dist/src/routes/agent-onboarding.js +21 -2
- package/dist/src/routes/agent-onboarding.js.map +1 -1
- 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 +34 -0
- package/dist/src/routes/secretary.d.ts.map +1 -0
- package/dist/src/routes/secretary.js +311 -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/onboarding-guide.d.ts +53 -0
- package/dist/src/services/onboarding-guide.d.ts.map +1 -0
- package/dist/src/services/onboarding-guide.js +81 -0
- package/dist/src/services/onboarding-guide.js.map +1 -0
- 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 +50 -2
- package/dist/src/services/scheduler.d.ts.map +1 -1
- package/dist/src/services/scheduler.js +454 -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/secretary-workbrief.d.ts +144 -0
- package/dist/src/services/secretary-workbrief.d.ts.map +1 -0
- package/dist/src/services/secretary-workbrief.js +315 -0
- package/dist/src/services/secretary-workbrief.js.map +1 -0
- package/dist/src/services/secretary-worktier.d.ts +118 -0
- package/dist/src/services/secretary-worktier.d.ts.map +1 -0
- package/dist/src/services/secretary-worktier.js +157 -0
- package/dist/src/services/secretary-worktier.js.map +1 -0
- package/dist/src/services/skill-bundle/generator.d.ts +3 -0
- package/dist/src/services/skill-bundle/generator.d.ts.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/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
|
@@ -55,6 +55,7 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
55
55
|
const [myOrganisms, setMyOrganisms] = useState(null);
|
|
56
56
|
const [publicOrganisms, setPublicOrganisms] = useState([]);
|
|
57
57
|
const [expanded, setExpanded] = useState(null);
|
|
58
|
+
const [archivedOpen, setArchivedOpen] = useState(false); // collapsible "Archived" section (closed by default)
|
|
58
59
|
// organism whose workspaces are open, then the specific workspace within it — both restored from
|
|
59
60
|
// sessionStorage so an F5 returns to where you were. openId set + openWs null = the workspace LIST.
|
|
60
61
|
const [openId, setOpenId] = useState(() => { try { return sessionStorage.getItem('aimeat.ws.openId') || null; } catch (e) { return null; } });
|
|
@@ -201,6 +202,16 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
201
202
|
return () => window.removeEventListener('aimeat-open-organism', onOpen);
|
|
202
203
|
}, []);
|
|
203
204
|
|
|
205
|
+
// Deep link from another view (e.g. the Secretary's "Open" links open in a new tab):
|
|
206
|
+
// ?org=…&ws=… opens that organism (+ workspace) on a cold navigation. Runs once on mount.
|
|
207
|
+
useEffect(() => {
|
|
208
|
+
try {
|
|
209
|
+
const params = new URLSearchParams(window.location.search);
|
|
210
|
+
const org = params.get('org');
|
|
211
|
+
if (org) { setOpenId(org); setOpenWs(params.get('ws') || null); setOpenSpace(null); setOpenSettings(false); }
|
|
212
|
+
} catch (e) { /* noop */ }
|
|
213
|
+
}, []);
|
|
214
|
+
|
|
204
215
|
// Cross-organism content search (debounced) — librarian FTS across all my own content.
|
|
205
216
|
useEffect(() => {
|
|
206
217
|
const query = gQuery.trim();
|
|
@@ -291,6 +302,28 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
291
302
|
}, { danger: true });
|
|
292
303
|
}, [showToast, loadData]);
|
|
293
304
|
|
|
305
|
+
// Archive / unarchive a whole organism (creator/admin). Archived organisms become read-only and
|
|
306
|
+
// their content is excluded from AI materials (overview/read/search); the workspaces cascade-archive
|
|
307
|
+
// and unarchive uses smart restore. Reversible — not a delete.
|
|
308
|
+
const handleArchive = useCallback(async (id, name, archived) => {
|
|
309
|
+
const verb = archived ? (t('organisms.archive') || 'Archive') : (t('organisms.unarchive') || 'Unarchive');
|
|
310
|
+
confirm(
|
|
311
|
+
(archived
|
|
312
|
+
? (t('organisms.confirmArchive') || 'Archive “{name}”? It becomes read-only and is hidden from AI operations until you unarchive it. Its workspaces are archived too.')
|
|
313
|
+
: (t('organisms.confirmUnarchive') || 'Unarchive “{name}”? It and the workspaces archived with it become active again.')
|
|
314
|
+
).replace('{name}', name),
|
|
315
|
+
async () => {
|
|
316
|
+
try {
|
|
317
|
+
if (archived) await orgService.archiveContent(id, { level: 'organism' });
|
|
318
|
+
else await orgService.unarchiveContent(id, { level: 'organism' });
|
|
319
|
+
showToast(archived ? (t('organisms.organismArchived') || 'Organism archived') : (t('organisms.organismUnarchived') || 'Organism restored'));
|
|
320
|
+
loadData();
|
|
321
|
+
} catch (e) { showToast((e && e.message) || 'Failed'); }
|
|
322
|
+
},
|
|
323
|
+
{ title: verb },
|
|
324
|
+
);
|
|
325
|
+
}, [showToast, loadData, confirm]);
|
|
326
|
+
|
|
294
327
|
const toggleExpand = useCallback((id) => {
|
|
295
328
|
setExpanded(prev => prev === id ? null : id);
|
|
296
329
|
}, []);
|
|
@@ -348,6 +381,9 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
348
381
|
const menuItems = isMine ? [
|
|
349
382
|
canEdit && { label: t('organisms.settings') || 'Settings', icon: '⚙', onClick: () => openHome(true) },
|
|
350
383
|
{ label: t('organisms.exportOrg') || 'Export organism', icon: '⬇', onClick: () => exportOrganismZip(org, showToast) },
|
|
384
|
+
canEdit && (org.archived
|
|
385
|
+
? { label: t('organisms.unarchive') || 'Unarchive', icon: '♻️', onClick: () => handleArchive(org.id, org.name, false) }
|
|
386
|
+
: { label: t('organisms.archive') || 'Archive', icon: '🗄️', onClick: () => handleArchive(org.id, org.name, true) }),
|
|
351
387
|
!isCreator && { label: t('organisms.leave') || 'Leave', danger: true, onClick: () => handleLeave(org.id, org.name) },
|
|
352
388
|
isCreator && { label: t('organisms.delete') || 'Delete', danger: true, onClick: () => handleDelete(org.id, org.name) },
|
|
353
389
|
] : [];
|
|
@@ -367,6 +403,7 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
367
403
|
<span class="pj-org-name">${(org.name)}</span>
|
|
368
404
|
<span class="badge badge-info">${typeLabel}</span>
|
|
369
405
|
${org.visibility !== 'public' ? html`<span class="pj-org-lock" title=${visLabel}>${'🔒'}</span>` : null}
|
|
406
|
+
${org.archived ? html`<span class="pj-tab-pill" title=${t('organisms.archivedHint') || 'Archived — read-only, hidden from AI operations'}>${'🗄️ '}${t('organisms.archived') || 'archived'}</span>` : null}
|
|
370
407
|
</div>
|
|
371
408
|
${org.description ? html`<div class="pj-org-desc">${(org.description)}</div>` : null}
|
|
372
409
|
</div>
|
|
@@ -514,14 +551,30 @@ export default function OrganismsTab({ session, showToast, onStats }) {
|
|
|
514
551
|
<button class="btn-primary btn-sm" onClick=${() => setShowCreate(true)}>${'+ '}${t('organisms.createNew') || 'Create Organism'}</button>
|
|
515
552
|
</div>
|
|
516
553
|
</div>
|
|
517
|
-
${
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
554
|
+
${(() => {
|
|
555
|
+
// Active organisms drive the working list; archived ones move into a collapsed "Archived"
|
|
556
|
+
// section below (read-only/retired — out of the way, but restorable from there).
|
|
557
|
+
const activeMine = sortedMine.filter(o => !o.archived);
|
|
558
|
+
const archivedMine = sortedMine.filter(o => o.archived);
|
|
559
|
+
return html`
|
|
560
|
+
${activeMine.length === 0 && archivedMine.length === 0
|
|
561
|
+
? html`<div class="empty">${t('organisms.empty') || 'You are not part of any organisms yet.'}</div>`
|
|
562
|
+
: html`
|
|
563
|
+
${activeMine.length === 0
|
|
564
|
+
? html`<div class="empty">${t('organisms.allArchived') || 'All your organisms are archived.'}</div>`
|
|
565
|
+
: html`
|
|
566
|
+
<div class="pj-org-list">${activeMine.map(org => renderOrgRow(org, true))}</div>
|
|
567
|
+
${sortMode === 'custom' && activeMine.length > 1 ? html`
|
|
568
|
+
<div class="pj-org-hint">${t('organisms.reorderHint') || 'Drag rows to reorder — the order is saved to your profile.'}</div>` : null}`}
|
|
569
|
+
${archivedMine.length > 0 ? html`
|
|
570
|
+
<button class="pj-struct-toggle section-title-spaced" aria-expanded=${archivedOpen} onClick=${() => setArchivedOpen(o => !o)}>
|
|
571
|
+
<span class="pj-struct-caret">${archivedOpen ? '▾' : '▸'}</span>
|
|
572
|
+
<span>${'🗄️ '}${(t('organisms.archivedSection') || 'Archived ({n})').replace('{n}', String(archivedMine.length))}</span>
|
|
573
|
+
</button>
|
|
574
|
+
${archivedOpen ? html`<div class="pj-org-list">${archivedMine.map(org => renderOrgRow(org, true))}</div>` : null}` : null}
|
|
575
|
+
`}
|
|
576
|
+
`;
|
|
577
|
+
})()}
|
|
525
578
|
|
|
526
579
|
<!-- Discover -->
|
|
527
580
|
${publicOrganisms.length > 0 && html`
|
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file secretary/calendar.js
|
|
3
|
+
* @description Phase 4 (Decision B) — a REAL calendar for the Secretary: month / week / day views of
|
|
4
|
+
* what it has done and what it will do. Events come from real data, no placeholders:
|
|
5
|
+
* - PAST → the Home feed (`secretary.feed`) entries, placed on the day/hour they happened.
|
|
6
|
+
* - FUTURE→ the daily autonomous tick (the `secretary` schedule's cron hour) projected forward, plus
|
|
7
|
+
* each recurring Routine's cadence (daily/weekly) projected from its `nextRunAt`.
|
|
8
|
+
* Pure event-builder (`buildCalendarEvents`) + a small view-state hook (`useCalendar`) + the render
|
|
9
|
+
* (`calendarCard`). The view (views/secretary.js) passes in feed/schedule/routines it already loads.
|
|
10
|
+
* @structure buildCalendarEvents · useCalendar · calendarCard (+ date helpers)
|
|
11
|
+
* @usage const cal = useCalendar(); calendarCard({ ...cal, feed: auto.feed, schedule: auto.schedule, routines: next.routines })
|
|
12
|
+
* @version-history
|
|
13
|
+
* v0.2.0 — 2026-06-28 — CalendarEventDialog: the trigger editor can also set the bound action
|
|
14
|
+
* (remind / run a workflow / run a specialist) in place; takes workflows + specialists props.
|
|
15
|
+
* v0.1.0 — 2026-06-28 — Decision B: month/week/day calendar of feed (past) + tick & routine cadence (future).
|
|
16
|
+
*/
|
|
17
|
+
import { h } from 'preact';
|
|
18
|
+
import htm from 'htm';
|
|
19
|
+
import { useState, useMemo, useCallback } from 'preact/hooks';
|
|
20
|
+
import { t } from '/js/i18n.js';
|
|
21
|
+
import { Markdown } from '/components/Markdown.js';
|
|
22
|
+
const html = htm.bind(h);
|
|
23
|
+
|
|
24
|
+
// ── date helpers (browser — real Date is fine) ──
|
|
25
|
+
const DAY_MS = 86400000;
|
|
26
|
+
function startOfDay(d) { const x = new Date(d); x.setHours(0, 0, 0, 0); return x; }
|
|
27
|
+
function addDays(d, n) { const x = new Date(d); x.setDate(x.getDate() + n); return x; }
|
|
28
|
+
function startOfWeek(d) { const x = startOfDay(d); const wd = (x.getDay() + 6) % 7; return addDays(x, -wd); } // Monday
|
|
29
|
+
function startOfMonth(d) { const x = startOfDay(d); x.setDate(1); return x; }
|
|
30
|
+
function sameDay(a, b) { return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate(); }
|
|
31
|
+
function fmtTime(d) { return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); }
|
|
32
|
+
|
|
33
|
+
/** The hour-of-day a `m h * * *` cron fires (daily ticks); null if not a simple daily cron. */
|
|
34
|
+
export function cronHour(cron) {
|
|
35
|
+
const m = String(cron || '').trim().match(/^\d+\s+(\d{1,2})\s+\*\s+\*\s+\*$/);
|
|
36
|
+
return m ? Math.min(23, parseInt(m[1], 10)) : null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Build dated calendar events within [start, end). Past = feed entries; future (≥ now) = the daily tick
|
|
41
|
+
* (projected at the schedule's cron hour) + each recurring routine's cadence projected from nextRunAt.
|
|
42
|
+
* Pure given `now`. Each event: { at: Date, type: 'done'|'scheduled', kind, text }.
|
|
43
|
+
*/
|
|
44
|
+
export function buildCalendarEvents({ feed, schedule, routines, triggers, start, end, now }) {
|
|
45
|
+
const events = [];
|
|
46
|
+
for (const it of (feed || [])) {
|
|
47
|
+
if (!it || !it.ts) continue;
|
|
48
|
+
const d = new Date(it.ts);
|
|
49
|
+
if (d >= start && d < end) events.push({ at: d, type: 'done', kind: it.kind || 'act', text: it.text || '', ref: { srcType: 'feed', item: it } });
|
|
50
|
+
}
|
|
51
|
+
if (schedule && schedule.enabled !== false) {
|
|
52
|
+
const hour = cronHour(schedule.cron);
|
|
53
|
+
if (hour !== null) {
|
|
54
|
+
let d = startOfDay(now > start ? now : start); d.setHours(hour, 0, 0, 0);
|
|
55
|
+
if (d < now) d = addDays(d, 1);
|
|
56
|
+
for (let i = 0; i < 370 && d < end; i++, d = addDays(d, 1)) {
|
|
57
|
+
events.push({ at: new Date(d), type: 'scheduled', kind: 'tick', text: t('secretary.cal.tickEvent'), ref: { srcType: 'tick' } });
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (const r of (routines || [])) {
|
|
62
|
+
if (!r || !r.cadence || !r.nextRunAt) continue;
|
|
63
|
+
const step = r.cadence === 'weekly' ? 7 : 1;
|
|
64
|
+
let d = new Date(r.nextRunAt);
|
|
65
|
+
for (let i = 0; i < 370 && d < end; i++, d = addDays(d, step)) {
|
|
66
|
+
if (d >= start && d >= now) events.push({ at: new Date(d), type: 'scheduled', kind: 'routine', text: r.title || t('secretary.cal.routineRun'), ref: { srcType: 'routine', id: r.id, routine: r } });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
for (const tr of (triggers || [])) {
|
|
70
|
+
if (!tr || tr.status !== 'armed' || !tr.nextFireAt) continue;
|
|
71
|
+
if (tr.kind === 'time') {
|
|
72
|
+
const d = new Date(tr.nextFireAt);
|
|
73
|
+
if (d >= start && d < end) events.push({ at: d, type: 'scheduled', kind: 'trigger', text: tr.label || t('secretary.cal.triggerEvent'), ref: { srcType: 'trigger', id: tr.id, trigger: tr } });
|
|
74
|
+
} else if (tr.kind === 'recurring') {
|
|
75
|
+
const stepMs = tr.cadence === 'monthly' ? 30 * DAY_MS : tr.cadence === 'weekly' ? 7 * DAY_MS : DAY_MS;
|
|
76
|
+
let d = new Date(tr.nextFireAt);
|
|
77
|
+
for (let i = 0; i < 370 && d < end; i++, d = new Date(d.getTime() + stepMs)) {
|
|
78
|
+
if (d >= start) events.push({ at: new Date(d), type: 'scheduled', kind: 'trigger', text: tr.label || t('secretary.cal.triggerEvent'), ref: { srcType: 'trigger', id: tr.id, trigger: tr } });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return events.sort((a, b) => a.at.getTime() - b.at.getTime());
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Calendar view-state: mode (month/week/day) + an anchor date, with prev/next/today navigation. */
|
|
86
|
+
export function useCalendar() {
|
|
87
|
+
const [mode, setMode] = useState('week');
|
|
88
|
+
const [anchorMs, setAnchorMs] = useState(() => startOfDay(new Date()).getTime());
|
|
89
|
+
const stepN = mode === 'month' ? 0 : (mode === 'week' ? 7 : 1);
|
|
90
|
+
const shift = useCallback((dir) => {
|
|
91
|
+
setAnchorMs((ms) => {
|
|
92
|
+
const d = new Date(ms);
|
|
93
|
+
if (mode === 'month') { d.setMonth(d.getMonth() + dir); return startOfDay(d).getTime(); }
|
|
94
|
+
return addDays(d, dir * stepN).getTime();
|
|
95
|
+
});
|
|
96
|
+
}, [mode, stepN]);
|
|
97
|
+
const goToday = useCallback(() => setAnchorMs(startOfDay(new Date()).getTime()), []);
|
|
98
|
+
const goDay = useCallback((d) => { setAnchorMs(startOfDay(d).getTime()); setMode('day'); }, []);
|
|
99
|
+
return { mode, setMode, anchor: new Date(anchorMs), prev: () => shift(-1), next: () => shift(1), today: goToday, goDay };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const HOUR_START = 6;
|
|
103
|
+
const HOUR_END = 22;
|
|
104
|
+
|
|
105
|
+
/** The month/week/day calendar card. */
|
|
106
|
+
export function calendarCard(p) {
|
|
107
|
+
const now = new Date();
|
|
108
|
+
const { mode, anchor } = p;
|
|
109
|
+
const weekdays = useMemo(() => {
|
|
110
|
+
const mon = startOfWeek(new Date(2024, 0, 1)); // a Monday
|
|
111
|
+
return Array.from({ length: 7 }, (_, i) => addDays(mon, i).toLocaleDateString([], { weekday: 'short' }));
|
|
112
|
+
}, []);
|
|
113
|
+
|
|
114
|
+
// Visible range per mode.
|
|
115
|
+
const { start, end, title } = useMemo(() => {
|
|
116
|
+
if (mode === 'month') {
|
|
117
|
+
const first = startOfMonth(anchor);
|
|
118
|
+
const gridStart = startOfWeek(first);
|
|
119
|
+
return { start: gridStart, end: addDays(gridStart, 42), title: anchor.toLocaleDateString([], { month: 'long', year: 'numeric' }) };
|
|
120
|
+
}
|
|
121
|
+
if (mode === 'week') {
|
|
122
|
+
const s = startOfWeek(anchor);
|
|
123
|
+
return { start: s, end: addDays(s, 7), title: `${s.toLocaleDateString([], { day: 'numeric', month: 'short' })} – ${addDays(s, 6).toLocaleDateString([], { day: 'numeric', month: 'short' })}` };
|
|
124
|
+
}
|
|
125
|
+
const s = startOfDay(anchor);
|
|
126
|
+
return { start: s, end: addDays(s, 1), title: s.toLocaleDateString([], { weekday: 'long', day: 'numeric', month: 'long' }) };
|
|
127
|
+
}, [mode, anchor]);
|
|
128
|
+
|
|
129
|
+
const events = useMemo(
|
|
130
|
+
() => buildCalendarEvents({ feed: p.feed, schedule: p.schedule, routines: p.routines, triggers: p.triggers, start, end, now }),
|
|
131
|
+
[p.feed, p.schedule, p.routines, p.triggers, start, end],
|
|
132
|
+
);
|
|
133
|
+
const eventsOn = useCallback((day) => events.filter((e) => sameDay(e.at, day)), [events]);
|
|
134
|
+
|
|
135
|
+
const head = html`
|
|
136
|
+
<div class="sec-card-head">
|
|
137
|
+
<h2 class="sec-h2">${t('secretary.cal.title')}</h2>
|
|
138
|
+
<div class="sec-cal-nav">
|
|
139
|
+
<div class="sec-cal-modes">
|
|
140
|
+
${['month', 'week', 'day'].map((m) => html`
|
|
141
|
+
<button class="btn-sm ${mode === m ? 'btn-primary' : 'btn-ghost'}" key=${m} onClick=${() => p.setMode(m)}>${t('secretary.cal.' + m)}</button>`)}
|
|
142
|
+
</div>
|
|
143
|
+
<div class="sec-cal-move">
|
|
144
|
+
<button class="btn-ghost btn-sm" onClick=${p.prev} title=${t('secretary.cal.prev')}>‹</button>
|
|
145
|
+
<button class="btn-ghost btn-sm" onClick=${p.today}>${t('secretary.cal.today')}</button>
|
|
146
|
+
<button class="btn-ghost btn-sm" onClick=${p.next} title=${t('secretary.cal.next')}>›</button>
|
|
147
|
+
</div>
|
|
148
|
+
</div>
|
|
149
|
+
<div class="sec-cal-range">${title}</div>
|
|
150
|
+
</div>`;
|
|
151
|
+
|
|
152
|
+
const onEv = (e) => (ev) => { ev.stopPropagation(); if (p.onEventClick) p.onEventClick(e); };
|
|
153
|
+
const dot = (e, i) => html`<span class="sec-cal-ev sec-cal-ev--${e.type} sec-cal-ev--${e.kind}" key=${i} title=${`${fmtTime(e.at)} · ${e.text}`} onClick=${onEv(e)}>${e.text}</span>`;
|
|
154
|
+
|
|
155
|
+
if (mode === 'month') {
|
|
156
|
+
const cells = Array.from({ length: 42 }, (_, i) => addDays(start, i));
|
|
157
|
+
return html`
|
|
158
|
+
<section class="sec-card sec-cal">
|
|
159
|
+
${head}
|
|
160
|
+
<div class="sec-cal-weekrow">${weekdays.map((w, i) => html`<div class="sec-cal-wd" key=${i}>${w}</div>`)}</div>
|
|
161
|
+
<div class="sec-cal-month">
|
|
162
|
+
${cells.map((day, i) => {
|
|
163
|
+
const evs = eventsOn(day);
|
|
164
|
+
const inMonth = day.getMonth() === anchor.getMonth();
|
|
165
|
+
return html`
|
|
166
|
+
<button class="sec-cal-cell ${inMonth ? '' : 'sec-cal-cell--dim'} ${sameDay(day, now) ? 'sec-cal-cell--today' : ''}" key=${i} onClick=${() => p.goDay(day)}>
|
|
167
|
+
<span class="sec-cal-daynum">${day.getDate()}</span>
|
|
168
|
+
<span class="sec-cal-cell-evs">${evs.slice(0, 3).map(dot)}${evs.length > 3 ? html`<span class="sec-cal-more">+${evs.length - 3}</span>` : null}</span>
|
|
169
|
+
</button>`;
|
|
170
|
+
})}
|
|
171
|
+
</div>
|
|
172
|
+
</section>`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
if (mode === 'week') {
|
|
176
|
+
const days = Array.from({ length: 7 }, (_, i) => addDays(start, i));
|
|
177
|
+
return html`
|
|
178
|
+
<section class="sec-card sec-cal">
|
|
179
|
+
${head}
|
|
180
|
+
<div class="sec-cal-week">
|
|
181
|
+
${days.map((day, i) => {
|
|
182
|
+
const evs = eventsOn(day);
|
|
183
|
+
return html`
|
|
184
|
+
<div class="sec-cal-weekcol ${sameDay(day, now) ? 'sec-cal-weekcol--today' : ''}" key=${i}>
|
|
185
|
+
<button class="sec-cal-weekcol-head" onClick=${() => p.goDay(day)}>
|
|
186
|
+
<span class="sec-cal-wd">${day.toLocaleDateString([], { weekday: 'short' })}</span>
|
|
187
|
+
<span class="sec-cal-daynum">${day.getDate()}</span>
|
|
188
|
+
</button>
|
|
189
|
+
<div class="sec-cal-weekcol-evs">
|
|
190
|
+
${evs.length === 0 ? html`<span class="sec-hint sec-cal-empty">·</span>`
|
|
191
|
+
: evs.map((e, j) => html`<div class="sec-cal-ev sec-cal-ev--${e.type} sec-cal-ev--${e.kind} sec-cal-ev--click" key=${j} onClick=${onEv(e)} title=${e.text}><span class="sec-cal-evtime">${fmtTime(e.at)}</span> ${e.text}</div>`)}
|
|
192
|
+
</div>
|
|
193
|
+
</div>`;
|
|
194
|
+
})}
|
|
195
|
+
</div>
|
|
196
|
+
</section>`;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// day view — hourly rail HOUR_START..HOUR_END, with "earlier"/"later" buckets for out-of-range events.
|
|
200
|
+
const day = start;
|
|
201
|
+
const dayEvents = eventsOn(day);
|
|
202
|
+
const earlier = dayEvents.filter((e) => e.at.getHours() < HOUR_START);
|
|
203
|
+
const later = dayEvents.filter((e) => e.at.getHours() > HOUR_END);
|
|
204
|
+
const hours = Array.from({ length: HOUR_END - HOUR_START + 1 }, (_, i) => HOUR_START + i);
|
|
205
|
+
return html`
|
|
206
|
+
<section class="sec-card sec-cal">
|
|
207
|
+
${head}
|
|
208
|
+
${dayEvents.length === 0 ? html`<div class="sec-hint sec-cal-empty">${t('secretary.cal.noEvents')}</div>` : null}
|
|
209
|
+
${earlier.length ? html`<div class="sec-cal-bucket"><span class="sec-cal-hour">${t('secretary.cal.earlier')}</span><div class="sec-cal-hour-evs">${earlier.map((e, j) => html`<div class="sec-cal-ev sec-cal-ev--${e.type} sec-cal-ev--${e.kind} sec-cal-ev--click" key=${j} onClick=${onEv(e)}><span class="sec-cal-evtime">${fmtTime(e.at)}</span> ${e.text}</div>`)}</div></div>` : null}
|
|
210
|
+
<div class="sec-cal-day">
|
|
211
|
+
${hours.map((hr) => {
|
|
212
|
+
const evs = dayEvents.filter((e) => e.at.getHours() === hr);
|
|
213
|
+
return html`
|
|
214
|
+
<div class="sec-cal-hourrow" key=${hr}>
|
|
215
|
+
<span class="sec-cal-hour">${String(hr).padStart(2, '0')}:00</span>
|
|
216
|
+
<div class="sec-cal-hour-evs">${evs.map((e, j) => html`<div class="sec-cal-ev sec-cal-ev--${e.type} sec-cal-ev--${e.kind} sec-cal-ev--click" key=${j} onClick=${onEv(e)}><span class="sec-cal-evtime">${fmtTime(e.at)}</span> ${e.text}</div>`)}</div>
|
|
217
|
+
</div>`;
|
|
218
|
+
})}
|
|
219
|
+
</div>
|
|
220
|
+
${later.length ? html`<div class="sec-cal-bucket"><span class="sec-cal-hour">${t('secretary.cal.later')}</span><div class="sec-cal-hour-evs">${later.map((e, j) => html`<div class="sec-cal-ev sec-cal-ev--${e.type} sec-cal-ev--${e.kind} sec-cal-ev--click" key=${j} onClick=${onEv(e)}><span class="sec-cal-evtime">${fmtTime(e.at)}</span> ${e.text}</div>`)}</div></div>` : null}
|
|
221
|
+
</section>`;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Type-specific editor for a clicked calendar entry — edit it IN PLACE (no navigation). The entries are
|
|
226
|
+
* heterogeneous (trigger = memory record · routine = secretary.config sub-object · tick = a schedule ·
|
|
227
|
+
* feed = an item in secretary.feed), so this dispatches by ref.srcType and drives the SAME domain hooks
|
|
228
|
+
* the dedicated cards use, so a change persists to the right backing store and shows everywhere.
|
|
229
|
+
* p = { event, onClose, triggers:{update,togglePause,remove}, routines:{setCadence,setStatus}, tick:{toggle,run} }
|
|
230
|
+
*/
|
|
231
|
+
export function CalendarEventDialog(p) {
|
|
232
|
+
const ev = p.event;
|
|
233
|
+
const ref = (ev && ev.ref) || {};
|
|
234
|
+
const tr = ref.trigger || {};
|
|
235
|
+
const ro = ref.routine || {};
|
|
236
|
+
const ta = tr.action || {};
|
|
237
|
+
const [label, setLabel] = useState(tr.label || '');
|
|
238
|
+
const [cadence, setCadence] = useState(tr.cadence || ro.cadence || 'weekly');
|
|
239
|
+
const [actKind, setActKind] = useState(ta.kind || 'remind');
|
|
240
|
+
const [actWf, setActWf] = useState(ta.workflowId || '');
|
|
241
|
+
const [actSpec, setActSpec] = useState(ta.specialistName || '');
|
|
242
|
+
const [actPrompt, setActPrompt] = useState(ta.prompt || '');
|
|
243
|
+
const [busy, setBusy] = useState(false);
|
|
244
|
+
const workflows = p.workflows || [];
|
|
245
|
+
const specialists = p.specialists || [];
|
|
246
|
+
/** Compose the bound action from the dialog's local state (mirrors use-triggers buildAction). */
|
|
247
|
+
const buildAct = () => {
|
|
248
|
+
const summary = (label.trim() || tr.label || '');
|
|
249
|
+
if (actKind === 'workflow' && actWf) return { kind: 'workflow', summary, workflowId: actWf };
|
|
250
|
+
if (actKind === 'specialist' && actSpec) return { kind: 'specialist', summary, specialistName: actSpec, prompt: actPrompt.trim() || summary };
|
|
251
|
+
return { kind: 'remind', summary };
|
|
252
|
+
};
|
|
253
|
+
if (!ev) return null;
|
|
254
|
+
const run = async (fn) => { setBusy(true); try { await fn(); } finally { setBusy(false); p.onClose(); } };
|
|
255
|
+
|
|
256
|
+
let title = ev.text || t('secretary.cal.title');
|
|
257
|
+
let body = null;
|
|
258
|
+
if (ref.srcType === 'trigger') {
|
|
259
|
+
title = t('secretary.cal.dlg.trigger');
|
|
260
|
+
body = html`
|
|
261
|
+
<label class="sec-hint">${t('secretary.trig.labelPlaceholder')}</label>
|
|
262
|
+
<input class="sec-input" value=${label} onInput=${(e) => setLabel(e.target.value)} />
|
|
263
|
+
${tr.kind === 'recurring' ? html`
|
|
264
|
+
<label class="sec-hint">${t('secretary.cal.dlg.cadence')}</label>
|
|
265
|
+
<select class="sec-input" value=${cadence} onChange=${(e) => setCadence(e.target.value)}>
|
|
266
|
+
${['daily', 'weekly', 'monthly'].map((c) => html`<option value=${c} selected=${cadence === c}>${t('secretary.trig.cad.' + c)}</option>`)}
|
|
267
|
+
</select>` : html`<div class="sec-hint">${new Date(tr.nextFireAt || ev.at).toLocaleString()}</div>`}
|
|
268
|
+
<label class="sec-hint">${t('secretary.trig.act.label')}</label>
|
|
269
|
+
<select class="sec-input" value=${actKind} onChange=${(e) => setActKind(e.target.value)}>
|
|
270
|
+
<option value="remind" selected=${actKind === 'remind'}>${t('secretary.trig.act.remind')}</option>
|
|
271
|
+
${workflows.length ? html`<option value="workflow" selected=${actKind === 'workflow'}>${t('secretary.trig.act.workflow')}</option>` : null}
|
|
272
|
+
${specialists.length ? html`<option value="specialist" selected=${actKind === 'specialist'}>${t('secretary.trig.act.specialist')}</option>` : null}
|
|
273
|
+
</select>
|
|
274
|
+
${actKind === 'workflow' ? html`<select class="sec-input" value=${actWf} onChange=${(e) => setActWf(e.target.value)}>
|
|
275
|
+
<option value="" selected=${!actWf}>${t('secretary.trig.act.pickWorkflow')}</option>
|
|
276
|
+
${workflows.map((w) => html`<option value=${w.id} key=${w.id} selected=${actWf === w.id}>${w.name || w.id}</option>`)}
|
|
277
|
+
</select>` : null}
|
|
278
|
+
${actKind === 'specialist' ? html`
|
|
279
|
+
<select class="sec-input" value=${actSpec} onChange=${(e) => setActSpec(e.target.value)}>
|
|
280
|
+
<option value="" selected=${!actSpec}>${t('secretary.trig.act.pickSpecialist')}</option>
|
|
281
|
+
${specialists.map((s) => html`<option value=${s.name} key=${s.name} selected=${actSpec === s.name}>${s.display_name || s.name}</option>`)}
|
|
282
|
+
</select>
|
|
283
|
+
<textarea class="sec-paste" rows="2" placeholder=${t('secretary.trig.act.promptPlaceholder')} value=${actPrompt} onInput=${(e) => setActPrompt(e.target.value)}></textarea>` : null}
|
|
284
|
+
<div class="sec-next-action-btns">
|
|
285
|
+
<button class="btn-primary btn-sm" disabled=${busy} onClick=${() => run(() => p.triggers.update(ref.id, { label: label.trim() || tr.label, cadence, action: buildAct() }))}>${t('secretary.cal.dlg.save')}</button>
|
|
286
|
+
<button class="btn-outline btn-sm" disabled=${busy} onClick=${() => run(() => p.triggers.togglePause(tr))}>${tr.status === 'paused' ? t('secretary.trig.resume') : t('secretary.trig.pause')}</button>
|
|
287
|
+
<button class="btn-ghost btn-sm sec-next-discard" disabled=${busy} onClick=${() => run(() => p.triggers.remove(tr))}>${t('secretary.cal.dlg.delete')}</button>
|
|
288
|
+
</div>`;
|
|
289
|
+
} else if (ref.srcType === 'routine') {
|
|
290
|
+
title = ro.title || t('secretary.cal.routineRun');
|
|
291
|
+
body = html`
|
|
292
|
+
<label class="sec-hint">${t('secretary.cal.dlg.cadence')}</label>
|
|
293
|
+
<select class="sec-input" value=${ro.cadence || 'none'} onChange=${(e) => run(() => p.routines.setCadence(ro, e.target.value))}>
|
|
294
|
+
${['none', 'daily', 'weekly'].map((c) => html`<option value=${c} selected=${(ro.cadence || 'none') === c}>${t('secretary.trig.cad.' + c) || c}</option>`)}
|
|
295
|
+
</select>
|
|
296
|
+
<div class="sec-next-action-btns">
|
|
297
|
+
<button class="btn-outline btn-sm" disabled=${busy} onClick=${() => run(() => p.routines.setStatus(ro, ro.status === 'paused' ? 'active' : 'paused'))}>${ro.status === 'paused' ? t('secretary.trig.resume') : t('secretary.trig.pause')}</button>
|
|
298
|
+
</div>`;
|
|
299
|
+
} else if (ref.srcType === 'tick') {
|
|
300
|
+
title = t('secretary.cal.tickEvent');
|
|
301
|
+
body = html`
|
|
302
|
+
<p class="sec-hint">${t('secretary.cal.dlg.tickDesc')}</p>
|
|
303
|
+
<div class="sec-next-action-btns">
|
|
304
|
+
<button class="btn-primary btn-sm" disabled=${busy} onClick=${() => run(() => p.tick.run())}>${t('secretary.auto.runNow')}</button>
|
|
305
|
+
<button class="btn-outline btn-sm" disabled=${busy} onClick=${() => run(() => p.tick.toggle())}>${t('secretary.auto.pause')}</button>
|
|
306
|
+
</div>`;
|
|
307
|
+
} else {
|
|
308
|
+
// feed entry — a past activity item; show its content, open its result, or remove it from the log.
|
|
309
|
+
title = t('secretary.cal.dlg.activity');
|
|
310
|
+
const item = ref.item || {};
|
|
311
|
+
body = html`
|
|
312
|
+
<div class="sec-hint">${new Date(ev.at).toLocaleString()}</div>
|
|
313
|
+
<div class="sec-stand-body"><${Markdown} text=${ev.text || ''} /></div>
|
|
314
|
+
<div class="sec-next-action-btns">
|
|
315
|
+
${item.href ? html`<a class="btn-outline btn-sm" href=${item.href} target="_blank" rel="noopener">${t('secretary.next.openResult')} ↗</a>` : null}
|
|
316
|
+
${item.id && p.feed && p.feed.remove ? html`<button class="btn-ghost btn-sm sec-next-discard" disabled=${busy} onClick=${() => run(() => p.feed.remove(item.id))}>${t('secretary.cal.dlg.delete')}</button>` : null}
|
|
317
|
+
</div>`;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return html`
|
|
321
|
+
<div class="sec-modal-backdrop" onClick=${p.onClose}>
|
|
322
|
+
<div class="sec-modal" onClick=${(e) => e.stopPropagation()}>
|
|
323
|
+
<div class="sec-card-head"><h2 class="sec-h2">${title}</h2><button class="btn-ghost btn-sm" onClick=${p.onClose}>✕</button></div>
|
|
324
|
+
${body}
|
|
325
|
+
</div>
|
|
326
|
+
</div>`;
|
|
327
|
+
}
|
|
@@ -20,7 +20,6 @@ import { h } from 'preact';
|
|
|
20
20
|
import htm from 'htm';
|
|
21
21
|
const html = htm.bind(h);
|
|
22
22
|
import { t } from '/js/i18n.js';
|
|
23
|
-
import { escHtml } from '/js/utils.js';
|
|
24
23
|
|
|
25
24
|
/** P2-A — when discover finds nothing, offer to CREATE the missing capability (Draft → approve). */
|
|
26
25
|
export function createResourceCard(p) {
|
|
@@ -30,8 +29,8 @@ export function createResourceCard(p) {
|
|
|
30
29
|
${p.created ? html`
|
|
31
30
|
<div class="sec-plan-result">
|
|
32
31
|
<div class="sec-status"><span class="sec-dot"></span> ${t('secretary.create.done')}</div>
|
|
33
|
-
<p class="sec-purpose">${
|
|
34
|
-
<p class="sec-hint"><code>${
|
|
32
|
+
<p class="sec-purpose">${(p.created.name)}</p>
|
|
33
|
+
<p class="sec-hint"><code>${(p.created.id)}</code></p>
|
|
35
34
|
<button class="btn-ghost btn-sm" onClick=${p.reset}>${t('secretary.plan.new')}</button>
|
|
36
35
|
</div>`
|
|
37
36
|
: p.draft ? html`
|
|
@@ -73,8 +72,8 @@ export function knowledgeCard(p) {
|
|
|
73
72
|
${p.created ? html`
|
|
74
73
|
<div class="sec-plan-result">
|
|
75
74
|
<div class="sec-status"><span class="sec-dot"></span> ${t('secretary.knowledge.done')}</div>
|
|
76
|
-
<p class="sec-purpose">${
|
|
77
|
-
<p class="sec-hint"><code>${
|
|
75
|
+
<p class="sec-purpose">${(p.created.name)}</p>
|
|
76
|
+
<p class="sec-hint"><code>${(p.created.package_id)}</code></p>
|
|
78
77
|
</div>` : null}
|
|
79
78
|
|
|
80
79
|
${(p.packages || []).length >= 2 ? html`
|
|
@@ -87,21 +86,21 @@ export function knowledgeCard(p) {
|
|
|
87
86
|
<div class="sec-note-bar">
|
|
88
87
|
<select class="sec-band" value=${p.linkForm.sourceId} onChange=${(e) => p.setLinkForm({ ...p.linkForm, sourceId: e.target.value })}>
|
|
89
88
|
<option value="">${t('secretary.knowledge.linkSource')}</option>
|
|
90
|
-
${p.packages.map((pk) => html`<option value=${pk.id} selected=${p.linkForm.sourceId === pk.id}>${
|
|
89
|
+
${p.packages.map((pk) => html`<option value=${pk.id} selected=${p.linkForm.sourceId === pk.id}>${(pk.name)}</option>`)}
|
|
91
90
|
</select>
|
|
92
91
|
<select class="sec-band" value=${p.linkForm.relation} onChange=${(e) => p.setLinkForm({ ...p.linkForm, relation: e.target.value })}>
|
|
93
92
|
${(p.LINK_RELATIONS || []).map((rel) => html`<option value=${rel} selected=${p.linkForm.relation === rel}>${rel}</option>`)}
|
|
94
93
|
</select>
|
|
95
94
|
<select class="sec-band" value=${p.linkForm.targetId} onChange=${(e) => p.setLinkForm({ ...p.linkForm, targetId: e.target.value })}>
|
|
96
95
|
<option value="">${t('secretary.knowledge.linkTarget')}</option>
|
|
97
|
-
${p.packages.map((pk) => html`<option value=${pk.id} selected=${p.linkForm.targetId === pk.id}>${
|
|
96
|
+
${p.packages.map((pk) => html`<option value=${pk.id} selected=${p.linkForm.targetId === pk.id}>${(pk.name)}</option>`)}
|
|
98
97
|
</select>
|
|
99
98
|
</div>
|
|
100
99
|
<input class="sec-input" placeholder=${t('secretary.knowledge.linkDesc')} value=${p.linkForm.description} onInput=${(e) => p.setLinkForm({ ...p.linkForm, description: e.target.value })} />
|
|
101
100
|
<button class="btn-primary" disabled=${!p.linkForm.sourceId || !p.linkForm.targetId || p.linkForm.sourceId === p.linkForm.targetId || !p.linkForm.description.trim() || p.linkForm.saving} onClick=${p.createLink}>${p.linkForm.saving ? t('secretary.applying') : t('secretary.knowledge.linkApprove')}</button>
|
|
102
101
|
</div>` : null}
|
|
103
102
|
${p.linkResult ? html`
|
|
104
|
-
<div class="sec-status"><span class="sec-dot"></span> ${
|
|
103
|
+
<div class="sec-status"><span class="sec-dot"></span> ${(p.linkResult.sourceName)} <em>${(p.linkResult.relation)}</em> ${(p.linkResult.targetName)}</div>` : null}` : null}
|
|
105
104
|
</section>`;
|
|
106
105
|
}
|
|
107
106
|
|
|
@@ -130,8 +129,8 @@ export function accessCard(p) {
|
|
|
130
129
|
: html`<ul class="sec-goal-list">
|
|
131
130
|
${p.consents.map((c) => html`<li class="sec-goal-row" key=${c.id}>
|
|
132
131
|
<div class="sec-goal-main">
|
|
133
|
-
<div class="sec-goal-title">${
|
|
134
|
-
<div class="sec-hint"><code>${
|
|
132
|
+
<div class="sec-goal-title">${(c.recipient)}</div>
|
|
133
|
+
<div class="sec-hint"><code>${(c.data_pattern)}</code> · ${(c.purpose)}</div>
|
|
135
134
|
</div>
|
|
136
135
|
<button class="btn-ghost btn-sm" onClick=${() => p.revokeGrant(c.id)}>${t('secretary.access.revoke')}</button>
|
|
137
136
|
</li>`)}
|
|
@@ -150,7 +149,7 @@ export function accessCard(p) {
|
|
|
150
149
|
? html`<div class="sec-hint">${t('secretary.access.noGroups')}</div>`
|
|
151
150
|
: html`<ul class="sec-goal-list">
|
|
152
151
|
${p.groups.map((g) => html`<li class="sec-goal-row" key=${g.id}>
|
|
153
|
-
<div class="sec-goal-main"><div class="sec-goal-title">${
|
|
152
|
+
<div class="sec-goal-main"><div class="sec-goal-title">${(g.name)}</div>
|
|
154
153
|
<div class="sec-hint">${(g.members || []).length} ${t('secretary.access.members')}</div></div>
|
|
155
154
|
</li>`)}
|
|
156
155
|
</ul>`}
|
|
@@ -183,14 +182,14 @@ export function crewCard(p) {
|
|
|
183
182
|
|
|
184
183
|
${c ? html`
|
|
185
184
|
<div class="sec-form sec-consent">
|
|
186
|
-
<div class="sec-status"><span class="sec-dot"></span> ${t('secretary.crew.consentTitle')} <code>${
|
|
185
|
+
<div class="sec-status"><span class="sec-dot"></span> ${t('secretary.crew.consentTitle')} <code>${(c.name)}</code></div>
|
|
187
186
|
<p class="sec-hint">${t('secretary.crew.consentNote')}</p>
|
|
188
187
|
<ul class="sec-goal-list">
|
|
189
188
|
${c.extras.map((x) => html`<li class="sec-goal-row" key=${x.scope}>
|
|
190
189
|
<label class="sec-goal-main">
|
|
191
190
|
<input type="checkbox" checked=${c.checked.includes(x.scope)} onChange=${() => p.toggleExtra(x.scope)} />
|
|
192
|
-
<span class="sec-goal-title">${
|
|
193
|
-
<span class="sec-hint"><code>${
|
|
191
|
+
<span class="sec-goal-title">${(x.description)}</span>
|
|
192
|
+
<span class="sec-hint"><code>${(x.scope)}</code></span>
|
|
194
193
|
</label>
|
|
195
194
|
</li>`)}
|
|
196
195
|
</ul>
|
|
@@ -206,8 +205,8 @@ export function crewCard(p) {
|
|
|
206
205
|
<ul class="sec-goal-list">
|
|
207
206
|
${p.pending.map((r) => html`<li class="sec-goal-row" key=${r.user_code}>
|
|
208
207
|
<div class="sec-goal-main">
|
|
209
|
-
<div class="sec-goal-title">${
|
|
210
|
-
<div class="sec-hint"><code>${
|
|
208
|
+
<div class="sec-goal-title">${(r.display_name || r.agent_name || t('secretary.crew.unnamed'))}</div>
|
|
209
|
+
<div class="sec-hint"><code>${(r.user_code)}</code> · ${t('secretary.crew.expiresIn')} ${Math.floor((r.expires_in || 0) / 60)}m</div>
|
|
211
210
|
</div>
|
|
212
211
|
<button class="btn-primary btn-sm" onClick=${() => p.approve(r.user_code, 'standard')}>${t('secretary.crew.approve')}</button>
|
|
213
212
|
<button class="btn-ghost btn-sm" onClick=${() => p.deny(r.user_code)}>${t('secretary.crew.deny')}</button>
|
|
@@ -220,7 +219,7 @@ export function crewCard(p) {
|
|
|
220
219
|
: html`<ul class="sec-goal-list">
|
|
221
220
|
${p.agents.map((a) => html`<li class="sec-goal-row" key=${a.gaii || a.name}>
|
|
222
221
|
<div class="sec-goal-main">
|
|
223
|
-
<div class="sec-goal-title">${
|
|
222
|
+
<div class="sec-goal-title">${(a.name)}</div>
|
|
224
223
|
<div class="sec-note-bar">
|
|
225
224
|
<select class="sec-band" value=${a.mode || 'interactive'} onChange=${(e) => p.setMode(a.name, e.target.value)}>
|
|
226
225
|
${p.MODES.map((m) => html`<option value=${m} selected=${(a.mode || 'interactive') === m}>${m}</option>`)}
|