@k2b/cloud 0.6.0-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (540) hide show
  1. package/README.md +98 -0
  2. package/package.json +128 -0
  3. package/public/logo.svg +1 -0
  4. package/scripts/app-favicon.test.ts +49 -0
  5. package/scripts/app-favicon.ts +51 -0
  6. package/scripts/build.ts +205 -0
  7. package/scripts/preload.ts +164 -0
  8. package/scripts/sync-recovery-smoke.ts +200 -0
  9. package/src/_internal/app-favicon.ts +2 -0
  10. package/src/_internal/app-presentation.ts +61 -0
  11. package/src/_internal/app-workspace-state.ts +24 -0
  12. package/src/_internal/bounded-json.ts +47 -0
  13. package/src/_internal/build-metadata.ts +9 -0
  14. package/src/_internal/capabilities.ts +1118 -0
  15. package/src/_internal/define-app.ts +801 -0
  16. package/src/_internal/heartbeat.ts +167 -0
  17. package/src/_internal/help-catalog.ts +241 -0
  18. package/src/_internal/help.ts +95 -0
  19. package/src/_internal/nats-connection.ts +34 -0
  20. package/src/_internal/page-responses.ts +40 -0
  21. package/src/_internal/process-sync.ts +117 -0
  22. package/src/_internal/readiness.ts +3 -0
  23. package/src/_internal/registry-validation.ts +136 -0
  24. package/src/_internal/registry-watch.ts +52 -0
  25. package/src/_internal/registry.ts +338 -0
  26. package/src/_internal/runtime-compatibility.ts +64 -0
  27. package/src/_internal/runtime-context.ts +84 -0
  28. package/src/_internal/runtime-watcher.ts +78 -0
  29. package/src/_internal/static-assets.ts +86 -0
  30. package/src/_internal/status-preserving-ssr.ts +49 -0
  31. package/src/access/PermissionEditor.tsx +486 -0
  32. package/src/access/ResourceApiKeys.tsx +271 -0
  33. package/src/access/messages.ts +121 -0
  34. package/src/access/ui.ts +16 -0
  35. package/src/account/Avatar.tsx +28 -0
  36. package/src/account/EntitySearch.tsx +363 -0
  37. package/src/account/avatar-upload-dialog.tsx +197 -0
  38. package/src/account/avatar-upload.ts +149 -0
  39. package/src/account/ui.ts +3 -0
  40. package/src/ai/admin.ts +47 -0
  41. package/src/ai/approval-routes.ts +130 -0
  42. package/src/ai/approvals.ts +139 -0
  43. package/src/ai/assistant-models.ts +61 -0
  44. package/src/ai/attachments.ts +40 -0
  45. package/src/ai/browser.ts +92 -0
  46. package/src/ai/capabilities.ts +714 -0
  47. package/src/ai/capability-execution.ts +151 -0
  48. package/src/ai/chat/blocks.render.test.tsx +1108 -0
  49. package/src/ai/chat/blocks.tsx +730 -0
  50. package/src/ai/chat/builtin-tools.tsx +428 -0
  51. package/src/ai/chat/capability-table.tsx +87 -0
  52. package/src/ai/chat/composer-adapter.ts +169 -0
  53. package/src/ai/chat/file-tools.tsx +81 -0
  54. package/src/ai/chat/message-actions.tsx +370 -0
  55. package/src/ai/chat/message-utils.ts +479 -0
  56. package/src/ai/chat/messages.ts +43 -0
  57. package/src/ai/chat/presentation.tsx +395 -0
  58. package/src/ai/chat/primitives.tsx +3 -0
  59. package/src/ai/chat/tool-disclosure.tsx +41 -0
  60. package/src/ai/chat/turn-navigator-utils.ts +28 -0
  61. package/src/ai/chat/turn-navigator.tsx +164 -0
  62. package/src/ai/chat/user-message.tsx +149 -0
  63. package/src/ai/chat/visual-tools.tsx +435 -0
  64. package/src/ai/chat/web-tools.tsx +202 -0
  65. package/src/ai/chat-task-contracts.ts +113 -0
  66. package/src/ai/chat-tasks.ts +952 -0
  67. package/src/ai/client/controller.ts +1211 -0
  68. package/src/ai/client/file-source.ts +95 -0
  69. package/src/ai/client/live-connection.ts +140 -0
  70. package/src/ai/client/projection.ts +178 -0
  71. package/src/ai/client/transport.ts +111 -0
  72. package/src/ai/compaction.ts +182 -0
  73. package/src/ai/credentials.ts +124 -0
  74. package/src/ai/default-tool-contracts.ts +108 -0
  75. package/src/ai/default-tools.ts +127 -0
  76. package/src/ai/enrich.ts +280 -0
  77. package/src/ai/executor.ts +1624 -0
  78. package/src/ai/fetch-file-tool.ts +313 -0
  79. package/src/ai/file-context.ts +92 -0
  80. package/src/ai/file-media-type.ts +28 -0
  81. package/src/ai/file-mount.ts +16 -0
  82. package/src/ai/file-tools.ts +566 -0
  83. package/src/ai/files-store.ts +390 -0
  84. package/src/ai/firecrawl-tools.ts +305 -0
  85. package/src/ai/grids-skill.ts +126 -0
  86. package/src/ai/http.ts +265 -0
  87. package/src/ai/index.ts +337 -0
  88. package/src/ai/kit-skill.ts +22 -0
  89. package/src/ai/limits.ts +5 -0
  90. package/src/ai/live-events.ts +289 -0
  91. package/src/ai/live-messages.ts +45 -0
  92. package/src/ai/live-outbox.ts +102 -0
  93. package/src/ai/live-routes.ts +412 -0
  94. package/src/ai/live.ts +2 -0
  95. package/src/ai/maintenance.ts +302 -0
  96. package/src/ai/markdown-pdf-tool.ts +95 -0
  97. package/src/ai/memories.ts +605 -0
  98. package/src/ai/memory-learning-runs.ts +222 -0
  99. package/src/ai/memory-learning.ts +619 -0
  100. package/src/ai/memory-tool.ts +120 -0
  101. package/src/ai/memory-workflow-evidence.ts +158 -0
  102. package/src/ai/migrate.ts +2082 -0
  103. package/src/ai/model-access-migrate.ts +30 -0
  104. package/src/ai/model-access.ts +191 -0
  105. package/src/ai/personal-agent.ts +6 -0
  106. package/src/ai/prefs.ts +72 -0
  107. package/src/ai/project-tool.ts +94 -0
  108. package/src/ai/projects-routes.ts +277 -0
  109. package/src/ai/projects.ts +902 -0
  110. package/src/ai/protocol.ts +279 -0
  111. package/src/ai/provider.ts +73 -0
  112. package/src/ai/public-projection.ts +25 -0
  113. package/src/ai/resource-markers.ts +31 -0
  114. package/src/ai/resource-refs.ts +70 -0
  115. package/src/ai/routes.ts +1131 -0
  116. package/src/ai/runtime-tools.ts +39 -0
  117. package/src/ai/runtime.ts +544 -0
  118. package/src/ai/settings.ts +529 -0
  119. package/src/ai/short-id.ts +45 -0
  120. package/src/ai/skill-catalog.ts +73 -0
  121. package/src/ai/skill-format.ts +145 -0
  122. package/src/ai/skill-seeds.ts +430 -0
  123. package/src/ai/skill-tool.ts +81 -0
  124. package/src/ai/skills-routes.ts +143 -0
  125. package/src/ai/skills.ts +934 -0
  126. package/src/ai/solid.ts +27 -0
  127. package/src/ai/store.ts +3113 -0
  128. package/src/ai/stream.ts +308 -0
  129. package/src/ai/structured-runs.ts +66 -0
  130. package/src/ai/structured.ts +186 -0
  131. package/src/ai/system-prompt.ts +170 -0
  132. package/src/ai/task-prompt.ts +22 -0
  133. package/src/ai/timeline.ts +148 -0
  134. package/src/ai/tool-audit.ts +177 -0
  135. package/src/ai/tool-result-budget.ts +16 -0
  136. package/src/ai/tools.ts +155 -0
  137. package/src/ai/types.ts +967 -0
  138. package/src/ai/ui.tsx +39 -0
  139. package/src/ai/usage-migrate.ts +81 -0
  140. package/src/ai/usage.ts +315 -0
  141. package/src/ai/validate.ts +54 -0
  142. package/src/ai/vision-tool.ts +79 -0
  143. package/src/api/accounts-entities.ts +174 -0
  144. package/src/api/admin-ai-projects.ts +74 -0
  145. package/src/api/admin-ai-skills.ts +76 -0
  146. package/src/api/admin-ai-usage.ts +42 -0
  147. package/src/api/admin-core-settings.ts +508 -0
  148. package/src/api/admin-identity.ts +148 -0
  149. package/src/api/admin-lifecycle.ts +206 -0
  150. package/src/api/admin-linux-identities.ts +66 -0
  151. package/src/api/announcements.ts +131 -0
  152. package/src/api/app-approval.ts +129 -0
  153. package/src/api/apps.ts +51 -0
  154. package/src/api/auth/schemas.ts +54 -0
  155. package/src/api/auth.ts +370 -0
  156. package/src/api/capabilities.ts +658 -0
  157. package/src/api/help-messages.ts +19 -0
  158. package/src/api/help.ts +78 -0
  159. package/src/api/index.ts +108 -0
  160. package/src/api/mcp.ts +744 -0
  161. package/src/api/me-rail.ts +45 -0
  162. package/src/api/me.ts +753 -0
  163. package/src/api/search/schemas.ts +79 -0
  164. package/src/api/search.bench.ts +185 -0
  165. package/src/api/search.ts +407 -0
  166. package/src/api/sync-ops.ts +79 -0
  167. package/src/api/widgets.ts +143 -0
  168. package/src/browser/CloudResourceSearch.behavior.test.tsx +74 -0
  169. package/src/browser/CloudResourceSearch.tsx +791 -0
  170. package/src/browser/app-approval-vault.ts +305 -0
  171. package/src/browser/app-approval.ts +234 -0
  172. package/src/browser/live-websocket.ts +268 -0
  173. package/src/browser/locale-preference.ts +25 -0
  174. package/src/browser/notification-target.ts +27 -0
  175. package/src/browser/notification-timeout.ts +14 -0
  176. package/src/browser/notifications.ts +181 -0
  177. package/src/browser/resource-clipboard.ts +70 -0
  178. package/src/browser/resource-picker.tsx +39 -0
  179. package/src/browser/resource-search-messages.ts +55 -0
  180. package/src/browser/resource-search.ts +27 -0
  181. package/src/capabilities/client.ts +149 -0
  182. package/src/capabilities/response.ts +39 -0
  183. package/src/capabilities/server.ts +215 -0
  184. package/src/capabilities/signals.ts +4 -0
  185. package/src/capabilities/testing.ts +12 -0
  186. package/src/capabilities/types.ts +32 -0
  187. package/src/cli/access.ts +499 -0
  188. package/src/cli/account.ts +382 -0
  189. package/src/cli/admin/account-administration.ts +45 -0
  190. package/src/cli/admin/account-categories.ts +49 -0
  191. package/src/cli/admin/ai-usage.ts +118 -0
  192. package/src/cli/admin/app-sign-in.ts +49 -0
  193. package/src/cli/admin/data.ts +343 -0
  194. package/src/cli/admin/documentation.ts +34 -0
  195. package/src/cli/admin/gateway.ts +155 -0
  196. package/src/cli/admin/index.ts +61 -0
  197. package/src/cli/admin/instance.ts +250 -0
  198. package/src/cli/admin/jobs.ts +223 -0
  199. package/src/cli/admin/legal.ts +122 -0
  200. package/src/cli/admin/linux.ts +53 -0
  201. package/src/cli/admin/logs.ts +324 -0
  202. package/src/cli/admin/metrics.ts +142 -0
  203. package/src/cli/admin/nats.ts +167 -0
  204. package/src/cli/admin/notifications.ts +516 -0
  205. package/src/cli/admin/shared.ts +113 -0
  206. package/src/cli/admin/sync.ts +188 -0
  207. package/src/cli/admin/telemetry.ts +314 -0
  208. package/src/cli/admin/webhooks.ts +222 -0
  209. package/src/cli/admin/workflows.ts +367 -0
  210. package/src/cli/apps.ts +40 -0
  211. package/src/cli/capabilities.ts +239 -0
  212. package/src/cli/commands.ts +604 -0
  213. package/src/cli/index.ts +61 -0
  214. package/src/cli/locale.ts +31 -0
  215. package/src/cli/output.ts +58 -0
  216. package/src/clients/core.ts +20 -0
  217. package/src/config/env.ts +54 -0
  218. package/src/config/index.ts +6 -0
  219. package/src/contracts/account-categories.ts +62 -0
  220. package/src/contracts/announcements.ts +121 -0
  221. package/src/contracts/app-approval.ts +197 -0
  222. package/src/contracts/app.ts +162 -0
  223. package/src/contracts/capabilities.ts +484 -0
  224. package/src/contracts/index.ts +14 -0
  225. package/src/contracts/notification-live.ts +66 -0
  226. package/src/contracts/notification-types.ts +235 -0
  227. package/src/contracts/posix.ts +69 -0
  228. package/src/contracts/profile.ts +85 -0
  229. package/src/contracts/rail-preferences.ts +53 -0
  230. package/src/contracts/registry.ts +110 -0
  231. package/src/contracts/resource-clipboard.ts +33 -0
  232. package/src/contracts/settings-types.ts +98 -0
  233. package/src/contracts/shared.ts +408 -0
  234. package/src/contracts/user-notifications.ts +101 -0
  235. package/src/contracts/widgets.ts +183 -0
  236. package/src/desktop/index.ts +704 -0
  237. package/src/desktop/solid.tsx +938 -0
  238. package/src/index.ts +58 -0
  239. package/src/server/actor.ts +41 -0
  240. package/src/server/api/index.ts +2 -0
  241. package/src/server/api/respond.ts +96 -0
  242. package/src/server/api-client.ts +54 -0
  243. package/src/server/app-context.ts +39 -0
  244. package/src/server/help.ts +187 -0
  245. package/src/server/index.ts +89 -0
  246. package/src/server/locale.ts +55 -0
  247. package/src/server/middleware/auth.ts +386 -0
  248. package/src/server/middleware/index.ts +28 -0
  249. package/src/server/middleware/invocation.ts +55 -0
  250. package/src/server/middleware/middleware.ts +65 -0
  251. package/src/server/middleware/openapi.ts +126 -0
  252. package/src/server/middleware/rate-limit.ts +125 -0
  253. package/src/server/middleware/request-logger.ts +41 -0
  254. package/src/server/middleware/route-template.ts +46 -0
  255. package/src/server/middleware/runtime.ts +22 -0
  256. package/src/server/middleware/settings.ts +55 -0
  257. package/src/server/middleware/validator.ts +49 -0
  258. package/src/server/middleware/workload.ts +22 -0
  259. package/src/server/ratelimit.ts +151 -0
  260. package/src/server/request-cookies.ts +18 -0
  261. package/src/server/services/access.ts +644 -0
  262. package/src/server/services/freeipa/client.ts +137 -0
  263. package/src/server/services/freeipa/index.ts +9 -0
  264. package/src/server/services/freeipa/session.ts +123 -0
  265. package/src/server/services/freeipa/test-certificates.ts +44 -0
  266. package/src/server/services/freeipa/tls.ts +44 -0
  267. package/src/server/services/freeipa/transport.ts +116 -0
  268. package/src/server/services/freeipa/util.ts +59 -0
  269. package/src/server/services/geo.ts +198 -0
  270. package/src/server/services/index.ts +48 -0
  271. package/src/server/services/pagination.ts +22 -0
  272. package/src/server/services/services.ts +13 -0
  273. package/src/server/time.ts +33 -0
  274. package/src/services/account-category-policy.ts +51 -0
  275. package/src/services/account-lifecycle/audit.ts +41 -0
  276. package/src/services/account-lifecycle/index.ts +940 -0
  277. package/src/services/account-lifecycle/ipa-backfill.ts +152 -0
  278. package/src/services/account-lifecycle/notification-sender.ts +13 -0
  279. package/src/services/account-lifecycle/scheduler.ts +506 -0
  280. package/src/services/account-model.ts +22 -0
  281. package/src/services/accounts/app.ts +1671 -0
  282. package/src/services/accounts/authz.ts +44 -0
  283. package/src/services/accounts/avatar.ts +120 -0
  284. package/src/services/accounts/base-group.ts +11 -0
  285. package/src/services/accounts/base-user.ts +46 -0
  286. package/src/services/accounts/duplicate-emails.ts +82 -0
  287. package/src/services/accounts/entities.ts +622 -0
  288. package/src/services/accounts/group-sql.ts +106 -0
  289. package/src/services/accounts/groups.ts +272 -0
  290. package/src/services/accounts/index.ts +14 -0
  291. package/src/services/accounts/ipa-data.ts +66 -0
  292. package/src/services/accounts/lifecycle.ts +2 -0
  293. package/src/services/accounts/local-groups.ts +499 -0
  294. package/src/services/accounts/model.ts +129 -0
  295. package/src/services/accounts/notification-sender.ts +31 -0
  296. package/src/services/accounts/posix.ts +386 -0
  297. package/src/services/accounts/request-policy.ts +11 -0
  298. package/src/services/accounts/switching.ts +126 -0
  299. package/src/services/accounts/users.ts +745 -0
  300. package/src/services/announcements/index.ts +224 -0
  301. package/src/services/app-approval.ts +498 -0
  302. package/src/services/audit/index.ts +438 -0
  303. package/src/services/auth-flows/index.ts +15 -0
  304. package/src/services/auth-flows/ipa.ts +197 -0
  305. package/src/services/auth-flows/magic-link.ts +185 -0
  306. package/src/services/auth-flows/notification-sender.ts +10 -0
  307. package/src/services/auth-flows/password-reset.ts +268 -0
  308. package/src/services/auth-flows/proxy-return.ts +49 -0
  309. package/src/services/document-extraction.ts +148 -0
  310. package/src/services/freeipa-config.ts +179 -0
  311. package/src/services/gateway.ts +186 -0
  312. package/src/services/identity/constants.ts +18 -0
  313. package/src/services/identity/index.ts +52 -0
  314. package/src/services/identity/invocation-actor.ts +120 -0
  315. package/src/services/identity/invocation-authority.ts +53 -0
  316. package/src/services/identity/invocation-operations.ts +15 -0
  317. package/src/services/identity/invocation-token.ts +276 -0
  318. package/src/services/identity/key-config.ts +41 -0
  319. package/src/services/identity/key-ring.ts +530 -0
  320. package/src/services/identity/metrics.ts +29 -0
  321. package/src/services/identity/routes.ts +18 -0
  322. package/src/services/identity/runtime-config.ts +88 -0
  323. package/src/services/identity/session-token.ts +152 -0
  324. package/src/services/identity/signer-refresh.ts +44 -0
  325. package/src/services/identity/workload-auth.ts +64 -0
  326. package/src/services/index.ts +187 -0
  327. package/src/services/ipa/auth.ts +216 -0
  328. package/src/services/ipa/connection.ts +57 -0
  329. package/src/services/ipa/effective-groups.ts +70 -0
  330. package/src/services/ipa/groups.ts +722 -0
  331. package/src/services/ipa/guard.ts +17 -0
  332. package/src/services/ipa/index.ts +17 -0
  333. package/src/services/ipa/posix.ts +19 -0
  334. package/src/services/ipa/profile.ts +132 -0
  335. package/src/services/ipa/runtime.ts +28 -0
  336. package/src/services/ipa/search.ts +154 -0
  337. package/src/services/ipa/service-account.ts +15 -0
  338. package/src/services/ipa/sync-planning.ts +100 -0
  339. package/src/services/ipa/sync.ts +862 -0
  340. package/src/services/ipa/users.ts +806 -0
  341. package/src/services/legal-consent.ts +76 -0
  342. package/src/services/logging/index.ts +477 -0
  343. package/src/services/logging/redaction.ts +20 -0
  344. package/src/services/logging/trace.ts +1231 -0
  345. package/src/services/mandates/index.ts +855 -0
  346. package/src/services/mandates/policy.ts +85 -0
  347. package/src/services/network-security.ts +31 -0
  348. package/src/services/notifications/batches.ts +722 -0
  349. package/src/services/notifications/browser.ts +252 -0
  350. package/src/services/notifications/catalog.ts +113 -0
  351. package/src/services/notifications/channels.ts +104 -0
  352. package/src/services/notifications/dispatcher.ts +139 -0
  353. package/src/services/notifications/email.ts +118 -0
  354. package/src/services/notifications/index.ts +633 -0
  355. package/src/services/notifications/live.ts +50 -0
  356. package/src/services/notifications/observability.ts +407 -0
  357. package/src/services/notifications/platform.ts +421 -0
  358. package/src/services/notifications/runtime.ts +120 -0
  359. package/src/services/notifications/user.ts +248 -0
  360. package/src/services/notifications/web-push-transport.ts +135 -0
  361. package/src/services/oauth-tokens.ts +209 -0
  362. package/src/services/outbox.ts +187 -0
  363. package/src/services/pdf/gotenberg.ts +290 -0
  364. package/src/services/pdf/index.ts +43 -0
  365. package/src/services/pdf/markdown.ts +209 -0
  366. package/src/services/pdf/template-preview.ts +122 -0
  367. package/src/services/postgres.ts +76 -0
  368. package/src/services/providers/index.ts +27 -0
  369. package/src/services/providers/local/auth.ts +51 -0
  370. package/src/services/providers/local/index.ts +4 -0
  371. package/src/services/providers/local/users.ts +263 -0
  372. package/src/services/rail-preferences.ts +27 -0
  373. package/src/services/runtime-lifecycle.ts +191 -0
  374. package/src/services/secrets.ts +10 -0
  375. package/src/services/service-account-credentials.ts +891 -0
  376. package/src/services/service-accounts.ts +184 -0
  377. package/src/services/session/index.ts +236 -0
  378. package/src/services/session/test-fixture.ts +18 -0
  379. package/src/services/session/user.ts +144 -0
  380. package/src/services/settings/api.ts +61 -0
  381. package/src/services/settings/app.ts +86 -0
  382. package/src/services/settings/core-settings.ts +796 -0
  383. package/src/services/settings/crypto.ts +69 -0
  384. package/src/services/settings/defaults.ts +374 -0
  385. package/src/services/settings/index.ts +215 -0
  386. package/src/services/settings/namespace.ts +9 -0
  387. package/src/services/settings/snapshot.ts +49 -0
  388. package/src/services/settings/store.ts +241 -0
  389. package/src/services/settings/templates.ts +4 -0
  390. package/src/services/sync-ops.ts +403 -0
  391. package/src/services/topic-cursor.ts +22 -0
  392. package/src/services/weather/forecast.ts +323 -0
  393. package/src/services/weather/geo.ts +119 -0
  394. package/src/services/weather/index.ts +67 -0
  395. package/src/services/weather/location.ts +24 -0
  396. package/src/services/weather/locations.ts +133 -0
  397. package/src/services/weather/migrate.ts +45 -0
  398. package/src/services/weather/settings.ts +66 -0
  399. package/src/services/weather/short-id.ts +57 -0
  400. package/src/services/weather/types.ts +61 -0
  401. package/src/services/weather/ui.ts +50 -0
  402. package/src/services/webauthn.ts +385 -0
  403. package/src/shared/account-action-notice.ts +70 -0
  404. package/src/shared/account-display.ts +15 -0
  405. package/src/shared/account-session.ts +15 -0
  406. package/src/shared/ai-platform-prompt.ts +134 -0
  407. package/src/shared/ai-usage.ts +49 -0
  408. package/src/shared/app-presentation.ts +48 -0
  409. package/src/shared/app-url.ts +12 -0
  410. package/src/shared/branding.ts +14 -0
  411. package/src/shared/capability-messages.ts +70 -0
  412. package/src/shared/email-html.ts +54 -0
  413. package/src/shared/format.ts +91 -0
  414. package/src/shared/help.ts +68 -0
  415. package/src/shared/icons.ts +400 -0
  416. package/src/shared/index.ts +54 -0
  417. package/src/shared/locale.ts +48 -0
  418. package/src/shared/login-method.ts +44 -0
  419. package/src/shared/markdown/client.ts +130 -0
  420. package/src/shared/markdown/extensions/code.ts +105 -0
  421. package/src/shared/markdown/extensions/guided-help.ts +100 -0
  422. package/src/shared/markdown/extensions/images.ts +79 -0
  423. package/src/shared/markdown/extensions/info-blocks.ts +97 -0
  424. package/src/shared/markdown/extensions/katex.ts +120 -0
  425. package/src/shared/markdown/extensions/links.ts +43 -0
  426. package/src/shared/markdown/extensions/mark.ts +48 -0
  427. package/src/shared/markdown/extensions/sub-sup.ts +60 -0
  428. package/src/shared/markdown/extensions/tables.ts +109 -0
  429. package/src/shared/markdown/extensions/task-list.ts +53 -0
  430. package/src/shared/markdown/formula.ts +1187 -0
  431. package/src/shared/markdown/index.ts +193 -0
  432. package/src/shared/markdown/shared.ts +36 -0
  433. package/src/shared/mock-cover.ts +130 -0
  434. package/src/shared/network-address.ts +106 -0
  435. package/src/shared/redirect.ts +56 -0
  436. package/src/shared/template-rendering.ts +140 -0
  437. package/src/shared/theme.ts +68 -0
  438. package/src/shared/time.ts +13 -0
  439. package/src/ssr/AdminLayout.tsx +53 -0
  440. package/src/ssr/AdminSidebar.tsx +50 -0
  441. package/src/ssr/AppLaunchpad.island.tsx +236 -0
  442. package/src/ssr/AppWorkspaceController.island.tsx +21 -0
  443. package/src/ssr/BrowserNotifications.island.tsx +135 -0
  444. package/src/ssr/GlobalAnnouncements.island.tsx +142 -0
  445. package/src/ssr/GlobalSearchDialog.behavior.test.tsx +96 -0
  446. package/src/ssr/GlobalSearchDialog.tsx +35 -0
  447. package/src/ssr/GlobalSearchHelpDialog.tsx +104 -0
  448. package/src/ssr/GlobalSearchTrigger.island.tsx +55 -0
  449. package/src/ssr/HotkeysHelpRail.island.tsx +55 -0
  450. package/src/ssr/Layout.render.test.tsx +148 -0
  451. package/src/ssr/Layout.tsx +292 -0
  452. package/src/ssr/LayoutBreadcrumbs.island.tsx +44 -0
  453. package/src/ssr/LayoutFooter.tsx +22 -0
  454. package/src/ssr/LayoutHeader.tsx +81 -0
  455. package/src/ssr/LayoutHelp.tsx +869 -0
  456. package/src/ssr/LayoutPreferences.island.tsx +36 -0
  457. package/src/ssr/LayoutRail.tsx +58 -0
  458. package/src/ssr/MinimalLayout.tsx +46 -0
  459. package/src/ssr/PageError.tsx +29 -0
  460. package/src/ssr/ProfilePreferences.island.tsx +92 -0
  461. package/src/ssr/RailApps.island.tsx +61 -0
  462. package/src/ssr/RailEditor.tsx +293 -0
  463. package/src/ssr/RegisteredHelpDocuments.island.tsx +3 -0
  464. package/src/ssr/TimezoneCookie.island.tsx +23 -0
  465. package/src/ssr/admin-active-link.ts +47 -0
  466. package/src/ssr/admin-navigation.ts +78 -0
  467. package/src/ssr/app-appearance.ts +23 -0
  468. package/src/ssr/app-navigation.ts +41 -0
  469. package/src/ssr/help-messages.ts +104 -0
  470. package/src/ssr/index.ts +14 -0
  471. package/src/ssr/islands/SearchBar.island.tsx +80 -0
  472. package/src/ssr/islands/index.ts +16 -0
  473. package/src/ssr/layout-context.ts +17 -0
  474. package/src/ssr/layout-help-markdown.ts +9 -0
  475. package/src/ssr/layout-help-navigation.ts +24 -0
  476. package/src/ssr/layout-help-url.ts +5 -0
  477. package/src/ssr/layout-runtime.ts +22 -0
  478. package/src/ssr/page-error-messages.ts +25 -0
  479. package/src/ssr/platform-messages.ts +135 -0
  480. package/src/ssr/preference-controller.ts +44 -0
  481. package/src/ssr/profile-preferences-messages.ts +29 -0
  482. package/src/ssr/rail-context.ts +21 -0
  483. package/src/ssr/rail-messages.ts +66 -0
  484. package/src/ssr/rail-navigation.ts +60 -0
  485. package/src/ssr/runtime.ts +31 -0
  486. package/src/ssr/url-filter.ts +127 -0
  487. package/src/styles/base-popover.css +36 -0
  488. package/src/styles/effects.css +851 -0
  489. package/src/styles/global.css +268 -0
  490. package/src/styles/input.css +58 -0
  491. package/src/styles/tokens.css +230 -0
  492. package/src/styles/utilities-buttons.css +405 -0
  493. package/src/styles/utilities-data.css +33 -0
  494. package/src/styles/utilities-feedback.css +232 -0
  495. package/src/styles/utilities-layout.css +603 -0
  496. package/src/styles/utilities-markdown-editor.css +525 -0
  497. package/src/styles/utilities-markdown-table.css +234 -0
  498. package/src/styles/utilities-navigation.css +1054 -0
  499. package/src/types/ambient.d.ts +24 -0
  500. package/src/workflows/ai/index.ts +17 -0
  501. package/src/workflows/ai/runtime.ts +286 -0
  502. package/src/workflows/ai/store.ts +314 -0
  503. package/src/workflows/ai/types.ts +111 -0
  504. package/src/workflows/ai-actions.ts +269 -0
  505. package/src/workflows/authoring.ts +88 -0
  506. package/src/workflows/builtins.ts +193 -0
  507. package/src/workflows/contracts.ts +280 -0
  508. package/src/workflows/definition.ts +334 -0
  509. package/src/workflows/editor.ts +113 -0
  510. package/src/workflows/index.ts +71 -0
  511. package/src/workflows/language/binder.ts +45 -0
  512. package/src/workflows/language/canonical.ts +29 -0
  513. package/src/workflows/language/compiler.ts +396 -0
  514. package/src/workflows/language/expressions.ts +34 -0
  515. package/src/workflows/language/index.ts +20 -0
  516. package/src/workflows/language/references.ts +55 -0
  517. package/src/workflows/language/schema.ts +134 -0
  518. package/src/workflows/language/strict-yaml.ts +131 -0
  519. package/src/workflows/manifest.ts +24 -0
  520. package/src/workflows/module.ts +12 -0
  521. package/src/workflows/runtime/coordinator.ts +115 -0
  522. package/src/workflows/runtime/executor.ts +1003 -0
  523. package/src/workflows/runtime/index.ts +60 -0
  524. package/src/workflows/runtime/instant.ts +8 -0
  525. package/src/workflows/runtime/ports.ts +213 -0
  526. package/src/workflows/runtime/schedule.ts +203 -0
  527. package/src/workflows/runtime/trigger.ts +37 -0
  528. package/src/workflows/runtime/wake.ts +13 -0
  529. package/src/workflows/store/actions.ts +438 -0
  530. package/src/workflows/store/budget.ts +145 -0
  531. package/src/workflows/store/definitions.ts +345 -0
  532. package/src/workflows/store/events.ts +383 -0
  533. package/src/workflows/store/index.ts +107 -0
  534. package/src/workflows/store/observability.ts +594 -0
  535. package/src/workflows/store/runs.ts +975 -0
  536. package/src/workflows/store/transaction.ts +23 -0
  537. package/src/workflows/store/worker.ts +439 -0
  538. package/src/workflows/testing/conformance.ts +72 -0
  539. package/src/workflows/testing/index.ts +11 -0
  540. package/src/workflows/testing/process-fixtures.ts +275 -0
@@ -0,0 +1,486 @@
1
+ import { mutation } from "@k2b/stdlib/solid";
2
+ import { Button, Combobox, type ComboboxOption, IconButton, Placeholder, prompts, SelectChip, Tooltip, useLocale } from "@k2b/ui";
3
+ import { createSignal, For, Show } from "solid-js";
4
+ import { CloudAvatar } from "../account/Avatar";
5
+ import type { AccessEntry, PermissionLevel, Principal } from "../contracts/shared";
6
+ import { accessMessages } from "./messages";
7
+
8
+ // ─────────────────────────────────────────────────────────────────────────
9
+ // Public API
10
+ // ─────────────────────────────────────────────────────────────────────────
11
+
12
+ /** The three grantable permission levels — `"none"` exists in the
13
+ * contract for resolution semantics but is never directly granted. */
14
+ export type GrantableLevel = Exclude<PermissionLevel, "none">;
15
+
16
+ /** Either a bare level (uses the default View / Edit / Manage label and
17
+ * icon) or an object with per-context overrides. */
18
+ export type AllowedLevel = GrantableLevel | { level: GrantableLevel; label?: string; icon?: string };
19
+
20
+ type PermissionEditorProps = {
21
+ /** Initial access entries — caller stays the source of truth for
22
+ * what's stored on the resource; the editor updates its local copy
23
+ * after successful mutations. */
24
+ initialEntries: AccessEntry[];
25
+
26
+ /** Whether the current user can edit permissions. When `false`, the
27
+ * editor renders the entries read-only — no row dropdowns, no
28
+ * delete buttons, no add form. */
29
+ canEdit?: boolean;
30
+
31
+ /** Grant access. The caller closes over the resource id. The optional
32
+ * display metadata lets deferred form drafts retain the selected name. */
33
+ grantAccess: (principal: Principal, permission: GrantableLevel, display?: { displayName: string }) => Promise<AccessEntry>;
34
+
35
+ /** Update an existing entry's permission level. */
36
+ updateAccess: (accessId: string, permission: GrantableLevel) => Promise<void>;
37
+
38
+ /** Revoke an existing entry. The last entry IS deletable — for
39
+ * hierarchical resources the parent ACL still applies. */
40
+ revokeAccess: (accessId: string) => Promise<void>;
41
+
42
+ /** Allow granting `public` access from this editor. */
43
+ allowPublic?: boolean;
44
+
45
+ /** Allow granting access to every authenticated user. Defaults to
46
+ * true; resources with an explicit principal allowlist can disable it. */
47
+ allowAuthenticated?: boolean;
48
+
49
+ /** Allow granting service accounts. Off by default so ordinary
50
+ * permission pickers stay user/group focused. */
51
+ allowServiceAccounts?: boolean;
52
+
53
+ /** Which levels the UI offers — and what they're called. Bare strings
54
+ * use the default labels (View / Edit / Manage). Objects override
55
+ * label and/or icon for per-context vocabulary (e.g. forms call
56
+ * write "Use", views call read "View"). When undefined, all three
57
+ * are offered with default labels. New entries are granted
58
+ * `allowedLevels[0]` on pick — the user upgrades via the row pill
59
+ * afterwards. */
60
+ allowedLevels?: AllowedLevel[];
61
+ };
62
+
63
+ // ─────────────────────────────────────────────────────────────────────────
64
+ // Defaults & helpers
65
+ // ─────────────────────────────────────────────────────────────────────────
66
+
67
+ const defaultLabels = (t: ReturnType<typeof accessMessages.resolve>["t"]): Record<PermissionLevel, { label: string; icon: string }> => ({
68
+ read: { label: t.view, icon: "ti-eye" },
69
+ write: { label: t.edit, icon: "ti-pencil" },
70
+ admin: { label: t.manage, icon: "ti-shield" },
71
+ // Defensive — never granted by this editor, but renders correctly if
72
+ // a legacy entry has permission === "none".
73
+ none: { label: t.noAccess, icon: "ti-ban" },
74
+ });
75
+
76
+ type ResolvedLevel = {
77
+ level: GrantableLevel;
78
+ label: string;
79
+ icon: string;
80
+ };
81
+
82
+ /** Resolve the AllowedLevel union into a flat shape the renderer can
83
+ * loop over. Falls back to the default View / Edit / Manage list when
84
+ * no override is given. */
85
+ const resolveAllowedLevels = (allowed: AllowedLevel[] | undefined, t: ReturnType<typeof accessMessages.resolve>["t"]): ResolvedLevel[] => {
86
+ const defaults = defaultLabels(t);
87
+ const list = allowed && allowed.length > 0 ? allowed : (["read", "write", "admin"] as GrantableLevel[]);
88
+ return list.map((entry) => {
89
+ const level = typeof entry === "string" ? entry : entry.level;
90
+ const override = typeof entry === "string" ? null : entry;
91
+ const def = defaults[level];
92
+ return {
93
+ level,
94
+ label: override?.label ?? def.label,
95
+ icon: override?.icon ?? def.icon,
96
+ };
97
+ });
98
+ };
99
+
100
+ /** Resolve a stored entry's permission to a renderable {label,icon},
101
+ * preferring the caller's allowedLevels override and falling back to
102
+ * the platform defaults. Tolerates "none" / unknown legacy values. */
103
+ const resolveEntryDisplay = (
104
+ permission: PermissionLevel,
105
+ allowed: ResolvedLevel[],
106
+ t: ReturnType<typeof accessMessages.resolve>["t"],
107
+ ): { label: string; icon: string } => {
108
+ const fromAllowed = allowed.find((a) => a.level === permission);
109
+ if (fromAllowed) return fromAllowed;
110
+ const defaults = defaultLabels(t);
111
+ return defaults[permission] ?? defaults.none;
112
+ };
113
+
114
+ const getEntryDisplayName = (entry: AccessEntry, t: ReturnType<typeof accessMessages.resolve>["t"]): string => {
115
+ if (entry.displayName) return entry.displayName;
116
+ if (entry.principal.type === "authenticated") return t.allUsers;
117
+ if (entry.principal.type === "public") return t.public;
118
+ if (entry.principal.type === "user") return entry.principal.userId;
119
+ if (entry.principal.type === "service_account") return entry.principal.serviceAccountId;
120
+ return entry.principal.groupId;
121
+ };
122
+
123
+ const getPrincipalIcon = (principal: Principal): string => {
124
+ switch (principal.type) {
125
+ case "user":
126
+ return "ti-user";
127
+ case "group":
128
+ return "ti-users-group";
129
+ case "service_account":
130
+ return "ti-key";
131
+ case "authenticated":
132
+ return "ti-lock-open-2";
133
+ case "public":
134
+ return "ti-world";
135
+ }
136
+ };
137
+
138
+ // Backend `/api/accounts/entities` shape (the subset we consume).
139
+ type ApiEntity =
140
+ | { kind: "user"; user: { id: string; uid: string; displayName: string; mail: string | null } }
141
+ | { kind: "group"; group: { id: string; name: string; description: string | null } }
142
+ | {
143
+ kind: "service_account";
144
+ serviceAccount: {
145
+ id: string;
146
+ name: string;
147
+ kind: "user_delegated" | "resource_bound";
148
+ appId: string | null;
149
+ resourceType: string | null;
150
+ resourceId: string | null;
151
+ };
152
+ };
153
+
154
+ // ─────────────────────────────────────────────────────────────────────────
155
+ // PermissionEditor
156
+ // ─────────────────────────────────────────────────────────────────────────
157
+
158
+ export default function PermissionEditor(props: PermissionEditorProps) {
159
+ const locale = useLocale();
160
+ const t = () => accessMessages.resolve([locale()]).t;
161
+ const [entries, setEntries] = createSignal<AccessEntry[]>([...props.initialEntries]);
162
+ const canEdit = () => props.canEdit !== false;
163
+ const allowPublic = () => props.allowPublic === true;
164
+ const allowAuthenticated = () => props.allowAuthenticated !== false;
165
+ const allowed = () => resolveAllowedLevels(props.allowedLevels, t());
166
+ const isSinglePicker = () => allowed().length === 1;
167
+
168
+ // Defensive dev-warning: an empty allowedLevels array makes the editor
169
+ // unable to grant anything.
170
+ if (props.allowedLevels && props.allowedLevels.length === 0) {
171
+ if (typeof console !== "undefined") {
172
+ console.warn(
173
+ "[PermissionEditor] `allowedLevels=[]` — the editor cannot grant any permission. Pass at least one level or omit the prop for the default View / Edit / Manage set.",
174
+ );
175
+ }
176
+ }
177
+
178
+ const existingUserIds = () =>
179
+ entries()
180
+ .filter((e) => e.principal.type === "user")
181
+ .map((e) => (e.principal as { type: "user"; userId: string }).userId);
182
+ const existingGroupIds = () =>
183
+ entries()
184
+ .filter((e) => e.principal.type === "group")
185
+ .map((e) => (e.principal as { type: "group"; groupId: string }).groupId);
186
+ const existingServiceAccountIds = () =>
187
+ entries()
188
+ .filter((e) => e.principal.type === "service_account")
189
+ .map((e) => (e.principal as { type: "service_account"; serviceAccountId: string }).serviceAccountId);
190
+ const hasAuthenticatedEntry = () => entries().some((entry) => entry.principal.type === "authenticated");
191
+ const hasPublicEntry = () => entries().some((entry) => entry.principal.type === "public");
192
+
193
+ const grantMut = mutation.create({
194
+ mutation: async (data: { principal: Principal; permission: GrantableLevel; display: { displayName: string } }) =>
195
+ props.grantAccess(data.principal, data.permission, data.display),
196
+ onSuccess: (newEntry) => {
197
+ setEntries([...entries(), newEntry as AccessEntry]);
198
+ },
199
+ onError: (err) => prompts.error(err.message),
200
+ });
201
+
202
+ const updateMut = mutation.create<{ accessId: string; permission: GrantableLevel }, { accessId: string; permission: GrantableLevel }>({
203
+ mutation: async (data) => {
204
+ await props.updateAccess(data.accessId, data.permission);
205
+ return data;
206
+ },
207
+ onSuccess: (result) => {
208
+ if (result) {
209
+ setEntries(entries().map((e) => (e.id === result.accessId ? { ...e, permission: result.permission } : e)));
210
+ }
211
+ },
212
+ onError: (err) => prompts.error(err.message),
213
+ });
214
+
215
+ const revokeMut = mutation.create<string | null, AccessEntry>({
216
+ mutation: async (entry) => {
217
+ const displayName = getEntryDisplayName(entry, t());
218
+ const confirmed = await prompts.confirm(t().removeAccessConfirm({ name: displayName }), {
219
+ title: t().removeAccess,
220
+ variant: "danger",
221
+ });
222
+ if (!confirmed) return null;
223
+ await props.revokeAccess(entry.id);
224
+ return entry.id;
225
+ },
226
+ onSuccess: (accessId) => {
227
+ if (accessId) setEntries(entries().filter((entry) => entry.id !== accessId));
228
+ },
229
+ onError: (err) => prompts.error(err.message),
230
+ });
231
+ const busy = () => grantMut.loading() || updateMut.loading() || revokeMut.loading();
232
+
233
+ // ── Combobox add-flow ─────────────────────────────────────────────────
234
+ // The Combobox is a fire-and-forget input: type → pick → granted at the
235
+ // lowest allowed level. The `principalsByOptId` map carries the original
236
+ // discriminated principal across the ComboboxOption boundary so onSelect
237
+ // can route it to grantAccess without re-parsing prefixed ids.
238
+ let principalsByOptId = new Map<string, Principal>();
239
+
240
+ const fetchPrincipals = async (q: string, signal: AbortSignal): Promise<ComboboxOption[]> => {
241
+ const map = new Map<string, Principal>();
242
+ const opts: ComboboxOption[] = [];
243
+
244
+ // Synthetic principals — only when allowed AND not already granted.
245
+ // Placed first so they're visible immediately on focus, before any
246
+ // typing kicks off a backend request.
247
+ if (allowAuthenticated() && !hasAuthenticatedEntry()) {
248
+ map.set("auth", { type: "authenticated" });
249
+ opts.push({
250
+ id: "auth",
251
+ label: t().allUsers,
252
+ description: t().signedInDescription,
253
+ icon: "ti-lock-open-2",
254
+ });
255
+ }
256
+ if (allowPublic() && !hasPublicEntry()) {
257
+ map.set("public", { type: "public" });
258
+ opts.push({
259
+ id: "public",
260
+ label: t().public,
261
+ description: t().publicDescription,
262
+ icon: "ti-world",
263
+ });
264
+ }
265
+
266
+ // Real entities require a query — avoid a wide listing on every focus.
267
+ if (q.length >= 2) {
268
+ const url = new URL("/api/accounts/entities", window.location.origin);
269
+ url.searchParams.set("search", q);
270
+ url.searchParams.set("kinds", props.allowServiceAccounts ? "user,group,service_account" : "user,group");
271
+ url.searchParams.set("per_page", "10");
272
+ const userIds = existingUserIds();
273
+ if (userIds.length) url.searchParams.set("exclude_user_ids", userIds.join(","));
274
+ const groupIds = existingGroupIds();
275
+ if (groupIds.length) url.searchParams.set("exclude_group_ids", groupIds.join(","));
276
+ const serviceAccountIds = existingServiceAccountIds();
277
+ if (serviceAccountIds.length) url.searchParams.set("exclude_service_account_ids", serviceAccountIds.join(","));
278
+
279
+ const res = await fetch(url.toString(), { credentials: "same-origin", signal });
280
+ if (res.ok) {
281
+ const data = (await res.json()) as { items?: ApiEntity[] };
282
+ for (const item of data.items ?? []) {
283
+ if (item.kind === "user") {
284
+ const id = `u:${item.user.id}`;
285
+ map.set(id, { type: "user", userId: item.user.id });
286
+ opts.push({
287
+ id,
288
+ label: item.user.displayName,
289
+ description: item.user.mail ?? item.user.uid,
290
+ icon: "ti ti-user",
291
+ });
292
+ } else if (item.kind === "group") {
293
+ const id = `g:${item.group.id}`;
294
+ map.set(id, { type: "group", groupId: item.group.id });
295
+ opts.push({
296
+ id,
297
+ label: item.group.name,
298
+ description: item.group.description ?? undefined,
299
+ icon: "ti ti-users-group",
300
+ });
301
+ } else if (item.kind === "service_account") {
302
+ const id = `sa:${item.serviceAccount.id}`;
303
+ map.set(id, { type: "service_account", serviceAccountId: item.serviceAccount.id });
304
+ opts.push({
305
+ id,
306
+ label: item.serviceAccount.name,
307
+ description:
308
+ item.serviceAccount.kind === "user_delegated"
309
+ ? t().userBoundServiceAccount
310
+ : [item.serviceAccount.appId, item.serviceAccount.resourceType, item.serviceAccount.resourceId]
311
+ .filter(Boolean)
312
+ .join(" · "),
313
+ icon: "ti ti-key",
314
+ });
315
+ }
316
+ }
317
+ }
318
+ }
319
+
320
+ principalsByOptId = map;
321
+ return opts;
322
+ };
323
+
324
+ const handleSelect = (option: ComboboxOption) => {
325
+ if (busy()) return;
326
+ const principal = principalsByOptId.get(option.id);
327
+ if (!principal) return;
328
+ const firstLevel = allowed()[0]?.level;
329
+ if (!firstLevel) return; // dev-warned above; bail silently
330
+ grantMut.mutate({ principal, permission: firstLevel, display: { displayName: option.label } });
331
+ };
332
+
333
+ return (
334
+ <div class="flex flex-col gap-3">
335
+ {/* Existing entries */}
336
+ <div class="flex flex-col gap-1">
337
+ <For each={entries()}>
338
+ {(entry) => (
339
+ <AccessEntryRow
340
+ entry={entry}
341
+ canEdit={canEdit()}
342
+ disabled={busy()}
343
+ allowed={allowed()}
344
+ singlePicker={isSinglePicker()}
345
+ onUpdatePermission={(permission) => {
346
+ if (!busy()) void updateMut.mutate({ accessId: entry.id, permission });
347
+ }}
348
+ onRevoke={() => {
349
+ if (!busy()) void revokeMut.mutate(entry);
350
+ }}
351
+ />
352
+ )}
353
+ </For>
354
+ <Show when={entries().length === 0}>
355
+ <Placeholder align="left" class="px-1 py-2" description={t().noDirectGrants} />
356
+ </Show>
357
+ </div>
358
+
359
+ {/* Add access — single Combobox, granted at the lowest allowed
360
+ level on pick. The user upgrades via the row pill if they want
361
+ a higher level. KISS: one decision per step. */}
362
+ <Show when={canEdit()}>
363
+ <Combobox
364
+ placeholder={
365
+ props.allowServiceAccounts
366
+ ? allowAuthenticated()
367
+ ? t().addAll
368
+ : t().addService
369
+ : allowAuthenticated()
370
+ ? t().addAudience
371
+ : t().addBasic
372
+ }
373
+ fetchData={fetchPrincipals}
374
+ onSelect={handleSelect}
375
+ disabled={busy()}
376
+ />
377
+ </Show>
378
+ </div>
379
+ );
380
+ }
381
+
382
+ // ─────────────────────────────────────────────────────────────────────────
383
+ // Access Entry Row
384
+ // ─────────────────────────────────────────────────────────────────────────
385
+
386
+ function AccessEntryRow(props: {
387
+ entry: AccessEntry;
388
+ canEdit: boolean;
389
+ disabled: boolean;
390
+ allowed: ResolvedLevel[];
391
+ /** When true the per-row picker collapses to a non-interactive badge
392
+ * (single-level mode — there's nothing to switch to). */
393
+ singlePicker: boolean;
394
+ onUpdatePermission: (permission: GrantableLevel) => void;
395
+ onRevoke: () => void;
396
+ }) {
397
+ const locale = useLocale();
398
+ const t = () => accessMessages.resolve([locale()]).t;
399
+ const displayName = () => getEntryDisplayName(props.entry, t());
400
+ const display = () => resolveEntryDisplay(props.entry.permission, props.allowed, t());
401
+ const isInteractive = () =>
402
+ props.canEdit && !props.disabled && !props.singlePicker && props.allowed.some((option) => option.level === props.entry.permission);
403
+
404
+ const badgeClass =
405
+ "flex min-h-7 items-center gap-1 rounded-full border border-transparent bg-[var(--ui-surface-muted)] px-2.5 py-1 text-xs text-secondary";
406
+
407
+ const badgeContent = (
408
+ <>
409
+ <i class={`ti ${display().icon}`} />
410
+ <span>{display().label}</span>
411
+ <Show when={isInteractive()}>
412
+ <i class="ti ti-chevron-down text-[10px]" />
413
+ </Show>
414
+ </>
415
+ );
416
+
417
+ return (
418
+ <div class="group/access-row flex items-center gap-2 py-1.5">
419
+ <Show
420
+ when={props.entry.principal.type === "user"}
421
+ fallback={
422
+ <div class="flex h-7 w-7 shrink-0 items-center justify-center rounded-full bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">
423
+ <i class={`ti ${getPrincipalIcon(props.entry.principal)} text-sm`} />
424
+ </div>
425
+ }
426
+ >
427
+ <CloudAvatar
428
+ username={displayName()}
429
+ userId={props.entry.principal.type === "user" ? props.entry.principal.userId : undefined}
430
+ avatarHash={props.entry.avatarHash}
431
+ size="xs"
432
+ class="h-7 w-7"
433
+ />
434
+ </Show>
435
+
436
+ {/* Display name */}
437
+ <div class="min-w-0 flex-1">
438
+ <span class="truncate text-sm">{displayName()}</span>
439
+ <Show when={props.entry.principal.type === "public"}>
440
+ <span class="ml-1 text-xs text-dimmed">({t().anyoneWithLink})</span>
441
+ </Show>
442
+ </div>
443
+
444
+ {/* Permission badge — interactive single-value picker when editable,
445
+ plain span otherwise. */}
446
+ <Show when={isInteractive()} fallback={<span class={`${badgeClass} cursor-default`}>{badgeContent}</span>}>
447
+ <SelectChip
448
+ aria-label={t().permissionFor({ name: displayName() })}
449
+ value={() => props.entry.permission as GrantableLevel}
450
+ options={props.allowed.map((option) => ({
451
+ value: option.level,
452
+ label: option.label,
453
+ icon: `ti ${option.icon}`,
454
+ }))}
455
+ icon={`ti ${display().icon}`}
456
+ position="bottom-left"
457
+ onValueChange={(permission) => {
458
+ if (permission !== props.entry.permission) props.onUpdatePermission(permission);
459
+ }}
460
+ />
461
+ </Show>
462
+
463
+ {/* Destructive row actions stay quiet until the row is engaged. They
464
+ remain keyboard reachable and stay visible on touch-sized layouts. */}
465
+ <Show when={props.canEdit}>
466
+ <Tooltip.Anchor
467
+ content={t().remove({ name: displayName() })}
468
+ class="shrink-0 opacity-100 transition-opacity sm:opacity-0 sm:group-hover/access-row:opacity-100 sm:group-focus-within/access-row:opacity-100"
469
+ >
470
+ <IconButton
471
+ type="button"
472
+ label={t().remove({ name: displayName() })}
473
+ variant="ghost"
474
+ size="xs"
475
+ onClick={props.onRevoke}
476
+ disabled={props.disabled}
477
+ aria-label={t().remove({ name: displayName() })}
478
+ class="focus-ui flex h-7 w-7 items-center justify-center rounded text-zinc-400 transition-colors hover:bg-red-500/[0.08] hover:text-red-600 focus:opacity-100 dark:hover:text-red-400"
479
+ >
480
+ <i class="ti ti-x text-sm" />
481
+ </IconButton>
482
+ </Tooltip.Anchor>
483
+ </Show>
484
+ </div>
485
+ );
486
+ }