aimeat 1.23.1 → 1.23.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (173) hide show
  1. package/dist/locales/en.json +674 -125
  2. package/dist/locales/fi.json +712 -163
  3. package/dist/public/components/ContactCard.js +28 -0
  4. package/dist/public/css/theme.css +25 -10
  5. package/dist/public/css/views/admin.css +12 -0
  6. package/dist/public/css/views/agents-detail.css +62 -10
  7. package/dist/public/css/views/landing.css +87 -0
  8. package/dist/public/css/views/offers.css +96 -0
  9. package/dist/public/css/views/portfolio.css +18 -0
  10. package/dist/public/css/views/profile.css +216 -7
  11. package/dist/public/css/views/start.css +96 -0
  12. package/dist/public/img/business-hero.png +0 -0
  13. package/dist/public/img/toimitus.png +0 -0
  14. package/dist/public/js/components/tag-cloud.js +46 -31
  15. package/dist/public/js/components/tag-editor.js +2 -2
  16. package/dist/public/js/recents.js +40 -0
  17. package/dist/public/js/services/admin.js +6 -0
  18. package/dist/public/js/services/agents.js +34 -0
  19. package/dist/public/js/services/offers.js +152 -0
  20. package/dist/public/js/services/organisms.js +28 -5
  21. package/dist/public/js/services/packages.js +1 -0
  22. package/dist/public/spa.html +32 -2
  23. package/dist/public/views/admin/subdomains-tab.js +182 -0
  24. package/dist/public/views/admin.js +2 -0
  25. package/dist/public/views/business.js +119 -0
  26. package/dist/public/views/how-it-works.js +159 -0
  27. package/dist/public/views/landing.js +235 -0
  28. package/dist/public/views/portfolio.js +120 -23
  29. package/dist/public/views/pricing.js +75 -0
  30. package/dist/public/views/profile/access-tab.js +124 -51
  31. package/dist/public/views/profile/agents/agent-card.js +68 -37
  32. package/dist/public/views/profile/agents/shared-board.js +56 -22
  33. package/dist/public/views/profile/agents/tab-activity.js +24 -12
  34. package/dist/public/views/profile/agents/tab-agent-config.js +31 -1
  35. package/dist/public/views/profile/agents/tab-data-access.js +9 -13
  36. package/dist/public/views/profile/agents/tab-integration.js +27 -1
  37. package/dist/public/views/profile/agents/tab-messages.js +6 -11
  38. package/dist/public/views/profile/agents/tab-quality.js +45 -5
  39. package/dist/public/views/profile/agents-tab.js +41 -6
  40. package/dist/public/views/profile/apps-tab.js +7 -1
  41. package/dist/public/views/profile/knowledge-tab.js +4 -1
  42. package/dist/public/views/profile/landing-page.js +427 -123
  43. package/dist/public/views/profile/memory-tab.js +409 -211
  44. package/dist/public/views/profile/nodes-tab.js +18 -3
  45. package/dist/public/views/profile/offers-tab.js +310 -0
  46. package/dist/public/views/profile/organisms-tab.js +1330 -485
  47. package/dist/public/views/profile/packages-tab.js +152 -16
  48. package/dist/public/views/profile/portfolio-tab.js +98 -31
  49. package/dist/public/views/profile.js +2 -0
  50. package/dist/public/views/start-flows.js +281 -0
  51. package/dist/public/views/start.js +258 -0
  52. package/dist/src/cli/connect/mcp/tools/organisms.d.ts +3 -0
  53. package/dist/src/cli/connect/mcp/tools/organisms.d.ts.map +1 -1
  54. package/dist/src/cli/connect/mcp/tools/organisms.js +20 -3
  55. package/dist/src/cli/connect/mcp/tools/organisms.js.map +1 -1
  56. package/dist/src/cli/connect/tool-call.d.ts.map +1 -1
  57. package/dist/src/cli/connect/tool-call.js +20 -1
  58. package/dist/src/cli/connect/tool-call.js.map +1 -1
  59. package/dist/src/generated/api-types.d.ts +856 -17
  60. package/dist/src/generated/api-types.d.ts.map +1 -1
  61. package/dist/src/generated/prisma-postgres/edge.js +15 -3
  62. package/dist/src/generated/prisma-postgres/index-browser.js +12 -0
  63. package/dist/src/generated/prisma-postgres/index.d.ts +1476 -160
  64. package/dist/src/generated/prisma-postgres/index.js +15 -3
  65. package/dist/src/generated/prisma-postgres/package.json +1 -1
  66. package/dist/src/generated/prisma-postgres/schema.prisma +11 -0
  67. package/dist/src/generated/prisma-postgres/wasm.js +15 -3
  68. package/dist/src/mcp/organisms.d.ts +3 -0
  69. package/dist/src/mcp/organisms.d.ts.map +1 -1
  70. package/dist/src/mcp/organisms.js +18 -0
  71. package/dist/src/mcp/organisms.js.map +1 -1
  72. package/dist/src/middleware/subdomain.d.ts +28 -0
  73. package/dist/src/middleware/subdomain.d.ts.map +1 -0
  74. package/dist/src/middleware/subdomain.js +32 -0
  75. package/dist/src/middleware/subdomain.js.map +1 -0
  76. package/dist/src/models/offer-schemas.d.ts +229 -0
  77. package/dist/src/models/offer-schemas.d.ts.map +1 -0
  78. package/dist/src/models/offer-schemas.js +100 -0
  79. package/dist/src/models/offer-schemas.js.map +1 -0
  80. package/dist/src/routes/agent-tasks.d.ts.map +1 -1
  81. package/dist/src/routes/agent-tasks.js +33 -0
  82. package/dist/src/routes/agent-tasks.js.map +1 -1
  83. package/dist/src/routes/agents.d.ts.map +1 -1
  84. package/dist/src/routes/agents.js +146 -1
  85. package/dist/src/routes/agents.js.map +1 -1
  86. package/dist/src/routes/apps-backup.d.ts +20 -0
  87. package/dist/src/routes/apps-backup.d.ts.map +1 -0
  88. package/dist/src/routes/apps-backup.js +165 -0
  89. package/dist/src/routes/apps-backup.js.map +1 -0
  90. package/dist/src/routes/ghii.d.ts.map +1 -1
  91. package/dist/src/routes/ghii.js +4 -0
  92. package/dist/src/routes/ghii.js.map +1 -1
  93. package/dist/src/routes/instances.d.ts +2 -1
  94. package/dist/src/routes/instances.d.ts.map +1 -1
  95. package/dist/src/routes/instances.js +59 -1
  96. package/dist/src/routes/instances.js.map +1 -1
  97. package/dist/src/routes/organisms.d.ts.map +1 -1
  98. package/dist/src/routes/organisms.js +27 -5
  99. package/dist/src/routes/organisms.js.map +1 -1
  100. package/dist/src/routes/packages.d.ts.map +1 -1
  101. package/dist/src/routes/packages.js +32 -0
  102. package/dist/src/routes/packages.js.map +1 -1
  103. package/dist/src/routes/permissions.d.ts +12 -0
  104. package/dist/src/routes/permissions.d.ts.map +1 -1
  105. package/dist/src/routes/permissions.js +35 -3
  106. package/dist/src/routes/permissions.js.map +1 -1
  107. package/dist/src/routes/portal.d.ts +3 -0
  108. package/dist/src/routes/portal.d.ts.map +1 -1
  109. package/dist/src/routes/portal.js +12 -0
  110. package/dist/src/routes/portal.js.map +1 -1
  111. package/dist/src/routes/public-stats.d.ts +15 -0
  112. package/dist/src/routes/public-stats.d.ts.map +1 -0
  113. package/dist/src/routes/public-stats.js +90 -0
  114. package/dist/src/routes/public-stats.js.map +1 -0
  115. package/dist/src/routes/sse.d.ts +7 -2
  116. package/dist/src/routes/sse.d.ts.map +1 -1
  117. package/dist/src/routes/sse.js +35 -5
  118. package/dist/src/routes/sse.js.map +1 -1
  119. package/dist/src/routes/subdomains.d.ts +33 -0
  120. package/dist/src/routes/subdomains.d.ts.map +1 -0
  121. package/dist/src/routes/subdomains.js +208 -0
  122. package/dist/src/routes/subdomains.js.map +1 -0
  123. package/dist/src/server-bootstrap/routes-loader.d.ts.map +1 -1
  124. package/dist/src/server-bootstrap/routes-loader.js +12 -1
  125. package/dist/src/server-bootstrap/routes-loader.js.map +1 -1
  126. package/dist/src/server.d.ts.map +1 -1
  127. package/dist/src/server.js +4 -0
  128. package/dist/src/server.js.map +1 -1
  129. package/dist/src/services/apps-backup-export.d.ts +22 -0
  130. package/dist/src/services/apps-backup-export.d.ts.map +1 -0
  131. package/dist/src/services/apps-backup-export.js +136 -0
  132. package/dist/src/services/apps-backup-export.js.map +1 -0
  133. package/dist/src/services/apps-backup-import.d.ts +120 -0
  134. package/dist/src/services/apps-backup-import.d.ts.map +1 -0
  135. package/dist/src/services/apps-backup-import.js +259 -0
  136. package/dist/src/services/apps-backup-import.js.map +1 -0
  137. package/dist/src/services/component-registrar.d.ts +17 -0
  138. package/dist/src/services/component-registrar.d.ts.map +1 -1
  139. package/dist/src/services/component-registrar.js +63 -5
  140. package/dist/src/services/component-registrar.js.map +1 -1
  141. package/dist/src/services/handbooks/appdev.d.ts +1 -1
  142. package/dist/src/services/handbooks/appdev.d.ts.map +1 -1
  143. package/dist/src/services/handbooks/appdev.js +5 -2
  144. package/dist/src/services/handbooks/appdev.js.map +1 -1
  145. package/dist/src/services/schema-validator.d.ts.map +1 -1
  146. package/dist/src/services/schema-validator.js +12 -0
  147. package/dist/src/services/schema-validator.js.map +1 -1
  148. package/dist/src/storage/interface.d.ts +11 -1
  149. package/dist/src/storage/interface.d.ts.map +1 -1
  150. package/dist/src/storage/interface.js.map +1 -1
  151. package/dist/src/storage/providers/mongodb/index.d.ts +10 -1
  152. package/dist/src/storage/providers/mongodb/index.d.ts.map +1 -1
  153. package/dist/src/storage/providers/mongodb/index.js +76 -0
  154. package/dist/src/storage/providers/mongodb/index.js.map +1 -1
  155. package/dist/src/storage/providers/sqlite/index.d.ts +8 -1
  156. package/dist/src/storage/providers/sqlite/index.d.ts.map +1 -1
  157. package/dist/src/storage/providers/sqlite/index.js +61 -0
  158. package/dist/src/storage/providers/sqlite/index.js.map +1 -1
  159. package/dist/src/storage/providers/sqlite/schema.d.ts.map +1 -1
  160. package/dist/src/storage/providers/sqlite/schema.js +11 -0
  161. package/dist/src/storage/providers/sqlite/schema.js.map +1 -1
  162. package/dist/src/storage/repositories/index.d.ts +1 -0
  163. package/dist/src/storage/repositories/index.d.ts.map +1 -1
  164. package/dist/src/storage/repositories/package.repository.d.ts +2 -0
  165. package/dist/src/storage/repositories/package.repository.d.ts.map +1 -1
  166. package/dist/src/storage/repositories/subdomain-site.repository.d.ts +19 -0
  167. package/dist/src/storage/repositories/subdomain-site.repository.d.ts.map +1 -0
  168. package/dist/src/storage/repositories/subdomain-site.repository.js +2 -0
  169. package/dist/src/storage/repositories/subdomain-site.repository.js.map +1 -0
  170. package/dist/static/app-catalog.html +1139 -28
  171. package/package.json +1 -1
  172. package/prisma/schema.postgres.prisma +11 -0
  173. package/prisma/schema.prisma +11 -0
@@ -4,6 +4,10 @@
4
4
  * wallet, memory, agents, extensions, packages, knowledge, generator, and
5
5
  * edit panels. Consumes theme.css tokens so it flips with dark/light mode.
6
6
  * @version-history
7
+ * v1.14.0 — 2026-06-10 — SchemaForm usability: .pj-field-hint (per-field hint),
8
+ * .pj-field-invalid (missing-required outline) + .pj-form-error message.
9
+ * v1.13.0 — 2026-06-10 — Workspace renew, phase 2: .pj-ws-head-actions, review
10
+ * .pj-ws-banner, .pj-ws-tab-add (+ reuses .pj-org-tabs underline tabs).
7
11
  * v1.12.0 — 2026-06-10 — Organisms list renew, phase 1: compact .pj-org-row list
8
12
  * (avatar, one-line meta, kebab menu, drag-over state), .pj-menu dropdown,
9
13
  * organism home page (.pj-org-home-head, underline .pj-org-tabs, breadcrumb).
@@ -170,9 +174,52 @@
170
174
  Unified 2026-06-02 (#6). (.pf .wallet-lifetime grid wrapper kept.) */
171
175
  .pf .wallet-balance-copy{display:inline-block;margin-left:.5rem;font-size:.7rem;padding:2px 8px;background:rgba(232,86,74,.15);border:1px solid rgba(232,86,74,.3);border-radius:4px;color:var(--text,#1A1A2E);cursor:pointer;vertical-align:middle}
172
176
  .pf .wallet-balance-copy:hover{background:rgba(232,86,74,.25)}
173
- .pf .mem-item{display:flex;justify-content:space-between;align-items:center;padding:.5rem 0;border-bottom:1px solid rgba(232,86,74,.08);font-size:.85rem;cursor:pointer;gap:.5rem}
174
- .pf .mem-item:hover{background:rgba(232,86,74,.05)}
175
- .pf .mem-key{font-family:monospace;color:var(--accent,#E8564A);font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
177
+ .pf .mem-item{display:flex;justify-content:space-between;align-items:center;padding:.5rem 0;border-bottom:1px solid var(--border-subtle,var(--border));font-size:.85rem;cursor:pointer;gap:.5rem}
178
+ .pf .mem-item:hover{background:var(--bg-surface,var(--bg-dim))}
179
+ /* Keys in the NEUTRAL text color — monospace already separates them; a list of all-red
180
+ keys read like an error log and burned out red's alarm meaning. */
181
+ .pf .mem-key{font-family:monospace;color:var(--text);font-weight:500;flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
182
+ /* ── Grouped key list (2026-06-10): collapsible groups by first key segment ── */
183
+ .pf .mem-group{margin-bottom:.35rem}
184
+ .pf .mem-group-header{display:flex;align-items:center;gap:.5rem;width:100%;background:none;border:none;border-bottom:1px solid var(--border);padding:.45rem .2rem;cursor:pointer;font:inherit;text-align:left}
185
+ .pf .mem-group-header:hover{background:var(--bg-surface,var(--bg-dim))}
186
+ .pf .mem-group-name{font-family:monospace;font-weight:700;color:var(--text);font-size:.85rem}
187
+ .pf .mem-group-count{font-size:.75rem;color:var(--text-dim)}
188
+ .pf .mem-group-sub{font-family:monospace;font-size:.7rem;color:var(--muted)}
189
+ .pf .mem-item--grouped{padding-left:1.4rem}
190
+ .pf .mem-detail-key{font-family:monospace;font-size:.72rem;color:var(--muted);margin-bottom:.4rem;word-break:break-all}
191
+ .pf .mem-detail-visrow{display:flex;align-items:center;gap:.5rem;margin-top:.5rem}
192
+ .pf .mem-row-check{flex:none;margin:0 .15rem 0 0;cursor:pointer}
193
+ .pf .mem-vis-pop{display:flex;flex-wrap:wrap;gap:.35rem;padding:.5rem .75rem;margin:.25rem 0 .25rem 1.4rem;background:var(--card);border:1px solid var(--border);border-radius:8px;box-shadow:var(--shadow-sm)}
194
+ .pf .mem-vis-opt{background:var(--control-bg,var(--card));border:1px solid var(--control-border,var(--border));border-radius:999px;padding:.2rem .65rem;font-size:.76rem;color:var(--text);cursor:pointer}
195
+ .pf .mem-vis-opt:hover{border-color:var(--accent)}
196
+ .pf .mem-vis-opt--current{border-color:var(--accent);font-weight:700}
197
+ .pf .mem-vis-opt--dim{color:var(--text-dim);border-style:dashed}
198
+ .pf .mem-bulkbar{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;padding:.45rem .75rem;margin-bottom:.5rem;background:var(--bg-surface,var(--bg-dim));border:1px solid var(--border);border-radius:8px}
199
+ .pf .mem-bulkbar-count{font-size:.82rem;font-weight:600;color:var(--text)}
200
+ .pf .mem-actions .mem-delete-btn{margin-left:auto}
201
+ .pf .mem-vis-select{font-size:.72rem;padding:2px 6px;min-width:auto;width:auto}
202
+ .pf .mem-edit-textarea{min-height:280px;resize:vertical;font-family:monospace;font-size:.8rem}
203
+ .pf .mem-edit-textarea--error{border-color:var(--danger-fg,#DC2626)}
204
+ .pf .mem-json-error{color:var(--danger-fg,#DC2626);font-size:.78rem;margin-top:.35rem}
205
+ .pf .tag-pill.tag-more{border-style:dashed;color:var(--text-dim)}
206
+ .pf .tag-pill.tag-removable{background:var(--control-bg,var(--card));color:var(--text);border:1px solid var(--control-border,var(--border))}
207
+ .pf .tag-pill.tag-removable .tag-x{color:var(--muted);margin-left:2px}
208
+ .pf .tag-pill.tag-removable:hover .tag-x{color:var(--text)}
209
+ /* ── Access tab polish (2026-06-10): neutral headers, compact empties, fed coupling ── */
210
+ .pf .card-h3.access-h3{color:var(--text)}
211
+ .pf .access-empty-row{display:flex;align-items:center;gap:.75rem;padding:.4rem 0;margin-bottom:1rem}
212
+ .pf .access-fed-allowall{padding:.4rem 0;border-bottom:1px solid var(--border-subtle,var(--border))}
213
+ .pf .access-fed-warn-caption{font-size:.78rem;color:var(--warn-fg,#B45309);margin:.25rem 0 .1rem 1.6rem}
214
+ .pf .access-fed-list--inactive{opacity:.45;pointer-events:none}
215
+ .pf .access-copy-val{display:inline-flex;align-items:center;gap:.25rem;min-width:0;overflow-wrap:anywhere}
216
+ /* ── Sidebar reorg (2026-06-10): collapsible groups + pin-on-hover ── */
217
+ .pf .pf-side-group-toggle{display:flex;align-items:center;gap:.35rem;width:100%;background:none;border:none;cursor:pointer;font:inherit;text-align:left;padding:0}
218
+ .pf .pf-side-group-toggle .pf-chevron{font-size:.55rem;color:var(--muted)}
219
+ .pf .pf-side-pin{display:none;margin-left:auto;font-size:.72rem;opacity:.55;cursor:pointer;filter:grayscale(1)}
220
+ .pf .pf-side-item:hover .pf-side-pin{display:inline}
221
+ .pf .pf-side-pin:hover{opacity:1}
222
+ .pf .pf-side-pin--on{display:inline;opacity:.9;filter:none}
176
223
  .pf .mem-time{font-size:.7rem;color:var(--text-dim,#6B7280);white-space:nowrap;flex-shrink:0}
177
224
  .pf .mem-sort-bar{display:flex;align-items:center;gap:.35rem}
178
225
  .pf .mem-sort-select{font-size:.75rem;padding:2px 6px;min-width:auto;width:auto}
@@ -463,6 +510,22 @@
463
510
  .pf .pkg-badge-published{background:var(--success-bg);color:var(--success-fg)}
464
511
  .pf .pkg-badge-draft{background:var(--bg-surface);color:var(--text-dim)}
465
512
  .pf .pkg-empty{color:var(--muted,#888);font-size:.85rem;text-align:center;padding:2rem 0}
513
+ /* Expanded instance card: per-component list with status dots and inline actions */
514
+ .pf .pkg-card-expanded{grid-column:span 2}
515
+ @media (max-width:720px){.pf .pkg-card-expanded{grid-column:span 1}}
516
+ .pf .pkg-badge-sub{font-weight:400;text-transform:none;letter-spacing:0;opacity:.85;margin-left:.25rem}
517
+ .pf .pkg-comp-list{list-style:none;padding:0;margin:.75rem 0 .5rem;border-top:1px solid var(--border,#E5E7EB)}
518
+ .pf .pkg-comp{display:grid;grid-template-columns:1.5rem 1fr auto auto auto;gap:.5rem;align-items:center;padding:.4rem 0;border-bottom:1px dashed var(--border,#E5E7EB);font-size:.8rem}
519
+ .pf .pkg-comp:last-child{border-bottom:0}
520
+ .pf .pkg-comp-icon{font-size:1.1rem;line-height:1}
521
+ .pf .pkg-comp-id{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.78rem;color:var(--text,#111);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
522
+ .pf .pkg-comp-type{font-size:.7rem;color:var(--text-dim,#6B7280);background:var(--bg-dim,#F3F4F6);padding:1px 6px;border-radius:4px;text-transform:lowercase}
523
+ .pf .pkg-comp-status{display:flex;align-items:center;gap:.25rem}
524
+ .pf .pkg-dot{display:inline-block;width:.55rem;height:.55rem;border-radius:50%;background:var(--muted,#9CA3AF)}
525
+ .pf .pkg-dot-active{background:#22c55e;box-shadow:0 0 0 2px rgba(34,197,94,.18)}
526
+ .pf .pkg-comp-customized{font-size:.85rem;color:var(--warn-fg,#b45309)}
527
+ .pf .pkg-comp-actions{display:flex;gap:.25rem;flex-wrap:wrap}
528
+ .pf .pkg-comp-actions .btn-xs,.pf .pkg-card-meta .btn-xs{font-size:.7rem;padding:2px 6px}
466
529
  .pf .pkg-stats{display:flex;gap:.5rem;align-items:center;font-size:.8rem;color:var(--muted,#888)}
467
530
  .pf .pkg-section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem}
468
531
  .pf .pkg-section-header h3{margin:0}
@@ -1977,7 +2040,8 @@
1977
2040
  border-right: 1px solid var(--border);
1978
2041
  }
1979
2042
  .pf-side-identity { display: flex; align-items: center; gap: 0.6rem; padding: 0.4rem 0.6rem 0.8rem; }
1980
- .pf-side-avatar { width: 34px; height: 34px; border-radius: var(--radius-full, 999px); flex: 0 0 auto; background: linear-gradient(135deg, var(--accent), var(--accent-deep, var(--accent))); }
2043
+ .pf-side-avatar { width: 34px; height: 34px; border-radius: var(--radius-full, 999px); flex: 0 0 auto; background: var(--card); border: 1.5px solid var(--border); overflow: hidden; }
2044
+ .pf-side-avatar svg { width: 100%; height: 100%; }
1981
2045
  .pf-side-id-name { font-weight: 700; font-size: 0.95rem; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1982
2046
 
1983
2047
  .pf-side-group { margin-top: 0.4rem; }
@@ -2035,9 +2099,6 @@
2035
2099
 
2036
2100
  /* Workspace breadcrumb — keeps the parent organism visible so you always know
2037
2101
  which organism this workspace belongs to. */
2038
- .pj-ws-crumbs { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.2rem; }
2039
- .pj-crumb-sep { color: var(--muted); font-size: 0.85rem; }
2040
- .pj-crumb-org { font-size: 0.85rem; font-weight: 600; color: var(--text); }
2041
2102
 
2042
2103
  /* Title row — workspace name + status badge sit on one line. */
2043
2104
  .pj-ws-titlerow { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
@@ -2069,6 +2130,11 @@
2069
2130
  /* A declared space whose data does not live in workspace records (backing 'tasks', or a legacy
2070
2131
  unsupported backing) — shown as a notice instead of silently dropped from the view. */
2071
2132
  .pj-space-notice { font-size: 0.88rem; color: var(--muted); padding: 0.2rem 0 0.3rem; }
2133
+ /* The viewer's own contract-advertising agents (tag 'workspace-contract'), surfaced in the
2134
+ workspace People panel so the user sees which of their agents can serve this workspace. */
2135
+ .pj-contract-agents { border: 1px solid var(--border); border-radius: 10px; padding: 0.5rem 0.75rem; margin-bottom: 0.6rem; background: var(--card); }
2136
+ /* The per-contract one-click adopt action inside a contract-agent chip. */
2137
+ .pj-adopt-btn { margin-left: 0.4rem; padding: 0.1rem 0.5rem; font-size: 0.78rem; }
2072
2138
  .pj-draft-form { margin: 0.4rem 0; }
2073
2139
  .pj-drafts { display: flex; flex-direction: column; gap: 0.3rem; margin-bottom: 0.3rem; }
2074
2140
  .pj-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.35rem 0; }
@@ -2106,6 +2172,11 @@
2106
2172
  .pj-field { display: flex; flex-direction: column; gap: 0.2rem; margin-bottom: 0.35rem; }
2107
2173
  .pj-field > span { font-size: 0.75rem; color: var(--muted); }
2108
2174
  .pj-field-inline { flex-direction: row; align-items: center; gap: 0.4rem; }
2175
+ /* SchemaForm: per-field hint text + missing-required highlight after a Save attempt. */
2176
+ .pj-field-hint { font-size: 0.72rem; color: var(--muted); margin-top: -0.1rem; }
2177
+ .pj-field-invalid > span:first-child { color: var(--danger); }
2178
+ .pj-field-invalid .input-field { border-color: var(--danger); }
2179
+ .pj-form-error { font-size: 0.82rem; color: var(--danger); padding: 0.3rem 0; }
2109
2180
  .pj-errors { border: 1px solid var(--danger); border-radius: 10px; padding: 0.6rem 0.75rem; margin: 0.5rem 0; background: var(--bg-dim); }
2110
2181
  .pj-errors-title { font-weight: 600; margin-bottom: 0.3rem; color: var(--danger); }
2111
2182
  .pj-error-line { font-size: 0.82rem; color: var(--text); padding: 0.1rem 0; }
@@ -2331,6 +2402,44 @@
2331
2402
  .md-img { max-width: 100%; height: auto; border-radius: 6px; }
2332
2403
  @media (max-width: 640px) { .pj-docspace { flex-direction: column; } .pj-doc-index { flex: none; width: 100%; } }
2333
2404
 
2405
+ /* ───────────────── Workspace Overview tab ─────────────────
2406
+ * The landing view: a "what happened here" strip + every space stacked on one vertical scroll.
2407
+ * Sections collapse as accordions on mobile; documents expand inline there (no popups). */
2408
+ .pj-ov { display: flex; flex-direction: column; gap: 0.6rem; }
2409
+ .pj-ov-strip { border: 1px solid var(--border); border-radius: 10px; padding: 0.5rem 0.6rem; background: var(--card); }
2410
+ .pj-ov-strip-title { font-weight: 600; font-size: 0.85rem; color: var(--muted); margin-bottom: 0.3rem; }
2411
+ .pj-ov-event { display: flex; align-items: center; gap: 0.45rem; width: 100%; padding: 0.2rem 0.25rem; border: 0; background: none; border-radius: 6px; cursor: pointer; text-align: left; font-size: 0.85rem; color: var(--text); }
2412
+ .pj-ov-event:hover { background: var(--bg-dim); }
2413
+ .pj-ov-event-who { font-weight: 600; flex-shrink: 0; }
2414
+ .pj-ov-event-act { color: var(--muted); flex-shrink: 0; }
2415
+ .pj-ov-event-what { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
2416
+ .pj-ov-event-time { margin-left: auto; color: var(--muted); font-size: 0.75rem; flex-shrink: 0; }
2417
+ .pj-ov-sec { border: 1px solid var(--border); border-radius: 10px; background: var(--card); }
2418
+ .pj-ov-sec-head { display: flex; align-items: center; gap: 0.4rem; padding: 0.45rem 0.6rem; cursor: pointer; }
2419
+ .pj-ov-chevron { color: var(--muted); font-size: 0.8rem; width: 1em; flex-shrink: 0; }
2420
+ .pj-ov-name { font-weight: 600; color: var(--text); font-size: 0.95rem; }
2421
+ .pj-ov-name-link { border: 0; background: none; cursor: pointer; padding: 0; text-align: left; }
2422
+ .pj-ov-spacer { flex: 1; }
2423
+ .pj-ov-items { display: flex; flex-direction: column; gap: 0.1rem; padding: 0 0.6rem 0.5rem 1.5rem; }
2424
+ .pj-ov-row { display: flex; align-items: center; gap: 0.5rem; border: 1px solid var(--border); border-radius: 10px; background: var(--card); padding: 0.35rem 0.6rem; }
2425
+ .pj-ov-row .pj-ov-name { flex-shrink: 0; }
2426
+ .pj-ov-item { display: flex; align-items: center; gap: 0.45rem; width: 100%; padding: 0.25rem 0.3rem; border: 0; background: none; border-radius: 6px; cursor: pointer; text-align: left; font-size: 0.88rem; color: var(--text); min-width: 0; }
2427
+ .pj-ov-item:hover { background: var(--bg-dim); }
2428
+ .pj-ov-item-title { flex-shrink: 0; max-width: 60%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2429
+ .pj-ov-preview { color: var(--muted); font-size: 0.8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
2430
+ .pj-ov-more { border: 0; background: none; color: var(--accent); cursor: pointer; text-align: left; padding: 0.25rem 0.3rem; font-size: 0.85rem; }
2431
+ .pj-ov-more:hover { text-decoration: underline; }
2432
+ .pj-ov-doc-inline { border-top: 1px solid var(--border); margin: 0.2rem 0 0.4rem; padding-top: 0.4rem; }
2433
+ /* Unseen-changes badge on workspace tabs — neutral gray pill (red is reserved for errors). */
2434
+ .pj-org-tab-unseen { background: var(--bg-dim); color: var(--muted); border: 1px solid var(--border); border-radius: 999px; font-size: 0.68rem; padding: 0 0.35rem; margin-left: 0.3rem; line-height: 1.3; }
2435
+ @media (max-width: 640px) {
2436
+ .pj-ov-event { flex-wrap: wrap; } /* "what/where" wraps to its own line — never hidden */
2437
+ .pj-ov-event-what { flex-basis: 100%; padding-left: calc(9px + 0.45rem); }
2438
+ .pj-ov-item { flex-wrap: wrap; }
2439
+ .pj-ov-item-title { max-width: none; flex-shrink: 1; white-space: normal; }
2440
+ .pj-ov-preview { flex-basis: 100%; }
2441
+ }
2442
+
2334
2443
  /* ───────────────── Organisms list renew (phase 1) ─────────────────
2335
2444
  * Compact list rows + kebab menu + organism home page. All colors via theme tokens. */
2336
2445
 
@@ -2382,3 +2491,103 @@
2382
2491
  .pj-org-tab.active { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }
2383
2492
  .pj-org-tab-count { color: var(--muted); font-size: 0.75rem; margin-left: 0.3rem; }
2384
2493
  .pj-org-boardrow { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
2494
+
2495
+ /* ── Workspace renew (phase 2): header actions, review banner, add-space tab ── */
2496
+ .pj-ws-head-actions { margin-left: auto; display: flex; gap: 0.4rem; align-items: center; }
2497
+ .pj-ws-banner { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; border: 1px solid var(--border); border-left: 3px solid var(--warn, var(--accent)); border-radius: 10px; background: var(--card); padding: 0.55rem 0.75rem; margin: 0.6rem 0 0.2rem; }
2498
+ .pj-ws-banner-text { flex: 1; min-width: 200px; font-size: 0.85rem; color: var(--text); }
2499
+ .pj-ws-banner-sub { display: block; font-size: 0.76rem; color: var(--muted); }
2500
+ .pj-ws-tab-add { font-weight: 700; padding: 0.45rem 0.45rem; }
2501
+
2502
+ /* ── Organism home tab contents (feedback round): tab headers, pills, agent picker, board preview ── */
2503
+ .pj-tabhead { display: flex; align-items: flex-start; gap: 0.6rem; margin-bottom: 0.6rem; }
2504
+ .pj-tabhead-desc { flex: 1; margin: 0.2rem 0 0; }
2505
+ .pj-tab-pill { display: inline-block; background: var(--warn-bg, var(--bg-dim)); color: var(--warn-fg, var(--text)); border: 1px solid var(--warn-border, var(--border)); border-radius: 999px; font-size: 0.7rem; padding: 0.05rem 0.5rem; margin-left: 0.35rem; white-space: nowrap; }
2506
+ .pj-org-main-static { cursor: default; }
2507
+ .pj-linklike { background: none; border: none; padding: 0.2rem 0; font-size: 0.78rem; color: var(--accent); cursor: pointer; text-align: left; }
2508
+ .pj-linklike:hover { text-decoration: underline; }
2509
+ .pj-attach { border: 1px solid var(--border); border-radius: 10px; background: var(--bg-dim); padding: 0.6rem 0.75rem; margin-bottom: 0.6rem; display: flex; flex-direction: column; gap: 0.4rem; }
2510
+ .pj-invite-row { margin-bottom: 0.6rem; }
2511
+ .pj-req-row { border-left: 3px solid var(--warn, var(--accent)); margin-bottom: 0.5rem; }
2512
+ .pj-orgsearch { margin: 0 0 0.6rem; }
2513
+ .pj-orgsearch-input { min-width: 220px; }
2514
+ .pj-board-post { border: 1px solid var(--border); border-radius: 10px; background: var(--card); padding: 0.5rem 0.7rem; margin-bottom: 0.5rem; }
2515
+ .pj-board-post-head { display: flex; align-items: baseline; gap: 0.5rem; margin-bottom: 0.2rem; }
2516
+ .pj-board-author { font-weight: 600; font-size: 0.82rem; color: var(--text); }
2517
+ .pj-board-time { font-size: 0.74rem; color: var(--muted); }
2518
+ .pj-board-body { font-size: 0.85rem; color: var(--text); white-space: pre-wrap; word-break: break-word; }
2519
+ .pj-board-composer { margin-top: 0.6rem; }
2520
+ .pj-board-input { flex: 1; min-width: 220px; }
2521
+
2522
+ /* ── Organism settings form (labels, groups, hints, tag input, separated danger zone) ── */
2523
+ .pj-meta-line { display: flex; flex-wrap: wrap; gap: 0.25rem 0; font-size: 0.78rem; color: var(--muted); margin-bottom: 0.8rem; align-items: center; }
2524
+ .pj-meta-line > span + span::before { content: '·'; margin: 0 0.45rem; color: var(--muted); }
2525
+ .pj-form-group { font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); margin: 0.9rem 0 0.45rem; }
2526
+ .pj-form-row { display: flex; gap: 0.75rem; flex-wrap: wrap; }
2527
+ .pj-form-row > .pj-field { flex: 1; min-width: 160px; }
2528
+ .pj-form-hint { font-size: 0.76rem; color: var(--muted); margin: 0.1rem 0 0.5rem; }
2529
+ .pj-taginput { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; border: 1.5px solid var(--control-border); border-radius: var(--radius); background: var(--control-bg); padding: 0.3rem 0.45rem; }
2530
+ .pj-tag { display: inline-flex; align-items: center; gap: 0.25rem; }
2531
+ .pj-tag-x { background: none; border: none; cursor: pointer; color: var(--muted); font-size: 0.85rem; padding: 0 0.1rem; line-height: 1; }
2532
+ .pj-tag-x:hover { color: var(--danger); }
2533
+ .pj-taginput-field { flex: 1; min-width: 90px; border: none; background: transparent; color: var(--text); font-size: 0.82rem; outline: none; padding: 0.15rem 0; }
2534
+ .pj-danger-box { margin-top: 1.1rem; margin-bottom: 0.75rem; }
2535
+ .pj-danger-row { display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
2536
+ .pj-danger-text { flex: 1; min-width: 220px; }
2537
+ .pj-danger-sub { font-size: 0.78rem; color: var(--muted); }
2538
+ .pj-danger-confirm { margin-top: 0.6rem; display: flex; gap: 0.5rem; align-items: flex-end; flex-wrap: wrap; }
2539
+ .pj-members-cap { font-size: 0.74rem; color: var(--muted); }
2540
+
2541
+ /* ── Workspace row: clickable participants counter + inline people list ── */
2542
+ .pj-stat-btn { background: none; border: none; cursor: pointer; padding: 0.1rem 0.3rem; border-radius: 6px; font-size: 0.78rem; color: var(--muted); }
2543
+ .pj-stat-btn:hover { background: var(--bg-surface); color: var(--text); }
2544
+ .pj-stat-btn.active { background: var(--bg-dim); color: var(--text); }
2545
+ .pj-ws-person { display: flex; align-items: center; gap: 0.4rem; padding: 0.2rem 0; font-size: 0.83rem; color: var(--text); }
2546
+ .pj-ws-person-agents { font-size: 0.76rem; color: var(--muted); }
2547
+
2548
+ /* ── Settings polish: menu divider, bounded form card ── */
2549
+ .pj-menu-sep { height: 1px; background: var(--border); margin: 0.25rem 0.3rem; }
2550
+ .pj-form-card { border: 1px solid var(--border); border-radius: 10px; background: var(--card); padding: 0.75rem 0.9rem 0.6rem; margin-bottom: 0.25rem; }
2551
+ .pj-form-card .pj-form-group:first-child { margin-top: 0; }
2552
+
2553
+ /* ── Home dashboard: waiting-for-you box, continue/agents cards, clickable stats, promo dismiss ── */
2554
+ .pf-waiting { border: 1px solid var(--warn-border, var(--border)); border-left: 3px solid var(--warn, var(--accent)); border-radius: 12px; background: var(--card); padding: 0.8rem 1rem; margin: 1rem 0; }
2555
+ .pf-waiting-title { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--warn-fg, var(--text-dim)); margin-bottom: 0.5rem; }
2556
+ .pf-waiting-row { display: flex; align-items: center; gap: 0.75rem; padding: 0.35rem 0; }
2557
+ .pf-waiting-row + .pf-waiting-row { border-top: 1px solid var(--border-subtle, var(--border)); }
2558
+ .pf-waiting-text { flex: 1; font-size: 0.88rem; color: var(--text-dim); }
2559
+ .pf-waiting-text b { font-weight: 600; color: var(--text); }
2560
+ .pf-waiting-where { color: var(--muted); }
2561
+ .pf-home-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin: 1rem 0; }
2562
+ @media (max-width: 760px) { .pf-home-grid { grid-template-columns: 1fr; } }
2563
+ .pf-home-card { border: 1px solid var(--border); border-radius: 12px; background: var(--card); padding: 0.8rem 1rem; }
2564
+ .pf-home-card-title { font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 0.5rem; background: none; border: none; padding: 0; text-align: left; }
2565
+ .pf-home-card-link { cursor: pointer; }
2566
+ .pf-home-card-link:hover { color: var(--text); }
2567
+ .pf-home-card-note { font-weight: 500; text-transform: none; letter-spacing: 0; color: var(--muted); }
2568
+ .pf-home-row { display: flex; align-items: center; gap: 0.55rem; width: 100%; background: none; border: none; padding: 0.32rem 0.2rem; border-radius: 8px; cursor: pointer; font-size: 0.88rem; color: var(--text-dim); text-align: left; }
2569
+ .pf-home-row:hover { background: var(--bg-surface); color: var(--text); }
2570
+ .pf-home-row-static { cursor: default; }
2571
+ .pf-home-row-static:hover { background: none; color: var(--text-dim); }
2572
+ .pf-home-row-ico { flex: none; }
2573
+ .pf-home-row-label { flex: 1; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
2574
+ .pf-home-row-meta { flex: none; font-size: 0.78rem; color: var(--muted); }
2575
+ .pf-home-row-meta.pf-ok { color: var(--success-fg, var(--text)); }
2576
+ .pf-lp-stat-link { background: none; border: none; padding: 0.15rem 0.35rem; border-radius: 8px; cursor: pointer; font: inherit; color: inherit; }
2577
+ .pf-lp-stat-link:hover { background: var(--bg-surface); }
2578
+ .pf-lp-profile-btn { margin-left: auto; align-self: flex-start; }
2579
+ .pf-promo-dismiss { float: right; background: none; border: none; cursor: pointer; color: var(--muted); font-size: 0.85rem; padding: 0 0.2rem; }
2580
+ .pf-promo-dismiss:hover { color: var(--text); }
2581
+ /* Edit profile / password dialogs */
2582
+ .pf-avatar-row { display: flex; align-items: center; gap: 0.6rem; }
2583
+ .pf-avatar-preview { width: 42px; height: 42px; border-radius: 50%; border: 1px solid var(--border); background: var(--bg-dim); display: inline-flex; align-items: center; justify-content: center; font-size: 1.3rem; flex: none; }
2584
+ .pf-edit-link { font-size: 0.82rem; color: var(--accent); cursor: pointer; text-decoration: none; }
2585
+ .pf-edit-link:hover { text-decoration: underline; }
2586
+ .pf-edit-footer-left { margin-right: auto; }
2587
+ .pf-pw-wrap { position: relative; }
2588
+ .pf-pw-wrap .pf-edit-input { padding-right: 4.2rem; }
2589
+ .pf-pw-eye { position: absolute; right: 0.5rem; top: 50%; transform: translateY(-50%); background: none; border: none; cursor: pointer; color: var(--muted); font-size: 0.76rem; padding: 0.15rem 0.3rem; }
2590
+ .pf-pw-eye:hover { color: var(--text); }
2591
+ .pf-pw-rules { list-style: none; margin: 0.1rem 0 0.5rem; padding: 0; font-size: 0.78rem; color: var(--muted); }
2592
+ .pf-pw-rules li { padding: 0.08rem 0; }
2593
+ .pf-pw-rules li.ok { color: var(--success-fg, var(--text)); }
@@ -0,0 +1,96 @@
1
+ /**
2
+ * @file start.css
3
+ * @description Styles for the /v1/start diagnosis page (st- prefix): hero,
4
+ * question cards with big option buttons, the answer trail, the verdict card,
5
+ * the playbook checklist (code blocks, prompt block, doors) and print rules
6
+ * so "Print / save as PDF" emits just the playbook. Theme tokens only.
7
+ * @version-history
8
+ * v1.0.0 — 2026-06-11 — Initial: jira entrance styles + print rules.
9
+ * v1.0.1 — 2026-06-12 — Scope print rules to body.st-active: view CSS is global (spa.html
10
+ * preload), so the unscoped `body * { visibility: hidden }` blanked printing on every view
11
+ * (e.g. the public workspace viewer printed 14 empty pages).
12
+ */
13
+
14
+ .st { max-width: 760px; margin: 0 auto; padding: 2rem 1.25rem 3rem; color: var(--text); }
15
+
16
+ /* ── Hero (intro phase) ── */
17
+ .st-hero { text-align: center; padding: 3rem 0 1.5rem; }
18
+ .st-h1 { font-size: 2.2rem; font-weight: 800; letter-spacing: -0.5px; margin: 0 0 .7rem; }
19
+ .st-hero-sub { color: var(--text-dim); font-size: 1.08rem; margin: 0 auto 1.6rem; max-width: 560px; }
20
+ .st-start-btn { font-size: 1.05rem; padding: .75rem 2rem; }
21
+ .st-hero-meta { margin-top: 1rem; font-size: .85rem; color: var(--muted); }
22
+
23
+ /* ── Compact header + trail (after intro) ── */
24
+ .st-header { margin-bottom: 1.5rem; }
25
+ .st-header-title { font-size: 1rem; font-weight: 700; color: var(--text-dim); margin-bottom: .6rem; }
26
+ .st-trail { display: flex; flex-wrap: wrap; gap: .4rem; }
27
+ .st-trail-chip { font-size: .78rem; padding: .25rem .6rem; border: 1px solid var(--border); border-radius: 999px; background: var(--card); color: var(--text-dim); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
28
+
29
+ /* ── Question card ── */
30
+ .st-q { padding: .5rem 0; }
31
+ .st-q-title { font-size: 1.45rem; font-weight: 800; margin: 0 0 1.2rem; }
32
+ .st-opts { display: flex; flex-direction: column; gap: .7rem; }
33
+ .st-opt { text-align: left; font-size: 1rem; padding: .9rem 1.1rem; border: 1px solid var(--border); border-radius: 12px; background: var(--card); color: var(--text); cursor: pointer; transition: border-color .15s, transform .05s; font-family: inherit; }
34
+ .st-opt:hover { border-color: var(--accent); }
35
+ .st-opt:active { transform: scale(.99); }
36
+ .st-back { margin-top: 1.2rem; background: none; border: none; color: var(--text-dim); cursor: pointer; font-size: .88rem; padding: .3rem 0; font-family: inherit; }
37
+ .st-back:hover { color: var(--text); text-decoration: underline; }
38
+
39
+ /* ── Verdict ── */
40
+ .st-verdict { text-align: center; padding: .5rem 0; }
41
+ .st-verdict-label { font-size: .85rem; text-transform: uppercase; letter-spacing: 2px; color: var(--muted); margin-bottom: .7rem; }
42
+ .st-verdict-card { border: 2px solid var(--accent); border-radius: 14px; background: var(--card); padding: 1.6rem 1.5rem; margin-bottom: 1.2rem; }
43
+ .st-verdict-name { font-size: 1.7rem; font-weight: 800; margin-bottom: .6rem; }
44
+ .st-verdict-desc { color: var(--text-dim); font-size: 1.02rem; margin: 0 auto .8rem; max-width: 540px; }
45
+ .st-verdict-score { font-size: .85rem; color: var(--muted); }
46
+ .st-verdict-actions { display: flex; gap: .8rem; justify-content: center; flex-wrap: wrap; margin-bottom: .9rem; }
47
+ .st-verdict-note { color: var(--text-dim); font-size: .92rem; margin: 0; }
48
+
49
+ /* ── Playbook ── */
50
+ .st-pb-head { margin-bottom: 1.5rem; }
51
+ .st-pb-title { font-size: 1.7rem; font-weight: 800; margin: 0 0 .4rem; }
52
+ .st-pb-sub { color: var(--text-dim); margin: 0 0 .5rem; }
53
+ .st-pb-tier { font-size: .9rem; font-weight: 700; color: var(--accent); margin: 0; }
54
+ .st-pb-section { margin-bottom: 1.4rem; border: 1px solid var(--border); border-radius: 12px; background: var(--card); padding: 1rem 1.2rem; }
55
+ .st-pb-section h3 { margin: 0 0 .7rem; font-size: 1.05rem; }
56
+ .st-pb-items { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .6rem; }
57
+ .st-pb-item { font-size: .95rem; }
58
+ .st-pb-check { display: flex; gap: .55rem; align-items: flex-start; cursor: pointer; }
59
+ .st-pb-check input { margin-top: .25rem; accent-color: var(--accent); flex: none; }
60
+ .st-pb-check input:checked + span { color: var(--text-dim); text-decoration: line-through; }
61
+ .st-pb-quote, .st-pb-prompt blockquote { margin: .3rem 0; padding: .6rem .9rem; border-left: 3px solid var(--accent); background: var(--bg-surface, var(--bg-dim)); border-radius: 6px; font-size: .92rem; }
62
+ .st-pb-code { display: flex; gap: .6rem; align-items: flex-start; margin-top: .4rem; }
63
+ .st-pb-code pre { flex: 1; margin: 0; padding: .65rem .9rem; border: 1px solid var(--border); border-radius: 8px; background: var(--bg-surface, var(--bg-dim)); font-size: .82rem; overflow-x: auto; white-space: pre-wrap; word-break: break-all; }
64
+ .st-pb-copy { flex: none; }
65
+ .st-pb-prompt { margin-top: .4rem; }
66
+ .st-pb-prompt .btn-primary { margin-top: .5rem; }
67
+ .st-pb-link { color: var(--accent); font-weight: 700; text-decoration: none; font-size: .92rem; }
68
+ .st-pb-link:hover { text-decoration: underline; }
69
+ .st-pb-actions { display: flex; gap: .8rem; flex-wrap: wrap; align-items: center; margin: 1.4rem 0; }
70
+
71
+ /* ── Doors ── */
72
+ .st-doors { margin-top: 2rem; padding-top: 1.4rem; border-top: 1px solid var(--border); }
73
+ .st-doors-title { font-size: 1.15rem; margin: 0 0 .3rem; }
74
+ .st-doors-sub { color: var(--text-dim); font-size: .9rem; margin: 0 0 1rem; }
75
+ .st-doors-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: .9rem; }
76
+ @media (max-width: 700px) { .st-doors-grid { grid-template-columns: 1fr; } }
77
+ .st-door { display: flex; flex-direction: column; gap: .3rem; border: 1px solid var(--border); border-radius: 12px; background: var(--card); padding: 1rem 1.1rem; text-decoration: none; color: var(--text); }
78
+ .st-door:hover { border-color: var(--accent); }
79
+ .st-door-name { font-weight: 700; }
80
+ .st-door-desc { font-size: .85rem; color: var(--text-dim); }
81
+
82
+ /* ── Footer ── */
83
+ .st-footer { margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--border); text-align: center; font-size: .85rem; color: var(--text-dim); }
84
+ .st-footer a { color: var(--text-dim); }
85
+ .st-footer a:hover { color: var(--text); }
86
+
87
+ /* ── Print: emit only the playbook ──
88
+ * Scoped to body.st-active (added by start.js while the view is mounted): view CSS is preloaded
89
+ * globally in spa.html, so an unscoped `body *` rule here would blank printing on every view. */
90
+ @media print {
91
+ body.st-active * { visibility: hidden; }
92
+ body.st-active .st-print-area, body.st-active .st-print-area * { visibility: visible; }
93
+ body.st-active .st-print-area { position: absolute; top: 0; left: 0; width: 100%; }
94
+ body.st-active .st-no-print, body.st-active .st-no-print * { display: none !important; }
95
+ body.st-active .st-pb-section { break-inside: avoid; }
96
+ }
Binary file
@@ -1,31 +1,46 @@
1
- import { h } from 'preact';
2
- import htm from 'htm';
3
- const html = htm.bind(h);
4
- import { t } from '/js/i18n.js';
5
-
6
- /**
7
- * TagCloud — clickable tag filter pills.
8
- * @param {Object} props
9
- * @param {string[]} props.tags — all available tags (will be sorted)
10
- * @param {Set<string>} props.selected — currently active filter tags
11
- * @param {(tag: string) => void} props.onToggle — called when a tag is clicked
12
- * @param {() => void} props.onClear — called when clear button is clicked
13
- */
14
- export default function TagCloud({ tags, selected, onToggle, onClear }) {
15
- if (!tags || tags.length === 0) return null;
16
- const sorted = [...tags].sort();
17
- return html`
18
- <div class="tag-cloud">
19
- ${sorted.map(tag => html`
20
- <button key=${tag}
21
- class="tag-pill ${selected.has(tag) ? 'active' : ''}"
22
- onClick=${() => onToggle(tag)}>
23
- ${tag}
24
- </button>
25
- `)}
26
- ${selected.size > 0 && html`
27
- <button class="tag-pill tag-clear" onClick=${onClear}>\u2715 ${t('tags.clear') || 'Clear'}</button>
28
- `}
29
- </div>
30
- `;
31
- }
1
+ import { h } from 'preact';
2
+ import { useState } from 'preact/hooks';
3
+ import htm from 'htm';
4
+ const html = htm.bind(h);
5
+ import { t } from '/js/i18n.js';
6
+
7
+ /**
8
+ * TagCloud — clickable tag filter pills.
9
+ * @param {Object} props
10
+ * @param {string[]} props.tags — available tags. Pre-sorted order is respected when
11
+ * `limit` is set (pass most-used first); otherwise sorted alphabetically.
12
+ * @param {Set<string>} props.selected — currently active filter tags
13
+ * @param {(tag: string) => void} props.onToggle — called when a tag is clicked
14
+ * @param {() => void} props.onClear — called when clear button is clicked
15
+ * @param {number} [props.limit] — show only the first N tags behind a "show all" toggle.
16
+ * Selected tags always stay visible so an active filter can't hide its own pill.
17
+ */
18
+ export default function TagCloud({ tags, selected, onToggle, onClear, limit }) {
19
+ const [showAll, setShowAll] = useState(false);
20
+ if (!tags || tags.length === 0) return null;
21
+ const ordered = limit ? [...tags] : [...tags].sort();
22
+ const capped = limit && !showAll && ordered.length > limit;
23
+ const visible = capped ? ordered.filter((tag, i) => i < limit || selected.has(tag)) : ordered;
24
+ return html`
25
+ <div class="tag-cloud">
26
+ ${visible.map(tag => html`
27
+ <button key=${tag}
28
+ class="tag-pill ${selected.has(tag) ? 'active' : ''}"
29
+ onClick=${() => onToggle(tag)}>
30
+ ${tag}
31
+ </button>
32
+ `)}
33
+ ${capped && html`
34
+ <button class="tag-pill tag-more" onClick=${() => setShowAll(true)}>
35
+ ${(t('profile.memory.showAllTags') || 'Show all ({n})').replace('{n}', String(ordered.length))}
36
+ </button>
37
+ `}
38
+ ${limit && showAll && ordered.length > limit && html`
39
+ <button class="tag-pill tag-more" onClick=${() => setShowAll(false)}>${t('profile.memory.fewerTags') || 'Show fewer'}</button>
40
+ `}
41
+ ${selected.size > 0 && html`
42
+ <button class="tag-pill tag-clear" onClick=${onClear}>✕ ${t('tags.clear') || 'Clear'}</button>
43
+ `}
44
+ </div>
45
+ `;
46
+ }
@@ -29,8 +29,8 @@ export default function TagEditor({ tags, onSave, maxTags = 20 }) {
29
29
  <div class="tag-editor">
30
30
  <div class="tag-editor-pills">
31
31
  ${tags.map(tag => html`
32
- <span class="tag-pill active" key=${tag} onClick=${() => removeTag(tag)}>
33
- ${tag} \u2715
32
+ <span class="tag-pill tag-removable" key=${tag} onClick=${() => removeTag(tag)}>
33
+ ${tag} <span class="tag-x">\u2715</span>
34
34
  </span>
35
35
  `)}
36
36
  </div>
@@ -0,0 +1,40 @@
1
+ /**
2
+ * @file recents.js
3
+ * @description "Continue" list backing store — the last opened things (workspaces, apps,
4
+ * organisms, …) across types, so the profile home can offer one-click returns with real
5
+ * display names. Device-local by design (localStorage): what you opened HERE.
6
+ * @structure
7
+ * - recordRecent(item) — upsert by type+id to the front, capped at 12
8
+ * - listRecents(n) — newest first
9
+ * @usage
10
+ * import { recordRecent, listRecents } from '/js/recents.js';
11
+ * recordRecent({ type: 'workspace', id: orgId + '/' + wsId, label: wsName, sub: orgName, data: { orgId, wsId } });
12
+ * @version-history
13
+ * v1.0.0 — 2026-06-10 — Initial: localStorage-backed cross-type recents for the home dashboard.
14
+ */
15
+
16
+ const KEY = 'aimeat.recents';
17
+ const CAP = 12;
18
+
19
+ function readAll() {
20
+ try {
21
+ const raw = localStorage.getItem(KEY);
22
+ const list = raw ? JSON.parse(raw) : [];
23
+ return Array.isArray(list) ? list : [];
24
+ } catch { return []; }
25
+ }
26
+
27
+ /** Record a visit. item: { type, id, label, sub?, data? } — type+id is the identity. */
28
+ export function recordRecent(item) {
29
+ if (!item || !item.type || !item.id || !item.label) return;
30
+ try {
31
+ const list = readAll().filter(x => !(x.type === item.type && x.id === item.id));
32
+ list.unshift({ type: item.type, id: item.id, label: item.label, sub: item.sub || '', data: item.data || {}, at: new Date().toISOString() });
33
+ localStorage.setItem(KEY, JSON.stringify(list.slice(0, CAP)));
34
+ } catch { /* recents are a convenience — never break the caller */ }
35
+ }
36
+
37
+ /** Newest-first recents, capped at n. */
38
+ export function listRecents(n = 5) {
39
+ return readAll().slice(0, n);
40
+ }
@@ -269,3 +269,9 @@ export const getAdminAgentTasks = (params = {}) => {
269
269
 
270
270
  // ── Sharing Groups (admin) ──
271
271
  export const getAdminSharingGroups = () => apiGet('/v1/admin/sharing-groups');
272
+
273
+ // ── Subdomain mappings (operator) ──
274
+ export const getSubdomainSites = () => apiGet('/v1/admin/subdomains');
275
+ export const createSubdomainSite = (body) => apiPost('/v1/admin/subdomains', body);
276
+ export const updateSubdomainSite = (sub, updates) => apiPatch(`/v1/admin/subdomains/${encodeURIComponent(sub)}`, updates);
277
+ export const deleteSubdomainSite = (sub) => apiDelete(`/v1/admin/subdomains/${encodeURIComponent(sub)}`);
@@ -11,6 +11,40 @@ export async function listAgents(owner) {
11
11
  return owner ? list.filter(a => a.owner === owner) : list;
12
12
  }
13
13
 
14
+ /** Workspace-contract advertising via owner-managed agent tags (colon-free — the server tag
15
+ * pattern allows only [a-z0-9._-]): the tag 'workspace-contract' marks an agent as a contract
16
+ * provider, and 'contract.<name>' tags name the contract(s) it serves. Convention documented in
17
+ * docs/agent-workspace-contracts.md; agents set tags via aimeat_agent_tags_set / PATCH tags. */
18
+ export const offersWorkspaceContract = (a) => (a?.tags || []).includes('workspace-contract');
19
+
20
+ /** The contract names an agent advertises ('contract.<name>' tags, prefix stripped). */
21
+ export const contractNamesOf = (a) => (a?.tags || [])
22
+ .filter(tg => String(tg).startsWith('contract.'))
23
+ .map(tg => String(tg).slice('contract.'.length));
24
+
25
+ /** One-click contract adoption: queue the agreed adopt-contract TASK for one of the owner's
26
+ * agents. The agent recognises it from scope[kind]='adopt-contract' (NOT the title), joins the
27
+ * organism if needed, provisions its contract's spaces itself (workspace_update add_spaces from
28
+ * its OWN contract declaration — §2 of the guide), and completes the task with what was added.
29
+ * Convention: docs/agent-workspace-contracts.md §7c — agreed with the crewaimeat side 2026-06-10. */
30
+ export async function adoptContractTask(agentName, { organismId, ws, contract }) {
31
+ const { createTask } = await import('/js/services/agent-tasks.js');
32
+ return createTask(agentName, {
33
+ title: `adopt-contract: ${contract || 'workspace-contract'} → ${ws}`,
34
+ description: `Adopt your '${contract || ''}' contract into workspace ${ws} of organism ${organismId}: join the organism if needed, provision the contract's spaces (workspace_update add_spaces from your own contract declaration), then complete this task with what was added.`,
35
+ // 'queued' is REQUIRED: without it the task is created as a draft and never auto-activates.
36
+ // A task-runner-mode agent flips queued → active immediately (push over the tunnel); other
37
+ // modes keep the standard queued → owner-start gate. (crewaimeat live-test finding 2026-06-10.)
38
+ status: 'queued',
39
+ scope: [
40
+ { name: 'kind', value: 'adopt-contract', type: 'text' },
41
+ { name: 'organism_id', value: organismId, type: 'text' },
42
+ { name: 'ws', value: ws, type: 'text' },
43
+ ...(contract ? [{ name: 'contract', value: contract, type: 'text' }] : []),
44
+ ],
45
+ });
46
+ }
47
+
14
48
  /** List chat session agents (name starts with 'session-'). */
15
49
  export async function listChatSessions(owner) {
16
50
  const agents = await listAgents(owner);