@cyberskill/design 1.1.1 → 1.3.8

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 (481) hide show
  1. package/DESIGN.md +28 -5
  2. package/README.md +23 -22
  3. package/SKILL.md +7 -7
  4. package/THIRD-PARTY-NOTICES.md +35 -0
  5. package/VERSION +1 -1
  6. package/_ds_bundle.js +1495 -1733
  7. package/_ds_manifest.json +1 -1
  8. package/_esm/cs.mjs +18 -4
  9. package/_esm/react.d.ts +2 -0
  10. package/_esm/react.mjs +7 -1
  11. package/_vendor/NOTICE.md +16 -0
  12. package/_vendor/babel/README.md +7 -0
  13. package/_vendor/babel/babel.min.js +4 -0
  14. package/_vendor/react/README.md +3 -0
  15. package/_vendor/react/react-dom.production.min.js +1 -0
  16. package/_vendor/react/react.production.min.js +1 -0
  17. package/assets/aurora-hoa.webp +0 -0
  18. package/assets/aurora-kim.webp +0 -0
  19. package/assets/aurora-moc.webp +0 -0
  20. package/assets/aurora-tho.webp +0 -0
  21. package/assets/aurora-thuy.webp +0 -0
  22. package/base/a11y.css +18 -0
  23. package/base/ai.css +9 -4
  24. package/base/collections.css +18 -7
  25. package/base/components.css +16 -4
  26. package/base/controls.css +116 -66
  27. package/base/data.css +4 -4
  28. package/base/feedback.css +14 -2
  29. package/base/forms.css +10 -4
  30. package/base/glass.css +7 -1
  31. package/base/interaction.css +6 -2
  32. package/base/navigation.css +9 -3
  33. package/base/reset.css +7 -1
  34. package/base/responsive.css +38 -12
  35. package/base/shell.css +15 -3
  36. package/components/_i18n/i18n.js +41 -4
  37. package/components/_i18n/strings.js +89 -22
  38. package/components/ai/ChatMessage.d.ts +2 -0
  39. package/components/ai/ChatMessage.jsx +8 -4
  40. package/components/ai/ConfidenceMeter.d.ts +2 -0
  41. package/components/ai/ConfidenceMeter.jsx +8 -6
  42. package/components/ai/HumanReviewGate.d.ts +2 -0
  43. package/components/ai/PromptInput.d.ts +2 -0
  44. package/components/ai/TypingIndicator.d.ts +2 -0
  45. package/components/ai/ai-conversation.card.html +3 -3
  46. package/components/ai/ai-lumi.card.html +3 -3
  47. package/components/ai/ai.card.html +3 -3
  48. package/components/brand/brand.card.html +3 -3
  49. package/components/button/buttons.card.html +3 -3
  50. package/components/data/Image.jsx +18 -2
  51. package/components/data/Sortable.d.ts +5 -1
  52. package/components/data/Sortable.jsx +42 -9
  53. package/components/data/Sortable.prompt.md +4 -2
  54. package/components/data/Terminal.jsx +3 -2
  55. package/components/data/Tooltip.d.ts +2 -0
  56. package/components/data/Tooltip.jsx +6 -3
  57. package/components/data/Tree.d.ts +2 -1
  58. package/components/data/Tree.jsx +137 -16
  59. package/components/data/Tree.prompt.md +1 -1
  60. package/components/data/Watermark.jsx +11 -3
  61. package/components/data/collections.card.html +3 -3
  62. package/components/data/data.card.html +3 -3
  63. package/components/datatable/DataGrid.jsx +10 -1
  64. package/components/datatable/DataTable.d.ts +24 -1
  65. package/components/datatable/DataTable.jsx +78 -5
  66. package/components/datatable/DataTable.prompt.md +2 -1
  67. package/components/datatable/datatable.card.html +20 -16
  68. package/components/dialog/Dialog.jsx +13 -45
  69. package/components/dialog/dialog.card.html +3 -3
  70. package/components/feedback/Alert.d.ts +2 -0
  71. package/components/feedback/Alert.jsx +8 -2
  72. package/components/feedback/EmptyState.d.ts +2 -0
  73. package/components/feedback/EmptyState.jsx +6 -3
  74. package/components/feedback/ProgressBar.d.ts +2 -0
  75. package/components/feedback/ProgressBar.jsx +9 -4
  76. package/components/feedback/Toast.jsx +15 -1
  77. package/components/feedback/Toast.prompt.md +2 -1
  78. package/components/feedback/feedback-2.card.html +3 -3
  79. package/components/feedback/feedback.card.html +3 -3
  80. package/components/forms/Cascader.jsx +76 -8
  81. package/components/forms/Editor.jsx +37 -2
  82. package/components/forms/FileUpload.d.ts +2 -0
  83. package/components/forms/FileUpload.jsx +22 -8
  84. package/components/forms/Form.d.ts +3 -1
  85. package/components/forms/Form.jsx +9 -1
  86. package/components/forms/Form.prompt.md +1 -1
  87. package/components/forms/Mentions.d.ts +1 -1
  88. package/components/forms/Mentions.jsx +59 -7
  89. package/components/forms/Mentions.prompt.md +3 -1
  90. package/components/forms/Rating.d.ts +3 -2
  91. package/components/forms/Rating.jsx +34 -3
  92. package/components/forms/Rating.prompt.md +1 -1
  93. package/components/forms/Select.d.ts +2 -0
  94. package/components/forms/Select.jsx +6 -2
  95. package/components/forms/Slider.d.ts +2 -0
  96. package/components/forms/Slider.jsx +5 -2
  97. package/components/forms/Switch.d.ts +2 -0
  98. package/components/forms/Switch.jsx +15 -3
  99. package/components/forms/TimePicker.jsx +3 -1
  100. package/components/forms/Toggle.d.ts +2 -0
  101. package/components/forms/Toggle.jsx +14 -2
  102. package/components/forms/forms-2.card.html +3 -3
  103. package/components/forms/forms.card.html +3 -3
  104. package/components/icon/Icon.d.ts +1 -1
  105. package/components/icon/Icon.jsx +1 -0
  106. package/components/icon/Icon.prompt.md +1 -1
  107. package/components/icon/icon.card.html +3 -3
  108. package/components/logo/logo.card.html +3 -3
  109. package/components/navigation/CommandPalette.d.ts +2 -0
  110. package/components/navigation/CommandPalette.jsx +22 -7
  111. package/components/navigation/Menu.d.ts +2 -0
  112. package/components/navigation/Menu.jsx +60 -6
  113. package/components/navigation/Steps.d.ts +2 -0
  114. package/components/navigation/Steps.jsx +6 -3
  115. package/components/navigation/Tabs.d.ts +2 -0
  116. package/components/navigation/Tabs.jsx +6 -3
  117. package/components/navigation/Toolbar.d.ts +3 -2
  118. package/components/navigation/Toolbar.jsx +119 -12
  119. package/components/navigation/Toolbar.prompt.md +1 -1
  120. package/components/navigation/navigation.card.html +3 -3
  121. package/components/navigation/shell.card.html +3 -3
  122. package/components/overlays/AlertDialog.jsx +19 -49
  123. package/components/overlays/ContextMenu.d.ts +2 -0
  124. package/components/overlays/ContextMenu.jsx +6 -3
  125. package/components/overlays/Drawer.d.ts +2 -0
  126. package/components/overlays/Drawer.jsx +24 -20
  127. package/components/overlays/OverlayManager.d.ts +40 -0
  128. package/components/overlays/OverlayManager.jsx +214 -0
  129. package/components/overlays/OverlayManager.prompt.md +12 -0
  130. package/components/overlays/Tour.jsx +14 -7
  131. package/components/overlays/alertdialog.card.html +3 -3
  132. package/components/overlays/overlays.card.html +3 -3
  133. package/components/textfield/textfield.card.html +3 -3
  134. package/dist/styles.min.css +2252 -0
  135. package/docs/benchmark-rubric.md +1 -1
  136. package/docs/ci-cd.md +19 -12
  137. package/docs/consumer-grant.md +3 -0
  138. package/docs/consuming.md +13 -13
  139. package/docs/contrast-report.md +79 -1
  140. package/docs/conventions.md +21 -8
  141. package/docs/decisions.md +45 -7
  142. package/docs/deploy.md +17 -6
  143. package/docs/design-styles.md +2 -2
  144. package/docs/quality-gates.md +35 -15
  145. package/docs/release-notes.md +2 -2
  146. package/docs/release-runbook.md +78 -0
  147. package/docs/sync.md +2 -2
  148. package/docs/vi/benchmark-rubric.md +1 -1
  149. package/docs/vi/ci-cd.md +17 -12
  150. package/docs/vi/consumer-grant.md +3 -0
  151. package/docs/vi/consuming.md +13 -13
  152. package/docs/vi/contrast-report.md +79 -1
  153. package/docs/vi/conventions.md +20 -7
  154. package/docs/vi/decisions.md +43 -6
  155. package/docs/vi/deploy.md +17 -6
  156. package/docs/vi/quality-gates.md +34 -15
  157. package/docs/vi/release-notes.md +2 -2
  158. package/docs/vi/release-runbook.md +78 -0
  159. package/docs/vi/sync.md +2 -2
  160. package/docs/viewer.html +2 -2
  161. package/fonts/OFL.txt +98 -0
  162. package/guidelines/atomic-view.html +13 -9
  163. package/guidelines/brand-imagery.html +1 -1
  164. package/guidelines/elements-variants.html +5 -5
  165. package/guidelines/spacing-breakpoints.html +2 -1
  166. package/guidelines/surfaces-glass.html +1 -1
  167. package/llms.txt +5 -5
  168. package/package.json +38 -10
  169. package/templates/_ds-base.canonical.js +55 -0
  170. package/templates/ai-cluster-demo.html +2 -2
  171. package/templates/app-shell/AppShell.dc.html +7 -5
  172. package/templates/app-shell/content-schema.json +7 -0
  173. package/templates/app-shell/ds-base.js +33 -1
  174. package/templates/app-shell/support.js +13 -20
  175. package/templates/article/Article.dc.html +7 -3
  176. package/templates/article/content-schema.json +7 -0
  177. package/templates/article/ds-base.js +33 -1
  178. package/templates/article/support.js +13 -20
  179. package/templates/auth/Auth.dc.html +33 -12
  180. package/templates/auth/content-schema.json +37 -0
  181. package/templates/auth/ds-base.js +33 -1
  182. package/templates/auth/support.js +13 -20
  183. package/templates/bod-deck/BodDeck.dc.html +11 -10
  184. package/templates/bod-deck/ds-base.js +33 -1
  185. package/templates/bod-deck/support.js +13 -20
  186. package/templates/bod-investor-update/BodInvestorUpdate.dc.html +4 -3
  187. package/templates/bod-investor-update/ds-base.js +33 -1
  188. package/templates/bod-investor-update/support.js +13 -20
  189. package/templates/bod-memo/BodMemo.dc.html +6 -5
  190. package/templates/bod-memo/ds-base.js +33 -1
  191. package/templates/bod-memo/support.js +13 -20
  192. package/templates/bod-minutes/BodMinutes.dc.html +2 -1
  193. package/templates/bod-minutes/ds-base.js +33 -1
  194. package/templates/bod-minutes/support.js +13 -20
  195. package/templates/bod-report/BodReport.dc.html +3 -2
  196. package/templates/bod-report/ds-base.js +33 -1
  197. package/templates/bod-report/support.js +13 -20
  198. package/templates/bod-resolution/BodResolution.dc.html +5 -4
  199. package/templates/bod-resolution/ds-base.js +33 -1
  200. package/templates/bod-resolution/support.js +13 -20
  201. package/templates/culture-event-invite/CultureEventInvite.dc.html +3 -2
  202. package/templates/culture-event-invite/ds-base.js +33 -1
  203. package/templates/culture-event-invite/support.js +13 -20
  204. package/templates/dashboard/Dashboard.dc.html +10 -6
  205. package/templates/dashboard/content-schema.json +7 -0
  206. package/templates/dashboard/ds-base.js +33 -1
  207. package/templates/dashboard/support.js +13 -20
  208. package/templates/delivery-change-note/DeliveryChangeNote.dc.html +4 -3
  209. package/templates/delivery-change-note/ds-base.js +33 -1
  210. package/templates/delivery-change-note/support.js +13 -20
  211. package/templates/delivery-kickoff/DeliveryKickoff.dc.html +3 -2
  212. package/templates/delivery-kickoff/ds-base.js +33 -1
  213. package/templates/delivery-kickoff/support.js +13 -20
  214. package/templates/delivery-qbr-deck/DeliveryQbrDeck.dc.html +6 -5
  215. package/templates/delivery-qbr-deck/ds-base.js +33 -1
  216. package/templates/delivery-qbr-deck/support.js +13 -20
  217. package/templates/delivery-runbook/DeliveryRunbook.dc.html +5 -4
  218. package/templates/delivery-runbook/ds-base.js +33 -1
  219. package/templates/delivery-runbook/support.js +13 -20
  220. package/templates/delivery-status-email/DeliveryStatusEmail.dc.html +3 -2
  221. package/templates/delivery-status-email/ds-base.js +33 -1
  222. package/templates/delivery-status-email/support.js +13 -20
  223. package/templates/doc-style-guide/DocStyleGuide.dc.html +67 -64
  224. package/templates/doc-style-guide/ds-base.js +33 -1
  225. package/templates/doc-style-guide/support.js +13 -20
  226. package/templates/doc-suite-index/DocSuiteIndex.dc.html +46 -45
  227. package/templates/doc-suite-index/ds-base.js +33 -1
  228. package/templates/doc-suite-index/support.js +13 -20
  229. package/templates/doc-templates/DocTemplates.dc.html +42 -41
  230. package/templates/doc-templates/ds-base.js +33 -1
  231. package/templates/doc-templates/support.js +13 -20
  232. package/templates/email/Email.dc.html +3 -2
  233. package/templates/email/ds-base.js +33 -1
  234. package/templates/email/support.js +13 -20
  235. package/templates/email-safe/email-announcement.html +2 -2
  236. package/templates/email-safe/email-dunning.html +2 -2
  237. package/templates/email-safe/email-investor.html +2 -2
  238. package/templates/email-safe/email-launch.html +2 -2
  239. package/templates/email-safe/email-newsletter.html +2 -2
  240. package/templates/email-safe/email-status.html +2 -2
  241. package/templates/email-safe/email-transactional.html +2 -2
  242. package/templates/finance-dunning-email/FinanceDunningEmail.dc.html +6 -5
  243. package/templates/finance-dunning-email/ds-base.js +33 -1
  244. package/templates/finance-dunning-email/support.js +13 -20
  245. package/templates/finance-expense-report/FinanceExpenseReport.dc.html +4 -3
  246. package/templates/finance-expense-report/ds-base.js +33 -1
  247. package/templates/finance-expense-report/support.js +13 -20
  248. package/templates/finance-invoice/FinanceInvoice.dc.html +6 -5
  249. package/templates/finance-invoice/ds-base.js +33 -1
  250. package/templates/finance-invoice/support.js +13 -20
  251. package/templates/finance-quote/FinanceQuote.dc.html +6 -5
  252. package/templates/finance-quote/ds-base.js +33 -1
  253. package/templates/finance-quote/support.js +13 -20
  254. package/templates/hr-announcement/HrAnnouncement.dc.html +5 -4
  255. package/templates/hr-announcement/ds-base.js +33 -1
  256. package/templates/hr-announcement/support.js +13 -20
  257. package/templates/hr-interview-kit/HrInterviewKit.dc.html +5 -4
  258. package/templates/hr-interview-kit/ds-base.js +33 -1
  259. package/templates/hr-interview-kit/support.js +13 -20
  260. package/templates/hr-offboarding/HrOffboarding.dc.html +2 -1
  261. package/templates/hr-offboarding/ds-base.js +33 -1
  262. package/templates/hr-offboarding/support.js +13 -20
  263. package/templates/hr-performance-review/HrPerformanceReview.dc.html +2 -1
  264. package/templates/hr-performance-review/ds-base.js +33 -1
  265. package/templates/hr-performance-review/support.js +13 -20
  266. package/templates/hr-pip/HrPip.dc.html +2 -1
  267. package/templates/hr-pip/ds-base.js +33 -1
  268. package/templates/hr-pip/support.js +13 -20
  269. package/templates/kitchen-sink.html +9 -4
  270. package/templates/legal-msa/LegalMsa.dc.html +2 -1
  271. package/templates/legal-msa/ds-base.js +33 -1
  272. package/templates/legal-msa/support.js +13 -20
  273. package/templates/legal-sow/LegalSow.dc.html +2 -1
  274. package/templates/legal-sow/ds-base.js +33 -1
  275. package/templates/legal-sow/support.js +13 -20
  276. package/templates/marketing-campaign-brief/MarketingCampaignBrief.dc.html +3 -2
  277. package/templates/marketing-campaign-brief/ds-base.js +33 -1
  278. package/templates/marketing-campaign-brief/support.js +13 -20
  279. package/templates/marketing-case-study/MarketingCaseStudy.dc.html +6 -5
  280. package/templates/marketing-case-study/ds-base.js +33 -1
  281. package/templates/marketing-case-study/support.js +13 -20
  282. package/templates/marketing-launch/MarketingLaunch.dc.html +6 -5
  283. package/templates/marketing-launch/ds-base.js +33 -1
  284. package/templates/marketing-launch/support.js +13 -20
  285. package/templates/marketing-newsletter/MarketingNewsletter.dc.html +13 -12
  286. package/templates/marketing-newsletter/ds-base.js +33 -1
  287. package/templates/marketing-newsletter/support.js +13 -20
  288. package/templates/marketing-page/MarketingPage.dc.html +19 -14
  289. package/templates/marketing-page/content-schema.json +1 -0
  290. package/templates/marketing-page/ds-base.js +33 -1
  291. package/templates/marketing-page/support.js +13 -20
  292. package/templates/marketing-press-release/MarketingPressRelease.dc.html +2 -1
  293. package/templates/marketing-press-release/ds-base.js +33 -1
  294. package/templates/marketing-press-release/support.js +13 -20
  295. package/templates/marketing-social-kit/MarketingSocialKit.dc.html +5 -4
  296. package/templates/marketing-social-kit/ds-base.js +33 -1
  297. package/templates/marketing-social-kit/support.js +13 -20
  298. package/templates/sales-one-pager/SalesOnePager.dc.html +5 -4
  299. package/templates/sales-one-pager/ds-base.js +33 -1
  300. package/templates/sales-one-pager/support.js +13 -20
  301. package/templates/sales-proposal-deck/SalesProposalDeck.dc.html +7 -6
  302. package/templates/sales-proposal-deck/ds-base.js +33 -1
  303. package/templates/sales-proposal-deck/support.js +13 -20
  304. package/templates/settings/Settings.dc.html +7 -3
  305. package/templates/settings/content-schema.json +7 -0
  306. package/templates/settings/ds-base.js +33 -1
  307. package/templates/settings/support.js +13 -20
  308. package/templates/slide-deck/SlideDeck.dc.html +5 -4
  309. package/templates/slide-deck/ds-base.js +33 -1
  310. package/templates/slide-deck/support.js +13 -20
  311. package/templates/team-meeting-agenda/TeamMeetingAgenda.dc.html +2 -1
  312. package/templates/team-meeting-agenda/ds-base.js +33 -1
  313. package/templates/team-meeting-agenda/support.js +13 -20
  314. package/templates/tech-incident-report/TechIncidentReport.dc.html +3 -2
  315. package/templates/tech-incident-report/ds-base.js +33 -1
  316. package/templates/tech-incident-report/support.js +13 -20
  317. package/templates/tech-release-notes/TechReleaseNotes.dc.html +6 -5
  318. package/templates/tech-release-notes/ds-base.js +33 -1
  319. package/templates/tech-release-notes/support.js +13 -20
  320. package/templates/tech-retro/TechRetro.dc.html +4 -3
  321. package/templates/tech-retro/ds-base.js +33 -1
  322. package/templates/tech-retro/support.js +13 -20
  323. package/templates/tech-rfc/TechRfc.dc.html +3 -2
  324. package/templates/tech-rfc/ds-base.js +33 -1
  325. package/templates/tech-rfc/support.js +13 -20
  326. package/templates/vn-appointment-transfer/VnAppointmentTransfer.dc.html +36 -35
  327. package/templates/vn-appointment-transfer/ds-base.js +33 -1
  328. package/templates/vn-appointment-transfer/support.js +13 -20
  329. package/templates/vn-certificate-of-employment/VnCertificateOfEmployment.dc.html +27 -26
  330. package/templates/vn-certificate-of-employment/ds-base.js +33 -1
  331. package/templates/vn-certificate-of-employment/support.js +13 -20
  332. package/templates/vn-code-of-conduct/VnCodeOfConduct.dc.html +44 -43
  333. package/templates/vn-code-of-conduct/ds-base.js +33 -1
  334. package/templates/vn-code-of-conduct/support.js +13 -20
  335. package/templates/vn-compensation-regulation/VnCompensationRegulation.dc.html +53 -52
  336. package/templates/vn-compensation-regulation/ds-base.js +33 -1
  337. package/templates/vn-compensation-regulation/support.js +13 -20
  338. package/templates/vn-contract-amendment/VnContractAmendment.dc.html +41 -40
  339. package/templates/vn-contract-amendment/ds-base.js +33 -1
  340. package/templates/vn-contract-amendment/support.js +13 -20
  341. package/templates/vn-contractor-agreement/VnContractorAgreement.dc.html +50 -49
  342. package/templates/vn-contractor-agreement/ds-base.js +33 -1
  343. package/templates/vn-contractor-agreement/support.js +13 -20
  344. package/templates/vn-data-protection-policy/VnDataProtectionPolicy.dc.html +54 -53
  345. package/templates/vn-data-protection-policy/ds-base.js +33 -1
  346. package/templates/vn-data-protection-policy/support.js +13 -20
  347. package/templates/vn-disciplinary-case-file/VnDisciplinaryCaseFile.dc.html +72 -71
  348. package/templates/vn-disciplinary-case-file/ds-base.js +33 -1
  349. package/templates/vn-disciplinary-case-file/support.js +13 -20
  350. package/templates/vn-disciplinary-schedule/VnDisciplinarySchedule.dc.html +21 -20
  351. package/templates/vn-disciplinary-schedule/ds-base.js +33 -1
  352. package/templates/vn-disciplinary-schedule/support.js +13 -20
  353. package/templates/vn-employee-request/VnEmployeeRequest.dc.html +54 -53
  354. package/templates/vn-employee-request/ds-base.js +33 -1
  355. package/templates/vn-employee-request/support.js +13 -20
  356. package/templates/vn-exit-handover/VnExitHandover.dc.html +122 -121
  357. package/templates/vn-exit-handover/ds-base.js +33 -1
  358. package/templates/vn-exit-handover/support.js +13 -20
  359. package/templates/vn-foreign-employee-pack/VnForeignEmployeePack.dc.html +31 -30
  360. package/templates/vn-foreign-employee-pack/ds-base.js +33 -1
  361. package/templates/vn-foreign-employee-pack/support.js +13 -20
  362. package/templates/vn-grassroots-democracy/VnGrassrootsDemocracy.dc.html +54 -53
  363. package/templates/vn-grassroots-democracy/ds-base.js +33 -1
  364. package/templates/vn-grassroots-democracy/support.js +13 -20
  365. package/templates/vn-infosec-policy/VnInfosecPolicy.dc.html +50 -49
  366. package/templates/vn-infosec-policy/ds-base.js +33 -1
  367. package/templates/vn-infosec-policy/support.js +13 -20
  368. package/templates/vn-internal-labor-regulations/VnInternalLaborRegulations.dc.html +84 -83
  369. package/templates/vn-internal-labor-regulations/ds-base.js +33 -1
  370. package/templates/vn-internal-labor-regulations/support.js +13 -20
  371. package/templates/vn-internship-agreement/VnInternshipAgreement.dc.html +44 -43
  372. package/templates/vn-internship-agreement/ds-base.js +33 -1
  373. package/templates/vn-internship-agreement/support.js +13 -20
  374. package/templates/vn-job-description/VnJobDescription.dc.html +28 -27
  375. package/templates/vn-job-description/ds-base.js +33 -1
  376. package/templates/vn-job-description/support.js +13 -20
  377. package/templates/vn-labor-contract/VnLaborContract.dc.html +109 -108
  378. package/templates/vn-labor-contract/ds-base.js +33 -1
  379. package/templates/vn-labor-contract/support.js +13 -20
  380. package/templates/vn-labour-management-book/VnLabourManagementBook.dc.html +32 -31
  381. package/templates/vn-labour-management-book/ds-base.js +33 -1
  382. package/templates/vn-labour-management-book/support.js +13 -20
  383. package/templates/vn-mutual-nda/VnMutualNda.dc.html +45 -44
  384. package/templates/vn-mutual-nda/ds-base.js +33 -1
  385. package/templates/vn-mutual-nda/support.js +13 -20
  386. package/templates/vn-ndnca-ip/VnNdncaIp.dc.html +73 -72
  387. package/templates/vn-ndnca-ip/ds-base.js +33 -1
  388. package/templates/vn-ndnca-ip/support.js +13 -20
  389. package/templates/vn-offer-letter/VnOfferLetter.dc.html +32 -31
  390. package/templates/vn-offer-letter/ds-base.js +33 -1
  391. package/templates/vn-offer-letter/support.js +13 -20
  392. package/templates/vn-onboarding-checklist/VnOnboardingChecklist.dc.html +16 -15
  393. package/templates/vn-onboarding-checklist/ds-base.js +33 -1
  394. package/templates/vn-onboarding-checklist/support.js +13 -20
  395. package/templates/vn-pdpl-consent/VnPdplConsent.dc.html +43 -42
  396. package/templates/vn-pdpl-consent/ds-base.js +33 -1
  397. package/templates/vn-pdpl-consent/support.js +13 -20
  398. package/templates/vn-performance-evaluation-regulation/VnPerformanceEvaluationRegulation.dc.html +65 -64
  399. package/templates/vn-performance-evaluation-regulation/ds-base.js +33 -1
  400. package/templates/vn-performance-evaluation-regulation/support.js +13 -20
  401. package/templates/vn-performance-review-form/VnPerformanceReviewForm.dc.html +49 -48
  402. package/templates/vn-performance-review-form/ds-base.js +33 -1
  403. package/templates/vn-performance-review-form/support.js +13 -20
  404. package/templates/vn-phantom-stock/VnPhantomStock.dc.html +41 -40
  405. package/templates/vn-phantom-stock/ds-base.js +33 -1
  406. package/templates/vn-phantom-stock/support.js +13 -20
  407. package/templates/vn-probation-contract/VnProbationContract.dc.html +56 -55
  408. package/templates/vn-probation-contract/ds-base.js +33 -1
  409. package/templates/vn-probation-contract/support.js +13 -20
  410. package/templates/vn-receipt-acknowledgement/VnReceiptAcknowledgement.dc.html +28 -27
  411. package/templates/vn-receipt-acknowledgement/ds-base.js +33 -1
  412. package/templates/vn-receipt-acknowledgement/support.js +13 -20
  413. package/templates/vn-remote-work-policy/VnRemoteWorkPolicy.dc.html +44 -43
  414. package/templates/vn-remote-work-policy/ds-base.js +33 -1
  415. package/templates/vn-remote-work-policy/support.js +13 -20
  416. package/templates/vn-resignation-letter/VnResignationLetter.dc.html +20 -19
  417. package/templates/vn-resignation-letter/ds-base.js +33 -1
  418. package/templates/vn-resignation-letter/support.js +13 -20
  419. package/templates/vn-salary-scale/VnSalaryScale.dc.html +39 -38
  420. package/templates/vn-salary-scale/ds-base.js +33 -1
  421. package/templates/vn-salary-scale/support.js +13 -20
  422. package/templates/vn-sow-appendix/VnSowAppendix.dc.html +21 -20
  423. package/templates/vn-sow-appendix/ds-base.js +33 -1
  424. package/templates/vn-sow-appendix/support.js +13 -20
  425. package/templates/vn-termination-decision/VnTerminationDecision.dc.html +25 -24
  426. package/templates/vn-termination-decision/ds-base.js +33 -1
  427. package/templates/vn-termination-decision/support.js +13 -20
  428. package/templates/vn-total-rewards-appendix/VnTotalRewardsAppendix.dc.html +63 -62
  429. package/templates/vn-total-rewards-appendix/ds-base.js +33 -1
  430. package/templates/vn-total-rewards-appendix/support.js +13 -20
  431. package/templates/vn-training-commitment/VnTrainingCommitment.dc.html +39 -38
  432. package/templates/vn-training-commitment/ds-base.js +33 -1
  433. package/templates/vn-training-commitment/support.js +13 -20
  434. package/templates/vn-travel-expense-policy/VnTravelExpensePolicy.dc.html +50 -49
  435. package/templates/vn-travel-expense-policy/ds-base.js +33 -1
  436. package/templates/vn-travel-expense-policy/support.js +13 -20
  437. package/tokens/colors.css +19 -1
  438. package/tokens/component-tokens.css +1 -0
  439. package/tokens/element-seeds.json +2 -2
  440. package/tokens/elements.css +228 -21
  441. package/tokens/elevation.css +8 -2
  442. package/tokens/native/CSTokens.kt +37 -2
  443. package/tokens/native/CSTokens.swift +37 -2
  444. package/tokens/native/cs_tokens.dart +37 -2
  445. package/tokens/provenance.json +12 -12
  446. package/tokens/spacing.css +4 -1
  447. package/tokens/tokens.dtcg.json +129 -16
  448. package/tokens/tokens.js +59 -18
  449. package/tokens/tokens.json +58 -17
  450. package/tokens/typography.css +2 -0
  451. package/ui_kits/deck/index.html +4 -0
  452. package/ui_kits/status-hub/StatusHub.jsx +30 -4
  453. package/ui_kits/status-hub/index.html +3 -3
  454. package/ui_kits/status-hub/login.html +55 -9
  455. package/ui_kits/status-hub/project.html +4 -0
  456. package/ui_kits/status-hub/settings.html +35 -13
  457. package/ui_kits/status-hub/status.css +5 -1
  458. package/ui_kits/website/Website.jsx +25 -3
  459. package/ui_kits/website/careers.html +5 -1
  460. package/ui_kits/website/chat.html +5 -3
  461. package/ui_kits/website/index.html +3 -3
  462. package/ui_kits/website/site.css +3 -3
  463. package/ui_kits/website/work.html +5 -1
  464. package/assets/aurora-hoa.png +0 -0
  465. package/assets/aurora-kim.png +0 -0
  466. package/assets/aurora-moc.png +0 -0
  467. package/assets/aurora-thuy.png +0 -0
  468. package/docs/status/assets/favicon.svg +0 -5
  469. package/docs/status/assets/status-legacy.css +0 -399
  470. package/docs/status/assets/status-legacy.js +0 -505
  471. package/docs/status/assets/status.css +0 -644
  472. package/docs/status/assets/status.js +0 -1308
  473. package/docs/status/data/status-feed.json +0 -1
  474. package/docs/status/data/task/TASK-REL-001.js +0 -1
  475. package/docs/status/data/task/TASK-REL-002.js +0 -1
  476. package/docs/status/index.html +0 -42
  477. package/docs/status/roadmap.html +0 -4
  478. package/docs/status/status-legacy.html +0 -117
  479. package/docs/tasks/BACKLOG.md +0 -26
  480. package/docs/tasks/release/TASK-REL-001-launch-1-1-0/spec.md +0 -55
  481. package/docs/tasks/release/TASK-REL-002-republish-1-1-1/spec.md +0 -54
package/_ds_bundle.js CHANGED
@@ -1,4 +1,4 @@
1
- /* @ds-bundle: {"format":4,"namespace":"CyberSkillDesignSystem_847b3c","components":[{"name":"AIDisclosureBadge","sourcePath":"components/ai/AIDisclosureBadge.jsx"},{"name":"ChatMessage","sourcePath":"components/ai/ChatMessage.jsx"},{"name":"CitationList","sourcePath":"components/ai/CitationList.jsx"},{"name":"ConfidenceMeter","sourcePath":"components/ai/ConfidenceMeter.jsx"},{"name":"HumanReviewGate","sourcePath":"components/ai/HumanReviewGate.jsx"},{"name":"PromptInput","sourcePath":"components/ai/PromptInput.jsx"},{"name":"PromptSuggestions","sourcePath":"components/ai/PromptSuggestions.jsx"},{"name":"TypingIndicator","sourcePath":"components/ai/TypingIndicator.jsx"},{"name":"LumiAvatar","sourcePath":"components/brand/LumiAvatar.jsx"},{"name":"Button","sourcePath":"components/button/Button.jsx"},{"name":"ButtonGroup","sourcePath":"components/button/ButtonGroup.jsx"},{"name":"FloatingActionButton","sourcePath":"components/button/FloatingActionButton.jsx"},{"name":"Accordion","sourcePath":"components/data/Accordion.jsx"},{"name":"AspectRatio","sourcePath":"components/data/AspectRatio.jsx"},{"name":"Avatar","sourcePath":"components/data/Avatar.jsx"},{"name":"AvatarGroup","sourcePath":"components/data/Avatar.jsx"},{"name":"Card","sourcePath":"components/data/Card.jsx"},{"name":"CardHeader","sourcePath":"components/data/Card.jsx"},{"name":"CardBody","sourcePath":"components/data/Card.jsx"},{"name":"CardFooter","sourcePath":"components/data/Card.jsx"},{"name":"Carousel","sourcePath":"components/data/Carousel.jsx"},{"name":"Chart","sourcePath":"components/data/Chart.jsx"},{"name":"CodeBlock","sourcePath":"components/data/CodeBlock.jsx"},{"name":"Collapsible","sourcePath":"components/data/Collapsible.jsx"},{"name":"Comment","sourcePath":"components/data/Comment.jsx"},{"name":"DescriptionList","sourcePath":"components/data/DescriptionList.jsx"},{"name":"Divider","sourcePath":"components/data/Divider.jsx"},{"name":"Image","sourcePath":"components/data/Image.jsx"},{"name":"Item","sourcePath":"components/data/Item.jsx"},{"name":"Kbd","sourcePath":"components/data/Kbd.jsx"},{"name":"List","sourcePath":"components/data/List.jsx"},{"name":"ListItem","sourcePath":"components/data/List.jsx"},{"name":"Masonry","sourcePath":"components/data/Masonry.jsx"},{"name":"QRCode","sourcePath":"components/data/QRCode.jsx"},{"name":"ScrollArea","sourcePath":"components/data/ScrollArea.jsx"},{"name":"Sortable","sourcePath":"components/data/Sortable.jsx"},{"name":"Splitter","sourcePath":"components/data/Splitter.jsx"},{"name":"Stat","sourcePath":"components/data/Stat.jsx"},{"name":"Terminal","sourcePath":"components/data/Terminal.jsx"},{"name":"Timeline","sourcePath":"components/data/Timeline.jsx"},{"name":"Tooltip","sourcePath":"components/data/Tooltip.jsx"},{"name":"Tree","sourcePath":"components/data/Tree.jsx"},{"name":"Watermark","sourcePath":"components/data/Watermark.jsx"},{"name":"DataGrid","sourcePath":"components/datatable/DataGrid.jsx"},{"name":"DataTable","sourcePath":"components/datatable/DataTable.jsx"},{"name":"TreeTable","sourcePath":"components/datatable/TreeTable.jsx"},{"name":"Dialog","sourcePath":"components/dialog/Dialog.jsx"},{"name":"Alert","sourcePath":"components/feedback/Alert.jsx"},{"name":"Badge","sourcePath":"components/feedback/Badge.jsx"},{"name":"EmptyState","sourcePath":"components/feedback/EmptyState.jsx"},{"name":"ProgressBar","sourcePath":"components/feedback/ProgressBar.jsx"},{"name":"Result","sourcePath":"components/feedback/Result.jsx"},{"name":"Skeleton","sourcePath":"components/feedback/Skeleton.jsx"},{"name":"Spinner","sourcePath":"components/feedback/Spinner.jsx"},{"name":"StatusIndicator","sourcePath":"components/feedback/StatusIndicator.jsx"},{"name":"Tag","sourcePath":"components/feedback/Tag.jsx"},{"name":"ToastStack","sourcePath":"components/feedback/Toast.jsx"},{"name":"Toast","sourcePath":"components/feedback/Toast.jsx"},{"name":"Calendar","sourcePath":"components/forms/Calendar.jsx"},{"name":"Cascader","sourcePath":"components/forms/Cascader.jsx"},{"name":"Checkbox","sourcePath":"components/forms/Checkbox.jsx"},{"name":"ColorPicker","sourcePath":"components/forms/ColorPicker.jsx"},{"name":"Combobox","sourcePath":"components/forms/Combobox.jsx"},{"name":"DatePicker","sourcePath":"components/forms/DatePicker.jsx"},{"name":"Editor","sourcePath":"components/forms/Editor.jsx"},{"name":"FileUpload","sourcePath":"components/forms/FileUpload.jsx"},{"name":"Form","sourcePath":"components/forms/Form.jsx"},{"name":"FormField","sourcePath":"components/forms/Form.jsx"},{"name":"FormFieldArray","sourcePath":"components/forms/Form.jsx"},{"name":"FormWizard","sourcePath":"components/forms/Form.jsx"},{"name":"InlineEdit","sourcePath":"components/forms/InlineEdit.jsx"},{"name":"InputGroup","sourcePath":"components/forms/InputGroup.jsx"},{"name":"InputOTP","sourcePath":"components/forms/InputOTP.jsx"},{"name":"Mentions","sourcePath":"components/forms/Mentions.jsx"},{"name":"NativeSelect","sourcePath":"components/forms/NativeSelect.jsx"},{"name":"NumberField","sourcePath":"components/forms/NumberField.jsx"},{"name":"Radio","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"RadioGroup","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"Rating","sourcePath":"components/forms/Rating.jsx"},{"name":"SearchField","sourcePath":"components/forms/SearchField.jsx"},{"name":"SegmentedControl","sourcePath":"components/forms/SegmentedControl.jsx"},{"name":"Select","sourcePath":"components/forms/Select.jsx"},{"name":"Slider","sourcePath":"components/forms/Slider.jsx"},{"name":"Switch","sourcePath":"components/forms/Switch.jsx"},{"name":"TagInput","sourcePath":"components/forms/TagInput.jsx"},{"name":"Textarea","sourcePath":"components/forms/Textarea.jsx"},{"name":"TimePicker","sourcePath":"components/forms/TimePicker.jsx"},{"name":"Toggle","sourcePath":"components/forms/Toggle.jsx"},{"name":"Transfer","sourcePath":"components/forms/Transfer.jsx"},{"name":"TreeSelect","sourcePath":"components/forms/TreeSelect.jsx"},{"name":"CS_ICONS","sourcePath":"components/icon/Icon.jsx"},{"name":"Icon","sourcePath":"components/icon/Icon.jsx"},{"name":"Logo","sourcePath":"components/logo/Logo.jsx"},{"name":"CS_LOGO_VIEWBOX","sourcePath":"components/logo/logo-data.js"},{"name":"CS_LOGO_MARK_INNER","sourcePath":"components/logo/logo-data.js"},{"name":"Anchor","sourcePath":"components/navigation/Anchor.jsx"},{"name":"BackTop","sourcePath":"components/navigation/BackTop.jsx"},{"name":"Breadcrumb","sourcePath":"components/navigation/Breadcrumb.jsx"},{"name":"CommandPalette","sourcePath":"components/navigation/CommandPalette.jsx"},{"name":"Dock","sourcePath":"components/navigation/Dock.jsx"},{"name":"HotKeys","sourcePath":"components/navigation/HotKeys.jsx"},{"name":"Link","sourcePath":"components/navigation/Link.jsx"},{"name":"Menu","sourcePath":"components/navigation/Menu.jsx"},{"name":"MenuItem","sourcePath":"components/navigation/Menu.jsx"},{"name":"Menubar","sourcePath":"components/navigation/Menubar.jsx"},{"name":"NavigationMenu","sourcePath":"components/navigation/NavigationMenu.jsx"},{"name":"Pagination","sourcePath":"components/navigation/Pagination.jsx"},{"name":"Sidebar","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"NavItem","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"Steps","sourcePath":"components/navigation/Steps.jsx"},{"name":"Tabs","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Tab","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Toolbar","sourcePath":"components/navigation/Toolbar.jsx"},{"name":"AlertDialog","sourcePath":"components/overlays/AlertDialog.jsx"},{"name":"ContextMenu","sourcePath":"components/overlays/ContextMenu.jsx"},{"name":"Drawer","sourcePath":"components/overlays/Drawer.jsx"},{"name":"HoverCard","sourcePath":"components/overlays/HoverCard.jsx"},{"name":"Popconfirm","sourcePath":"components/overlays/Popconfirm.jsx"},{"name":"Popover","sourcePath":"components/overlays/Popover.jsx"},{"name":"Tour","sourcePath":"components/overlays/Tour.jsx"},{"name":"TextField","sourcePath":"components/textfield/TextField.jsx"}],"sourceHashes":{"components/_i18n/i18n.js":"c6c51a65bd79","components/_i18n/strings.js":"73bc5837fc8e","components/_utils/cx.js":"ad6a747ea116","components/ai/AIDisclosureBadge.jsx":"4c8954aa2d79","components/ai/ChatMessage.jsx":"84076f3488f2","components/ai/CitationList.jsx":"4d12ef868ff3","components/ai/ConfidenceMeter.jsx":"24e43b7f7a47","components/ai/HumanReviewGate.jsx":"922b278dca5c","components/ai/PromptInput.jsx":"7634643dafa5","components/ai/PromptSuggestions.jsx":"02d64117ecfa","components/ai/TypingIndicator.jsx":"149e3a0c7ef8","components/brand/LumiAvatar.jsx":"b6444360cf80","components/button/Button.jsx":"6d902a925eed","components/button/ButtonGroup.jsx":"1a1481f6fb93","components/button/FloatingActionButton.jsx":"ce49fe4129a3","components/data/Accordion.jsx":"fed81278cefd","components/data/AspectRatio.jsx":"6c1e8703891e","components/data/Avatar.jsx":"88c8f59334e6","components/data/Card.jsx":"96e1f9ef44a9","components/data/Carousel.jsx":"caf09a766c42","components/data/Chart.jsx":"1953e9fef120","components/data/CodeBlock.jsx":"04e0bd2507e2","components/data/Collapsible.jsx":"3d961fef83f8","components/data/Comment.jsx":"81469a910d80","components/data/DescriptionList.jsx":"ce50d72546bb","components/data/Divider.jsx":"2d08b4a7b18e","components/data/Image.jsx":"da5c7d4c69af","components/data/Item.jsx":"7e1ac68c4e96","components/data/Kbd.jsx":"3b3d867cbdd8","components/data/List.jsx":"5c221b19efe0","components/data/Masonry.jsx":"a65508731d79","components/data/QRCode.jsx":"6acaf577a7fc","components/data/ScrollArea.jsx":"c2e2ec304b1e","components/data/Sortable.jsx":"315b50256128","components/data/Splitter.jsx":"d231064601e9","components/data/Stat.jsx":"35734a801a71","components/data/Terminal.jsx":"3a3cd4477814","components/data/Timeline.jsx":"22058a781a98","components/data/Tooltip.jsx":"4d027aff5559","components/data/Tree.jsx":"9097456de50f","components/data/Watermark.jsx":"4be0b6230d47","components/data/qr-encode.js":"14e8de77df5f","components/datatable/DataGrid.jsx":"e4aa4a47d05e","components/datatable/DataTable.jsx":"fbfad5570592","components/datatable/TreeTable.jsx":"597c35135ebb","components/dialog/Dialog.jsx":"7af51ee28ab6","components/feedback/Alert.jsx":"0bbe4e5f467a","components/feedback/Badge.jsx":"e691ae1634f8","components/feedback/EmptyState.jsx":"9a9c00523cec","components/feedback/ProgressBar.jsx":"1a66335d5d9e","components/feedback/Result.jsx":"c168f8dcb009","components/feedback/Skeleton.jsx":"86ee345cb412","components/feedback/Spinner.jsx":"cad2ed4008a6","components/feedback/StatusIndicator.jsx":"d27a5fc3db2a","components/feedback/Tag.jsx":"13d288da9f38","components/feedback/Toast.jsx":"38cc9a82ede9","components/forms/Calendar.jsx":"8a66cbe766af","components/forms/Cascader.jsx":"ada31ccaf716","components/forms/Checkbox.jsx":"2badb7b3b72b","components/forms/ColorPicker.jsx":"d04db0ee7678","components/forms/Combobox.jsx":"3f2fb4d28922","components/forms/DatePicker.jsx":"7a6c18e1e635","components/forms/Editor.jsx":"d7bed6e52cff","components/forms/FileUpload.jsx":"e8031a109093","components/forms/Form.jsx":"f7952da4b73a","components/forms/InlineEdit.jsx":"5ffb40598769","components/forms/InputGroup.jsx":"06d6c22420d0","components/forms/InputOTP.jsx":"c3a5d02ffebc","components/forms/Mentions.jsx":"04bc4b5a4ace","components/forms/NativeSelect.jsx":"d6f6d300f75d","components/forms/NumberField.jsx":"44975fd25721","components/forms/RadioGroup.jsx":"1a64f7600e01","components/forms/Rating.jsx":"06d345ecc5f4","components/forms/SearchField.jsx":"2a12c65900ec","components/forms/SegmentedControl.jsx":"1f143c62fb56","components/forms/Select.jsx":"1b5ba905b339","components/forms/Slider.jsx":"69aa1a5f1af8","components/forms/Switch.jsx":"0b37cfa41979","components/forms/TagInput.jsx":"2d1554422097","components/forms/Textarea.jsx":"cf244fee0e37","components/forms/TimePicker.jsx":"165192c8760c","components/forms/Toggle.jsx":"338453a34669","components/forms/Transfer.jsx":"dd0c02d1014e","components/forms/TreeSelect.jsx":"a5b1b91d1d6a","components/icon/Icon.jsx":"379dc6de903e","components/logo/Logo.jsx":"445c5f5fbd40","components/logo/logo-data.js":"f1a251a50c8f","components/navigation/Anchor.jsx":"491fb330e718","components/navigation/BackTop.jsx":"5cac0081fc20","components/navigation/Breadcrumb.jsx":"45636f3b0a51","components/navigation/CommandPalette.jsx":"2b5b30906b8b","components/navigation/Dock.jsx":"3073e2870c65","components/navigation/HotKeys.jsx":"f0f707a3335d","components/navigation/Link.jsx":"d4fe3fcffd2c","components/navigation/Menu.jsx":"6786788f13f8","components/navigation/Menubar.jsx":"1aa64f59891e","components/navigation/NavigationMenu.jsx":"020279fd7f79","components/navigation/Pagination.jsx":"a1fc19bfd104","components/navigation/Sidebar.jsx":"d578b013ad33","components/navigation/Steps.jsx":"85cb20bd45da","components/navigation/Tabs.jsx":"5d5921da1644","components/navigation/Toolbar.jsx":"9727d7bdbc5f","components/overlays/AlertDialog.jsx":"2ed7fbde4a5a","components/overlays/ContextMenu.jsx":"e7e3386d89ac","components/overlays/Drawer.jsx":"8227143abbfa","components/overlays/HoverCard.jsx":"5cfabde80960","components/overlays/Popconfirm.jsx":"55f08dba5f38","components/overlays/Popover.jsx":"1582f7fa5533","components/overlays/Tour.jsx":"9901ee649da0","components/textfield/TextField.jsx":"c06bd9729f20","image-slot.js":"0394ad34f685","tokens/tokens.js":"02cfbae229ac","ui_kits/status-hub/StatusHub.jsx":"88dfd93e1664","ui_kits/status-hub/data.js":"664bbbb30cb3","ui_kits/website/Website.jsx":"c3a0938d9279","ui_kits/website/copy.js":"9d42c52c17cb"},"inlinedExternals":[],"unexposedExports":[{"name":"cx","sourcePath":"components/_utils/cx.js"},{"name":"formatCurrency","sourcePath":"components/_i18n/i18n.js"},{"name":"formatDate","sourcePath":"components/_i18n/i18n.js"},{"name":"formatNumber","sourcePath":"components/_i18n/i18n.js"},{"name":"getPath","sourcePath":"components/forms/Form.jsx"},{"name":"makeT","sourcePath":"components/_i18n/i18n.js"},{"name":"monthName","sourcePath":"components/_i18n/i18n.js"},{"name":"qrMatrix","sourcePath":"components/data/qr-encode.js"},{"name":"resolveLang","sourcePath":"components/_i18n/i18n.js"},{"name":"setPath","sourcePath":"components/forms/Form.jsx"},{"name":"strings","sourcePath":"components/_i18n/strings.js"},{"name":"tokens","sourcePath":"tokens/tokens.js"},{"name":"tr","sourcePath":"components/_i18n/i18n.js"},{"name":"useLang","sourcePath":"components/_i18n/i18n.js"}]} */
1
+ /* @ds-bundle: {"format":4,"namespace":"CyberSkillDesignSystem_847b3c","components":[{"name":"AIDisclosureBadge","sourcePath":"components/ai/AIDisclosureBadge.jsx"},{"name":"ChatMessage","sourcePath":"components/ai/ChatMessage.jsx"},{"name":"CitationList","sourcePath":"components/ai/CitationList.jsx"},{"name":"ConfidenceMeter","sourcePath":"components/ai/ConfidenceMeter.jsx"},{"name":"HumanReviewGate","sourcePath":"components/ai/HumanReviewGate.jsx"},{"name":"PromptInput","sourcePath":"components/ai/PromptInput.jsx"},{"name":"PromptSuggestions","sourcePath":"components/ai/PromptSuggestions.jsx"},{"name":"TypingIndicator","sourcePath":"components/ai/TypingIndicator.jsx"},{"name":"LumiAvatar","sourcePath":"components/brand/LumiAvatar.jsx"},{"name":"Button","sourcePath":"components/button/Button.jsx"},{"name":"ButtonGroup","sourcePath":"components/button/ButtonGroup.jsx"},{"name":"FloatingActionButton","sourcePath":"components/button/FloatingActionButton.jsx"},{"name":"Accordion","sourcePath":"components/data/Accordion.jsx"},{"name":"AspectRatio","sourcePath":"components/data/AspectRatio.jsx"},{"name":"Avatar","sourcePath":"components/data/Avatar.jsx"},{"name":"AvatarGroup","sourcePath":"components/data/Avatar.jsx"},{"name":"Card","sourcePath":"components/data/Card.jsx"},{"name":"CardHeader","sourcePath":"components/data/Card.jsx"},{"name":"CardBody","sourcePath":"components/data/Card.jsx"},{"name":"CardFooter","sourcePath":"components/data/Card.jsx"},{"name":"Carousel","sourcePath":"components/data/Carousel.jsx"},{"name":"Chart","sourcePath":"components/data/Chart.jsx"},{"name":"CodeBlock","sourcePath":"components/data/CodeBlock.jsx"},{"name":"Collapsible","sourcePath":"components/data/Collapsible.jsx"},{"name":"Comment","sourcePath":"components/data/Comment.jsx"},{"name":"DescriptionList","sourcePath":"components/data/DescriptionList.jsx"},{"name":"Divider","sourcePath":"components/data/Divider.jsx"},{"name":"Image","sourcePath":"components/data/Image.jsx"},{"name":"Item","sourcePath":"components/data/Item.jsx"},{"name":"Kbd","sourcePath":"components/data/Kbd.jsx"},{"name":"List","sourcePath":"components/data/List.jsx"},{"name":"ListItem","sourcePath":"components/data/List.jsx"},{"name":"Masonry","sourcePath":"components/data/Masonry.jsx"},{"name":"QRCode","sourcePath":"components/data/QRCode.jsx"},{"name":"ScrollArea","sourcePath":"components/data/ScrollArea.jsx"},{"name":"Sortable","sourcePath":"components/data/Sortable.jsx"},{"name":"Splitter","sourcePath":"components/data/Splitter.jsx"},{"name":"Stat","sourcePath":"components/data/Stat.jsx"},{"name":"Terminal","sourcePath":"components/data/Terminal.jsx"},{"name":"Timeline","sourcePath":"components/data/Timeline.jsx"},{"name":"Tooltip","sourcePath":"components/data/Tooltip.jsx"},{"name":"Tree","sourcePath":"components/data/Tree.jsx"},{"name":"Watermark","sourcePath":"components/data/Watermark.jsx"},{"name":"DataGrid","sourcePath":"components/datatable/DataGrid.jsx"},{"name":"DataTable","sourcePath":"components/datatable/DataTable.jsx"},{"name":"TreeTable","sourcePath":"components/datatable/TreeTable.jsx"},{"name":"Dialog","sourcePath":"components/dialog/Dialog.jsx"},{"name":"Alert","sourcePath":"components/feedback/Alert.jsx"},{"name":"Badge","sourcePath":"components/feedback/Badge.jsx"},{"name":"EmptyState","sourcePath":"components/feedback/EmptyState.jsx"},{"name":"ProgressBar","sourcePath":"components/feedback/ProgressBar.jsx"},{"name":"Result","sourcePath":"components/feedback/Result.jsx"},{"name":"Skeleton","sourcePath":"components/feedback/Skeleton.jsx"},{"name":"Spinner","sourcePath":"components/feedback/Spinner.jsx"},{"name":"StatusIndicator","sourcePath":"components/feedback/StatusIndicator.jsx"},{"name":"Tag","sourcePath":"components/feedback/Tag.jsx"},{"name":"ToastStack","sourcePath":"components/feedback/Toast.jsx"},{"name":"Toast","sourcePath":"components/feedback/Toast.jsx"},{"name":"Calendar","sourcePath":"components/forms/Calendar.jsx"},{"name":"Cascader","sourcePath":"components/forms/Cascader.jsx"},{"name":"Checkbox","sourcePath":"components/forms/Checkbox.jsx"},{"name":"ColorPicker","sourcePath":"components/forms/ColorPicker.jsx"},{"name":"Combobox","sourcePath":"components/forms/Combobox.jsx"},{"name":"DatePicker","sourcePath":"components/forms/DatePicker.jsx"},{"name":"Editor","sourcePath":"components/forms/Editor.jsx"},{"name":"FileUpload","sourcePath":"components/forms/FileUpload.jsx"},{"name":"Form","sourcePath":"components/forms/Form.jsx"},{"name":"FormField","sourcePath":"components/forms/Form.jsx"},{"name":"FormFieldArray","sourcePath":"components/forms/Form.jsx"},{"name":"FormWizard","sourcePath":"components/forms/Form.jsx"},{"name":"InlineEdit","sourcePath":"components/forms/InlineEdit.jsx"},{"name":"InputGroup","sourcePath":"components/forms/InputGroup.jsx"},{"name":"InputOTP","sourcePath":"components/forms/InputOTP.jsx"},{"name":"Mentions","sourcePath":"components/forms/Mentions.jsx"},{"name":"NativeSelect","sourcePath":"components/forms/NativeSelect.jsx"},{"name":"NumberField","sourcePath":"components/forms/NumberField.jsx"},{"name":"Radio","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"RadioGroup","sourcePath":"components/forms/RadioGroup.jsx"},{"name":"Rating","sourcePath":"components/forms/Rating.jsx"},{"name":"SearchField","sourcePath":"components/forms/SearchField.jsx"},{"name":"SegmentedControl","sourcePath":"components/forms/SegmentedControl.jsx"},{"name":"Select","sourcePath":"components/forms/Select.jsx"},{"name":"Slider","sourcePath":"components/forms/Slider.jsx"},{"name":"Switch","sourcePath":"components/forms/Switch.jsx"},{"name":"TagInput","sourcePath":"components/forms/TagInput.jsx"},{"name":"Textarea","sourcePath":"components/forms/Textarea.jsx"},{"name":"TimePicker","sourcePath":"components/forms/TimePicker.jsx"},{"name":"Toggle","sourcePath":"components/forms/Toggle.jsx"},{"name":"Transfer","sourcePath":"components/forms/Transfer.jsx"},{"name":"TreeSelect","sourcePath":"components/forms/TreeSelect.jsx"},{"name":"CS_ICONS","sourcePath":"components/icon/Icon.jsx"},{"name":"Icon","sourcePath":"components/icon/Icon.jsx"},{"name":"Logo","sourcePath":"components/logo/Logo.jsx"},{"name":"CS_LOGO_VIEWBOX","sourcePath":"components/logo/logo-data.js"},{"name":"CS_LOGO_MARK_INNER","sourcePath":"components/logo/logo-data.js"},{"name":"Anchor","sourcePath":"components/navigation/Anchor.jsx"},{"name":"BackTop","sourcePath":"components/navigation/BackTop.jsx"},{"name":"Breadcrumb","sourcePath":"components/navigation/Breadcrumb.jsx"},{"name":"CommandPalette","sourcePath":"components/navigation/CommandPalette.jsx"},{"name":"Dock","sourcePath":"components/navigation/Dock.jsx"},{"name":"HotKeys","sourcePath":"components/navigation/HotKeys.jsx"},{"name":"Link","sourcePath":"components/navigation/Link.jsx"},{"name":"Menu","sourcePath":"components/navigation/Menu.jsx"},{"name":"MenuItem","sourcePath":"components/navigation/Menu.jsx"},{"name":"Menubar","sourcePath":"components/navigation/Menubar.jsx"},{"name":"NavigationMenu","sourcePath":"components/navigation/NavigationMenu.jsx"},{"name":"Pagination","sourcePath":"components/navigation/Pagination.jsx"},{"name":"Sidebar","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"NavItem","sourcePath":"components/navigation/Sidebar.jsx"},{"name":"Steps","sourcePath":"components/navigation/Steps.jsx"},{"name":"Tabs","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Tab","sourcePath":"components/navigation/Tabs.jsx"},{"name":"Toolbar","sourcePath":"components/navigation/Toolbar.jsx"},{"name":"AlertDialog","sourcePath":"components/overlays/AlertDialog.jsx"},{"name":"ContextMenu","sourcePath":"components/overlays/ContextMenu.jsx"},{"name":"Drawer","sourcePath":"components/overlays/Drawer.jsx"},{"name":"HoverCard","sourcePath":"components/overlays/HoverCard.jsx"},{"name":"OverlayProvider","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"Popconfirm","sourcePath":"components/overlays/Popconfirm.jsx"},{"name":"Popover","sourcePath":"components/overlays/Popover.jsx"},{"name":"Tour","sourcePath":"components/overlays/Tour.jsx"},{"name":"TextField","sourcePath":"components/textfield/TextField.jsx"}],"sourceHashes":{"components/_i18n/i18n.js":"c8fc9f74a4c0","components/_i18n/strings.js":"0e63dc7515d8","components/_utils/cx.js":"ad6a747ea116","components/ai/AIDisclosureBadge.jsx":"4c8954aa2d79","components/ai/ChatMessage.jsx":"344e9c13fcfe","components/ai/CitationList.jsx":"4d12ef868ff3","components/ai/ConfidenceMeter.jsx":"68cf2bba3a9e","components/ai/HumanReviewGate.jsx":"922b278dca5c","components/ai/PromptInput.jsx":"7634643dafa5","components/ai/PromptSuggestions.jsx":"02d64117ecfa","components/ai/TypingIndicator.jsx":"149e3a0c7ef8","components/brand/LumiAvatar.jsx":"b6444360cf80","components/button/Button.jsx":"6d902a925eed","components/button/ButtonGroup.jsx":"1a1481f6fb93","components/button/FloatingActionButton.jsx":"ce49fe4129a3","components/data/Accordion.jsx":"fed81278cefd","components/data/AspectRatio.jsx":"6c1e8703891e","components/data/Avatar.jsx":"88c8f59334e6","components/data/Card.jsx":"96e1f9ef44a9","components/data/Carousel.jsx":"caf09a766c42","components/data/Chart.jsx":"1953e9fef120","components/data/CodeBlock.jsx":"04e0bd2507e2","components/data/Collapsible.jsx":"3d961fef83f8","components/data/Comment.jsx":"81469a910d80","components/data/DescriptionList.jsx":"ce50d72546bb","components/data/Divider.jsx":"2d08b4a7b18e","components/data/Image.jsx":"0ea06cfe1532","components/data/Item.jsx":"7e1ac68c4e96","components/data/Kbd.jsx":"3b3d867cbdd8","components/data/List.jsx":"5c221b19efe0","components/data/Masonry.jsx":"a65508731d79","components/data/QRCode.jsx":"6acaf577a7fc","components/data/ScrollArea.jsx":"c2e2ec304b1e","components/data/Sortable.jsx":"ed7615a9469a","components/data/Splitter.jsx":"d231064601e9","components/data/Stat.jsx":"35734a801a71","components/data/Terminal.jsx":"9d419686bea8","components/data/Timeline.jsx":"22058a781a98","components/data/Tooltip.jsx":"3f30c6b1bf1d","components/data/Tree.jsx":"a1d33dec0451","components/data/Watermark.jsx":"66b7b71488d8","components/data/qr-encode.js":"14e8de77df5f","components/datatable/DataGrid.jsx":"c84fe5d5b8cc","components/datatable/DataTable.jsx":"2a7c3bda5142","components/datatable/TreeTable.jsx":"597c35135ebb","components/dialog/Dialog.jsx":"8ef7722c37c4","components/feedback/Alert.jsx":"4dbf685af3e1","components/feedback/Badge.jsx":"e691ae1634f8","components/feedback/EmptyState.jsx":"5ba1437e6d81","components/feedback/ProgressBar.jsx":"c63a431e9eed","components/feedback/Result.jsx":"c168f8dcb009","components/feedback/Skeleton.jsx":"86ee345cb412","components/feedback/Spinner.jsx":"cad2ed4008a6","components/feedback/StatusIndicator.jsx":"d27a5fc3db2a","components/feedback/Tag.jsx":"13d288da9f38","components/feedback/Toast.jsx":"6a7832751669","components/forms/Calendar.jsx":"8a66cbe766af","components/forms/Cascader.jsx":"63f35db7101d","components/forms/Checkbox.jsx":"2badb7b3b72b","components/forms/ColorPicker.jsx":"d04db0ee7678","components/forms/Combobox.jsx":"3f2fb4d28922","components/forms/DatePicker.jsx":"7a6c18e1e635","components/forms/Editor.jsx":"194484fb9dc4","components/forms/FileUpload.jsx":"509dfaff8bea","components/forms/Form.jsx":"6945025a205f","components/forms/InlineEdit.jsx":"5ffb40598769","components/forms/InputGroup.jsx":"06d6c22420d0","components/forms/InputOTP.jsx":"c3a5d02ffebc","components/forms/Mentions.jsx":"3924619137f3","components/forms/NativeSelect.jsx":"d6f6d300f75d","components/forms/NumberField.jsx":"44975fd25721","components/forms/RadioGroup.jsx":"1a64f7600e01","components/forms/Rating.jsx":"bd5c94741d63","components/forms/SearchField.jsx":"2a12c65900ec","components/forms/SegmentedControl.jsx":"1f143c62fb56","components/forms/Select.jsx":"009495d18845","components/forms/Slider.jsx":"213b7b9edec9","components/forms/Switch.jsx":"3cdf185f757c","components/forms/TagInput.jsx":"2d1554422097","components/forms/Textarea.jsx":"cf244fee0e37","components/forms/TimePicker.jsx":"abd6e54b2983","components/forms/Toggle.jsx":"a5378fd66713","components/forms/Transfer.jsx":"dd0c02d1014e","components/forms/TreeSelect.jsx":"a5b1b91d1d6a","components/icon/Icon.jsx":"280fba8477db","components/logo/Logo.jsx":"445c5f5fbd40","components/logo/logo-data.js":"f1a251a50c8f","components/navigation/Anchor.jsx":"491fb330e718","components/navigation/BackTop.jsx":"5cac0081fc20","components/navigation/Breadcrumb.jsx":"45636f3b0a51","components/navigation/CommandPalette.jsx":"51c25b8416eb","components/navigation/Dock.jsx":"3073e2870c65","components/navigation/HotKeys.jsx":"f0f707a3335d","components/navigation/Link.jsx":"d4fe3fcffd2c","components/navigation/Menu.jsx":"999e1659f8b3","components/navigation/Menubar.jsx":"1aa64f59891e","components/navigation/NavigationMenu.jsx":"020279fd7f79","components/navigation/Pagination.jsx":"a1fc19bfd104","components/navigation/Sidebar.jsx":"d578b013ad33","components/navigation/Steps.jsx":"e26c4a18cdfc","components/navigation/Tabs.jsx":"16964d61f349","components/navigation/Toolbar.jsx":"0f1f25d9e0c5","components/overlays/AlertDialog.jsx":"dcfbb7f61506","components/overlays/ContextMenu.jsx":"e3fb63e7b74f","components/overlays/Drawer.jsx":"a1c719da3f62","components/overlays/HoverCard.jsx":"5cfabde80960","components/overlays/OverlayManager.jsx":"6b58c4dad9da","components/overlays/Popconfirm.jsx":"55f08dba5f38","components/overlays/Popover.jsx":"1582f7fa5533","components/overlays/Tour.jsx":"707cd7464f0c","components/textfield/TextField.jsx":"c06bd9729f20","tokens/tokens.js":"aea97a95111a"},"inlinedExternals":[],"unexposedExports":[{"name":"attachFocusTrap","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"cx","sourcePath":"components/_utils/cx.js"},{"name":"focusableSelector","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"formatCurrency","sourcePath":"components/_i18n/i18n.js"},{"name":"formatDate","sourcePath":"components/_i18n/i18n.js"},{"name":"formatNumber","sourcePath":"components/_i18n/i18n.js"},{"name":"getOverlayManager","sourcePath":"components/overlays/OverlayManager.jsx"},{"name":"getPath","sourcePath":"components/forms/Form.jsx"},{"name":"makeT","sourcePath":"components/_i18n/i18n.js"},{"name":"monthName","sourcePath":"components/_i18n/i18n.js"},{"name":"qrMatrix","sourcePath":"components/data/qr-encode.js"},{"name":"reorderItems","sourcePath":"components/data/Sortable.jsx"},{"name":"resolveLang","sourcePath":"components/_i18n/i18n.js"},{"name":"setPath","sourcePath":"components/forms/Form.jsx"},{"name":"strings","sourcePath":"components/_i18n/strings.js"},{"name":"tokens","sourcePath":"tokens/tokens.js"},{"name":"tr","sourcePath":"components/_i18n/i18n.js"},{"name":"useLang","sourcePath":"components/_i18n/i18n.js"},{"name":"useOverlayLayer","sourcePath":"components/overlays/OverlayManager.jsx"}]} */
2
2
 
3
3
  (() => {
4
4
 
@@ -20,8 +20,8 @@ const strings = {
20
20
  vi: { label: "Đường dẫn" }
21
21
  },
22
22
  CommandPalette: {
23
- en: { placeholder: "Type a command or search…", empty: "No results found", aria: "Command palette", esc: "Esc" },
24
- vi: { placeholder: "Nhập lệnh hoặc tìm kiếm…", empty: "Không tìm thấy kết quả", aria: "Bảng lệnh", esc: "Esc" }
23
+ en: { placeholder: "Type a command or search…", empty: "No results", aria: "Command palette", esc: "Esc" },
24
+ vi: { placeholder: "Nhập lệnh hoặc tìm kiếm…", empty: "Không có kết quả", aria: "Bảng lệnh", esc: "Esc" }
25
25
  },
26
26
  SearchField: {
27
27
  en: { placeholder: "Search…", clear: "Clear search" },
@@ -44,12 +44,22 @@ const strings = {
44
44
  vi: { confirm: "Xác nhận", cancel: "Hủy" }
45
45
  },
46
46
  Drawer: {
47
- en: { close: "Close", panel: "Panel" },
48
- vi: { close: "Đóng", panel: "Bảng" }
47
+ en: { close: "Close", panel: "Side panel" },
48
+ vi: { close: "Đóng", panel: "Bảng bên" }
49
49
  },
50
50
  DataTable: {
51
- en: { empty: "No records" },
52
- vi: { empty: "Không có bản ghi" }
51
+ en: {
52
+ empty: "No results",
53
+ loading: "Loading table",
54
+ error: "We couldn't load this table",
55
+ errorHint: "Check your connection, then try again."
56
+ },
57
+ vi: {
58
+ empty: "Không có kết quả",
59
+ loading: "Đang tải bảng",
60
+ error: "Không tải được bảng này",
61
+ errorHint: "Kiểm tra kết nối, rồi thử lại."
62
+ }
53
63
  },
54
64
  ConfidenceMeter: {
55
65
  en: { label: "Confidence", low: "Low", medium: "Medium", high: "High" },
@@ -61,10 +71,10 @@ const strings = {
61
71
  },
62
72
  Tag: {
63
73
  en: { remove: "Remove" },
64
- vi: { remove: "Gỡ bỏ" }
74
+ vi: { remove: "Gỡ" }
65
75
  },
66
76
  Toast: {
67
- en: { notifications: "Notifications", dismiss: "Dismiss" },
77
+ en: { notifications: "Notifications", dismiss: "Close" },
68
78
  vi: { notifications: "Thông báo", dismiss: "Đóng" }
69
79
  },
70
80
  TypingIndicator: {
@@ -76,12 +86,12 @@ const strings = {
76
86
  vi: { toolbar: "Định dạng", bold: "Đậm", italic: "Nghiêng", list: "Danh sách", area: "Văn bản" }
77
87
  },
78
88
  Terminal: {
79
- en: { input: "Terminal input" },
80
- vi: { input: "Ô nhập lệnh" }
89
+ en: { input: "Terminal input", title: "cyberskill — zsh" },
90
+ vi: { input: "Ô nhập lệnh", title: "cyberskill — zsh" }
81
91
  },
82
92
  DataGrid: {
83
- en: { empty: "No records", selectAll: "Select all rows", selectRow: "Select row" },
84
- vi: { empty: "Không có bản ghi", selectAll: "Chọn tất cả", selectRow: "Chọn dòng" }
93
+ en: { empty: "No results", selectAll: "Select all rows", selectRow: "Select row", pin: "Pin this column", pinNamed: "Pin column {name}" },
94
+ vi: { empty: "Không có kết quả", selectAll: "Chọn tất cả", selectRow: "Chọn dòng", pin: "Ghim cột này", pinNamed: "Ghim cột {name}" }
85
95
  },
86
96
  Splitter: {
87
97
  en: { label: "Resize panes" },
@@ -115,9 +125,13 @@ const strings = {
115
125
  en: { placeholder: "Add a note — @ to mention" },
116
126
  vi: { placeholder: "Thêm ghi chú — gõ @ để nhắc tên" }
117
127
  },
128
+ Sortable: {
129
+ en: { moveUp: "Move up", moveDown: "Move down" },
130
+ vi: { moveUp: "Di chuyển lên", moveDown: "Di chuyển xuống" }
131
+ },
118
132
  Form: {
119
- en: { summary: "Please fix the following:", required: "required", requiredField: "This field is required" },
120
- vi: { summary: "Vui lòng sửa các mục sau:", required: "bắt buộc", requiredField: "Trường này là bắt buộc" }
133
+ en: { summary: "Please fix the following:", required: "required", requiredField: "Enter a value" },
134
+ vi: { summary: "Vui lòng sửa các mục sau:", required: "bắt buộc", requiredField: "Vui lòng nhập giá trị" }
121
135
  },
122
136
  BackTop: {
123
137
  en: { label: "Back to top" },
@@ -125,7 +139,7 @@ const strings = {
125
139
  },
126
140
  Carousel: {
127
141
  en: { prev: "Previous slide", next: "Next slide", slide: "Slide" },
128
- vi: { prev: "Trang trước", next: "Trang sau", slide: "Trang" }
142
+ vi: { prev: "Ảnh trước", next: "Ảnh sau", slide: "Ảnh" }
129
143
  },
130
144
  Comment: {
131
145
  en: { reply: "Reply" },
@@ -140,12 +154,12 @@ const strings = {
140
154
  vi: { more: "Thao tác khác" }
141
155
  },
142
156
  Popconfirm: {
143
- en: { ok: "OK", cancel: "Cancel" },
144
- vi: { ok: "Đồng ý", cancel: "Hủy" }
157
+ en: { ok: "Confirm", cancel: "Cancel" },
158
+ vi: { ok: "Xác nhận", cancel: "Hủy" }
145
159
  },
146
160
  Result: {
147
- en: { success: "Done", error: "Something went wrong", warning: "Please check", info: "For your information" },
148
- vi: { success: "Hoàn tất", error: "Đã có lỗi xảy ra", warning: "Vui lòng kiểm tra", info: "Thông tin" }
161
+ en: { success: "Done", error: "We couldn't complete that", warning: "Please check", info: "Here's what happened" },
162
+ vi: { success: "Hoàn tất", error: "Chúng tôi chưa hoàn tất được", warning: "Vui lòng kiểm tra", info: "Đây là điều đã xảy ra" }
149
163
  },
150
164
  InlineEdit: {
151
165
  en: { edit: "Edit", empty: "Add text" },
@@ -164,8 +178,8 @@ const strings = {
164
178
  vi: { label: "Giờ" }
165
179
  },
166
180
  Combobox: {
167
- en: { placeholder: "Select or type…", empty: "No matches" },
168
- vi: { placeholder: "Chọn hoặc nhập…", empty: "Không có kết quả phù hợp" }
181
+ en: { placeholder: "Select or type…", empty: "No results" },
182
+ vi: { placeholder: "Chọn hoặc nhập…", empty: "Không có kết quả" }
169
183
  },
170
184
  InputGroup: {
171
185
  en: { clear: "Clear", show: "Show password", hide: "Hide password", input: "Input" },
@@ -190,6 +204,58 @@ const strings = {
190
204
  HumanReviewGate: {
191
205
  en: { aria: "Human review gate", risk: "review required", approve: "Approve", reject: "Reject", reviewer: "Reviewer" },
192
206
  vi: { aria: "Cổng kiểm duyệt của con người", risk: "cần xem xét", approve: "Phê duyệt", reject: "Từ chối", reviewer: "Người duyệt" }
207
+ },
208
+ Switch: {
209
+ en: { on: "On", off: "Off" },
210
+ vi: { on: "Bật", off: "Tắt" }
211
+ },
212
+ Tabs: {
213
+ en: { list: "Tabs" },
214
+ vi: { list: "Tab" }
215
+ },
216
+ EmptyState: {
217
+ en: { title: "Nothing here yet", action: "Create your first item" },
218
+ vi: { title: "Chưa có gì ở đây", action: "Tạo mục đầu tiên" }
219
+ },
220
+ Alert: {
221
+ en: { dismiss: "Close" },
222
+ vi: { dismiss: "Đóng" }
223
+ },
224
+ Select: {
225
+ en: { placeholder: "Select…" },
226
+ vi: { placeholder: "Chọn…" }
227
+ },
228
+ Slider: {
229
+ en: { label: "Slider" },
230
+ vi: { label: "Thanh trượt" }
231
+ },
232
+ Steps: {
233
+ en: { label: "Steps" },
234
+ vi: { label: "Các bước" }
235
+ },
236
+ Toggle: {
237
+ en: { pressed: "Pressed", unpressed: "Not pressed" },
238
+ vi: { pressed: "Đang bật", unpressed: "Chưa bật" }
239
+ },
240
+ Menu: {
241
+ en: { menu: "Menu" },
242
+ vi: { menu: "Trình đơn" }
243
+ },
244
+ ContextMenu: {
245
+ en: { menu: "Context menu" },
246
+ vi: { menu: "Trình đơn ngữ cảnh" }
247
+ },
248
+ Tooltip: {
249
+ en: { label: "Tooltip" },
250
+ vi: { label: "Chú thích" }
251
+ },
252
+ ProgressBar: {
253
+ en: { label: "Progress" },
254
+ vi: { label: "Tiến độ" }
255
+ },
256
+ ChatMessage: {
257
+ en: { you: "You" },
258
+ vi: { you: "Bạn" }
193
259
  }
194
260
  };
195
261
  Object.assign(__ds_scope, { strings });
@@ -222,7 +288,16 @@ function useLang(propLang) {
222
288
  const ref = React.useRef(null);
223
289
  const [lang, setLang] = React.useState(() => resolveLang(propLang, null));
224
290
  React.useLayoutEffect(() => {
225
- setLang(resolveLang(propLang, ref.current));
291
+ const el = ref.current;
292
+ setLang(resolveLang(propLang, el));
293
+ if (!el || propLang) return void 0;
294
+ const obs = new MutationObserver(() => setLang(resolveLang(propLang, el)));
295
+ let node = el.parentElement;
296
+ while (node) {
297
+ obs.observe(node, { attributes: true, attributeFilter: ["lang"] });
298
+ node = node.parentElement;
299
+ }
300
+ return () => obs.disconnect();
226
301
  }, [propLang]);
227
302
  return [ref, lang];
228
303
  }
@@ -243,8 +318,21 @@ function formatNumber(n, lang) {
243
318
  if (n == null || isNaN(n)) return "";
244
319
  return new Intl.NumberFormat(lang === "vi" ? "vi-VN" : "en-US").format(n);
245
320
  }
246
- function formatCurrency(n, lang) {
321
+ function formatCurrency(n, langOrOpts) {
247
322
  if (n == null || isNaN(n)) return "";
323
+ const opts = langOrOpts && typeof langOrOpts === "object" ? langOrOpts : null;
324
+ if (opts && opts.currency) {
325
+ let locale = opts.locale;
326
+ if (!locale) {
327
+ locale = opts.lang === "vi" ? "vi-VN" : "en-US";
328
+ }
329
+ return new Intl.NumberFormat(locale, {
330
+ style: "currency",
331
+ currency: String(opts.currency).toUpperCase(),
332
+ currencyDisplay: "symbol"
333
+ }).format(Number(n));
334
+ }
335
+ const lang = typeof langOrOpts === "string" ? langOrOpts : opts && opts.lang || "en";
248
336
  return lang === "vi" ? new Intl.NumberFormat("vi-VN").format(n) + " ₫" : "$" + new Intl.NumberFormat("en-US").format(n);
249
337
  }
250
338
  Object.assign(__ds_scope, { resolveLang, tr, makeT, useLang, formatDate, monthName, formatNumber, formatCurrency });
@@ -287,11 +375,15 @@ Object.assign(__ds_scope, { AIDisclosureBadge });
287
375
 
288
376
  // components/ai/ChatMessage.jsx
289
377
  try { (() => {
378
+ const { makeT, useLang } = __ds_scope;
290
379
  const { cx } = __ds_scope;
291
- function ChatMessage({ role = "lumi", name, avatar, children, className }) {
380
+ function ChatMessage({ role = "lumi", name, avatar, lang, className, children }) {
292
381
  const isUser = role === "user";
293
- const defaultAvatar = isUser ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "You") : /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M12 3l1.8 5.4L19 10l-5.2 1.6L12 17l-1.8-5.4L5 10l5.2-1.6z" }));
294
- return /* @__PURE__ */ React.createElement("div", { className: cx("cs-chat-msg", isUser ? "cs-chat-msg--user" : "cs-chat-msg--lumi", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__avatar" }, avatar ?? defaultAvatar), /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__col" }, /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__name" }, name ?? (isUser ? "You" : "Lumi")), /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__bubble" }, children)));
382
+ const [ref, L] = useLang(lang);
383
+ const t = makeT("ChatMessage", L);
384
+ const you = t("you");
385
+ const defaultAvatar = isUser ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, you) : /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M12 3l1.8 5.4L19 10l-5.2 1.6L12 17l-1.8-5.4L5 10l5.2-1.6z" }));
386
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-chat-msg", isUser ? "cs-chat-msg--user" : "cs-chat-msg--lumi", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__avatar" }, avatar ?? defaultAvatar), /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__col" }, /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__name" }, name ?? (isUser ? you : "Lumi")), /* @__PURE__ */ React.createElement("div", { className: "cs-chat-msg__bubble" }, children)));
295
387
  }
296
388
  Object.assign(__ds_scope, { ChatMessage });
297
389
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/ai/ChatMessage.jsx", error: String((e && e.message) || e) }); }
@@ -319,19 +411,21 @@ const TONE = {
319
411
  };
320
412
  function ConfidenceMeter({ value, level, segments = 5, label, lang, className }) {
321
413
  let tone, filled;
414
+ const segs = Math.max(1, Number(segments) || 5);
322
415
  if (value != null) {
323
- tone = value < 0.4 ? "low" : value < 0.75 ? "medium" : "high";
324
- filled = Math.max(1, Math.round(value * segments));
416
+ const v = Math.max(0, Math.min(1, Number(value) || 0));
417
+ tone = v < 0.4 ? "low" : v < 0.75 ? "medium" : "high";
418
+ filled = Math.max(0, Math.min(segs, Math.round(v * segs)));
325
419
  } else {
326
- tone = level || "medium";
327
- filled = Math.round(TONE[tone].fill / 5 * segments);
420
+ tone = TONE[level] ? level : "medium";
421
+ filled = Math.max(0, Math.min(segs, Math.round(TONE[tone].fill / 5 * segs)));
328
422
  }
329
423
  const meta = TONE[tone];
330
424
  const [ref, L] = useLang(lang);
331
425
  const t = makeT("ConfidenceMeter", L);
332
426
  const lbl = label != null ? label : t("label");
333
427
  const levelText = t(tone);
334
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-confidence", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__head" }, /* @__PURE__ */ React.createElement("span", null, lbl), /* @__PURE__ */ React.createElement("span", { className: "cs-confidence__level", style: { color: meta.color } }, levelText)), /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__track", role: "meter", "aria-valuemin": 0, "aria-valuemax": segments, "aria-valuenow": filled, "aria-label": lbl + ": " + levelText }, Array.from({ length: segments }).map((_, i) => /* @__PURE__ */ React.createElement("span", { key: i, className: "cs-confidence__seg", style: i < filled ? { background: meta.color } : void 0 }))));
428
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-confidence", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__head" }, /* @__PURE__ */ React.createElement("span", null, lbl), /* @__PURE__ */ React.createElement("span", { className: "cs-confidence__level", style: { color: meta.color } }, levelText)), /* @__PURE__ */ React.createElement("div", { className: "cs-confidence__track", role: "meter", "aria-valuemin": 0, "aria-valuemax": segs, "aria-valuenow": filled, "aria-label": lbl + ": " + levelText }, Array.from({ length: segs }).map((_, i) => /* @__PURE__ */ React.createElement("span", { key: i, className: "cs-confidence__seg", style: i < filled ? { background: meta.color } : void 0 }))));
335
429
  }
336
430
  Object.assign(__ds_scope, { ConfidenceMeter });
337
431
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/ai/ConfidenceMeter.jsx", error: String((e && e.message) || e) }); }
@@ -822,10 +916,23 @@ Object.assign(__ds_scope, { ScrollArea });
822
916
 
823
917
  // components/data/Sortable.jsx
824
918
  try { (() => {
919
+ const { makeT, useLang } = __ds_scope;
825
920
  const { cx } = __ds_scope;
826
- function Sortable({ items = [], onChange, className }) {
921
+ function reorderItems(items, from, to) {
922
+ if (from < 0 || to < 0 || from >= items.length || to >= items.length || from === to) return items;
923
+ const next = [...items];
924
+ next.splice(to, 0, next.splice(from, 1)[0]);
925
+ return next;
926
+ }
927
+ function Sortable({ items = [], onChange, lang, className }) {
827
928
  const [dragKey, setDragKey] = React.useState(null);
828
929
  const [over, setOver] = React.useState(null);
930
+ const [ref, L] = useLang(lang);
931
+ const t = makeT("Sortable", L);
932
+ const move = (from, to) => {
933
+ const next = reorderItems(items, from, to);
934
+ if (next !== items) onChange && onChange(next);
935
+ };
829
936
  const drop = () => {
830
937
  if (dragKey == null || over == null || dragKey === over) {
831
938
  setDragKey(null);
@@ -834,19 +941,23 @@ function Sortable({ items = [], onChange, className }) {
834
941
  }
835
942
  const from = items.findIndex((i) => i.key === dragKey);
836
943
  const to = items.findIndex((i) => i.key === over);
837
- const next = [...items];
838
- next.splice(to, 0, next.splice(from, 1)[0]);
839
- onChange && onChange(next);
944
+ move(from, to);
840
945
  setDragKey(null);
841
946
  setOver(null);
842
947
  };
843
- return /* @__PURE__ */ React.createElement("ul", { className: cx("cs-sortable", className) }, items.map((it) => /* @__PURE__ */ React.createElement(
948
+ return /* @__PURE__ */ React.createElement("ul", { ref, className: cx("cs-sortable", className) }, items.map((it, idx) => /* @__PURE__ */ React.createElement(
844
949
  "li",
845
950
  {
846
951
  key: it.key,
847
952
  draggable: true,
848
953
  className: cx("cs-sortable__item", dragKey === it.key && "is-dragging", over === it.key && "is-over"),
849
- onDragStart: () => setDragKey(it.key),
954
+ onDragStart: (e) => {
955
+ if (e.target.closest && e.target.closest(".cs-sortable__ops")) {
956
+ e.preventDefault();
957
+ return;
958
+ }
959
+ setDragKey(it.key);
960
+ },
850
961
  onDragOver: (e) => {
851
962
  e.preventDefault();
852
963
  setOver(it.key);
@@ -855,10 +966,31 @@ function Sortable({ items = [], onChange, className }) {
855
966
  onDragEnd: drop
856
967
  },
857
968
  /* @__PURE__ */ React.createElement("span", { className: "cs-sortable__grip", "aria-hidden": "true" }, "⠿"),
858
- it.label
969
+ /* @__PURE__ */ React.createElement("span", { className: "cs-sortable__label" }, it.label),
970
+ /* @__PURE__ */ React.createElement("span", { className: "cs-sortable__ops", onMouseDown: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement(
971
+ "button",
972
+ {
973
+ type: "button",
974
+ className: "cs-button cs-button--secondary cs-button--xs",
975
+ "aria-label": t("moveUp"),
976
+ disabled: idx === 0,
977
+ onClick: () => move(idx, idx - 1)
978
+ },
979
+ "↑"
980
+ ), /* @__PURE__ */ React.createElement(
981
+ "button",
982
+ {
983
+ type: "button",
984
+ className: "cs-button cs-button--secondary cs-button--xs",
985
+ "aria-label": t("moveDown"),
986
+ disabled: idx === items.length - 1,
987
+ onClick: () => move(idx, idx + 1)
988
+ },
989
+ "↓"
990
+ ))
859
991
  )));
860
992
  }
861
- Object.assign(__ds_scope, { Sortable });
993
+ Object.assign(__ds_scope, { reorderItems, Sortable });
862
994
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Sortable.jsx", error: String((e && e.message) || e) }); }
863
995
 
864
996
  // components/data/Splitter.jsx
@@ -922,18 +1054,19 @@ Object.assign(__ds_scope, { Stat });
922
1054
  try { (() => {
923
1055
  const { makeT, useLang } = __ds_scope;
924
1056
  const { cx } = __ds_scope;
925
- function Terminal({ title = "cyberskill — zsh", welcome, onCommand, prompt = "➜", lang, className }) {
1057
+ function Terminal({ title, welcome, onCommand, prompt = "➜", lang, className }) {
926
1058
  const [hist, setHist] = React.useState(() => welcome ? [{ out: welcome }] : []);
927
1059
  const [q, setQ] = React.useState("");
928
1060
  const [ref, L] = useLang(lang);
929
1061
  const t = makeT("Terminal", L);
1062
+ const barTitle = title ?? t("title");
930
1063
  const run = () => {
931
1064
  if (!q.trim()) return;
932
1065
  const res = onCommand ? onCommand(q.trim()) : "";
933
1066
  setHist((h) => [...h, { cmd: q }, ...res ? [{ out: res }] : []]);
934
1067
  setQ("");
935
1068
  };
936
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-terminal", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__bar" }, /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("span", null, title)), /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__body" }, hist.map((l, i) => l.cmd != null ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-terminal__line" }, /* @__PURE__ */ React.createElement("span", { className: "p" }, prompt), " ", l.cmd) : /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-terminal__out" }, l.out)), /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__line" }, /* @__PURE__ */ React.createElement("span", { className: "p" }, prompt), /* @__PURE__ */ React.createElement(
1069
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-terminal", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__bar" }, /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("i", null), /* @__PURE__ */ React.createElement("span", null, barTitle)), /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__body" }, hist.map((l, i) => l.cmd != null ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-terminal__line" }, /* @__PURE__ */ React.createElement("span", { className: "p" }, prompt), " ", l.cmd) : /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-terminal__out" }, l.out)), /* @__PURE__ */ React.createElement("div", { className: "cs-terminal__line" }, /* @__PURE__ */ React.createElement("span", { className: "p" }, prompt), /* @__PURE__ */ React.createElement(
937
1070
  "input",
938
1071
  {
939
1072
  value: q,
@@ -960,9 +1093,12 @@ Object.assign(__ds_scope, { Timeline });
960
1093
 
961
1094
  // components/data/Tooltip.jsx
962
1095
  try { (() => {
1096
+ const { makeT, useLang } = __ds_scope;
963
1097
  const { cx } = __ds_scope;
964
- function Tooltip({ label, children, className }) {
965
- return /* @__PURE__ */ React.createElement("span", { className: cx("cs-tooltip", className) }, children, /* @__PURE__ */ React.createElement("span", { className: "cs-tooltip__bubble", role: "tooltip" }, label));
1098
+ function Tooltip({ label, children, lang, className }) {
1099
+ const [ref, L] = useLang(lang);
1100
+ const t = makeT("Tooltip", L);
1101
+ return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-tooltip", className) }, children, /* @__PURE__ */ React.createElement("span", { className: "cs-tooltip__bubble", role: "tooltip" }, label ?? t("label")));
966
1102
  }
967
1103
  Object.assign(__ds_scope, { Tooltip });
968
1104
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Tooltip.jsx", error: String((e && e.message) || e) }); }
@@ -970,16 +1106,135 @@ Object.assign(__ds_scope, { Tooltip });
970
1106
  // components/data/Tree.jsx
971
1107
  try { (() => {
972
1108
  const { cx } = __ds_scope;
973
- function Node({ n, depth, selected, onSelect, defaultOpen }) {
974
- const [open, setOpen] = React.useState(!!defaultOpen);
975
- const kids = n.children || [];
976
- return /* @__PURE__ */ React.createElement("li", { role: "treeitem", "aria-expanded": kids.length ? open : void 0, "aria-selected": selected === n.key }, /* @__PURE__ */ React.createElement("span", { className: cx("cs-tree__row", selected === n.key && "is-selected"), style: { paddingInlineStart: depth * 18 + 6 } }, kids.length ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-tree__twist", "aria-hidden": "true", tabIndex: -1, onClick: () => setOpen((o) => !o) }, open ? "▾" : "▸") : /* @__PURE__ */ React.createElement("span", { className: "cs-tree__twist", "aria-hidden": "true" }), /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-tree__label", onClick: () => {
977
- onSelect && onSelect(n.key, n);
978
- if (kids.length) setOpen((o) => !o);
979
- } }, n.label)), kids.length && open ? /* @__PURE__ */ React.createElement("ul", { role: "group" }, kids.map((c) => /* @__PURE__ */ React.createElement(Node, { key: c.key, n: c, depth: depth + 1, selected, onSelect, defaultOpen }))) : null);
1109
+ function collectVisible(nodes, openMap, out = []) {
1110
+ for (const n of nodes) {
1111
+ out.push(n);
1112
+ const kids = n.children || [];
1113
+ if (kids.length && openMap[n.key]) collectVisible(kids, openMap, out);
1114
+ }
1115
+ return out;
1116
+ }
1117
+ function parentOf(nodes, key, parent = null) {
1118
+ for (const n of nodes) {
1119
+ if (n.key === key) return parent;
1120
+ const kids = n.children || [];
1121
+ if (kids.length) {
1122
+ const p = parentOf(kids, key, n);
1123
+ if (p !== void 0) return p;
1124
+ }
1125
+ }
1126
+ return void 0;
1127
+ }
1128
+ function findNode(nodes, key) {
1129
+ for (const n of nodes) {
1130
+ if (n.key === key) return n;
1131
+ const kids = n.children || [];
1132
+ if (kids.length) {
1133
+ const hit = findNode(kids, key);
1134
+ if (hit) return hit;
1135
+ }
1136
+ }
1137
+ return null;
1138
+ }
1139
+ function seedOpen(nodes, defaultOpen, map = {}) {
1140
+ for (const n of nodes) {
1141
+ const kids = n.children || [];
1142
+ if (kids.length) {
1143
+ map[n.key] = !!defaultOpen;
1144
+ seedOpen(kids, defaultOpen, map);
1145
+ }
1146
+ }
1147
+ return map;
980
1148
  }
981
1149
  function Tree({ nodes = [], selected, onSelect, defaultOpen = false, className }) {
982
- return /* @__PURE__ */ React.createElement("ul", { role: "tree", className: cx("cs-tree", className) }, nodes.map((n) => /* @__PURE__ */ React.createElement(Node, { key: n.key, n, depth: 0, selected, onSelect, defaultOpen })));
1150
+ const [openMap, setOpenMap] = React.useState(() => seedOpen(nodes, defaultOpen));
1151
+ const [focusKey, setFocusKey] = React.useState(() => {
1152
+ if (selected && findNode(nodes, selected)) return selected;
1153
+ return nodes[0] ? nodes[0].key : null;
1154
+ });
1155
+ const itemRefs = React.useRef({});
1156
+ React.useEffect(() => {
1157
+ setOpenMap((prev) => {
1158
+ const next = seedOpen(nodes, defaultOpen);
1159
+ for (const k of Object.keys(next)) if (k in prev) next[k] = prev[k];
1160
+ return next;
1161
+ });
1162
+ }, [nodes, defaultOpen]);
1163
+ const visible = React.useMemo(() => collectVisible(nodes, openMap), [nodes, openMap]);
1164
+ React.useEffect(() => {
1165
+ if (!visible.length) {
1166
+ setFocusKey(null);
1167
+ return;
1168
+ }
1169
+ if (!focusKey || !visible.some((n) => n.key === focusKey)) setFocusKey(visible[0].key);
1170
+ }, [visible, focusKey]);
1171
+ const toggle = (key) => setOpenMap((m) => ({ ...m, [key]: !m[key] }));
1172
+ const focusItem = (key) => {
1173
+ setFocusKey(key);
1174
+ requestAnimationFrame(() => {
1175
+ const el = itemRefs.current[key];
1176
+ if (el) el.focus();
1177
+ });
1178
+ };
1179
+ const onKeyDown = (e, key) => {
1180
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
1181
+ const idx = visible.findIndex((n) => n.key === key);
1182
+ if (idx < 0) return;
1183
+ const node = visible[idx];
1184
+ const kids = node.children || [];
1185
+ const open = !!openMap[key];
1186
+ if (e.key === "ArrowDown") {
1187
+ e.preventDefault();
1188
+ if (idx < visible.length - 1) focusItem(visible[idx + 1].key);
1189
+ } else if (e.key === "ArrowUp") {
1190
+ e.preventDefault();
1191
+ if (idx > 0) focusItem(visible[idx - 1].key);
1192
+ } else if (e.key === "Home") {
1193
+ e.preventDefault();
1194
+ focusItem(visible[0].key);
1195
+ } else if (e.key === "End") {
1196
+ e.preventDefault();
1197
+ focusItem(visible[visible.length - 1].key);
1198
+ } else if (e.key === "ArrowRight") {
1199
+ e.preventDefault();
1200
+ if (kids.length && !open) toggle(key);
1201
+ else if (kids.length && open) focusItem(kids[0].key);
1202
+ } else if (e.key === "ArrowLeft") {
1203
+ e.preventDefault();
1204
+ if (kids.length && open) toggle(key);
1205
+ else {
1206
+ const p = parentOf(nodes, key);
1207
+ if (p) focusItem(p.key);
1208
+ }
1209
+ } else if (e.key === "Enter" || e.key === " ") {
1210
+ e.preventDefault();
1211
+ onSelect && onSelect(key, node);
1212
+ if (kids.length) toggle(key);
1213
+ }
1214
+ };
1215
+ const renderNodes = (list, depth) => list.map((n) => {
1216
+ const kids = n.children || [];
1217
+ const open = !!openMap[n.key];
1218
+ return /* @__PURE__ */ React.createElement("li", { key: n.key, role: "treeitem", "aria-expanded": kids.length ? open : void 0, "aria-selected": selected === n.key }, /* @__PURE__ */ React.createElement("span", { className: cx("cs-tree__row", selected === n.key && "is-selected"), style: { paddingInlineStart: depth * 18 + 6 } }, kids.length ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-tree__twist", "aria-hidden": "true", tabIndex: -1, onClick: () => toggle(n.key) }, open ? "▾" : "▸") : /* @__PURE__ */ React.createElement("span", { className: "cs-tree__twist", "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
1219
+ "button",
1220
+ {
1221
+ type: "button",
1222
+ className: "cs-tree__label",
1223
+ tabIndex: focusKey === n.key ? 0 : -1,
1224
+ ref: (el) => {
1225
+ itemRefs.current[n.key] = el;
1226
+ },
1227
+ onClick: () => {
1228
+ onSelect && onSelect(n.key, n);
1229
+ if (kids.length) toggle(n.key);
1230
+ },
1231
+ onKeyDown: (e) => onKeyDown(e, n.key),
1232
+ onFocus: () => setFocusKey(n.key)
1233
+ },
1234
+ n.label
1235
+ )), kids.length && open ? /* @__PURE__ */ React.createElement("ul", { role: "group" }, renderNodes(kids, depth + 1)) : null);
1236
+ });
1237
+ return /* @__PURE__ */ React.createElement("ul", { role: "tree", className: cx("cs-tree", className) }, renderNodes(nodes, 0));
983
1238
  }
984
1239
  Object.assign(__ds_scope, { Tree });
985
1240
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Tree.jsx", error: String((e && e.message) || e) }); }
@@ -988,10 +1243,17 @@ Object.assign(__ds_scope, { Tree });
988
1243
  try { (() => {
989
1244
  const { cx } = __ds_scope;
990
1245
  function Watermark({ text = "CyberSkill", opacity = 0.09, gap = 140, rotate = -22, children, className }) {
1246
+ const host = React.useRef(null);
1247
+ const [fill, setFill] = React.useState("#45210E");
1248
+ React.useLayoutEffect(() => {
1249
+ if (!host.current || typeof getComputedStyle === "undefined") return;
1250
+ const c = getComputedStyle(host.current).getPropertyValue("--cs-color-text-primary").trim();
1251
+ if (c) setFill(c);
1252
+ }, []);
991
1253
  const svg = encodeURIComponent(
992
- `<svg xmlns="http://www.w3.org/2000/svg" width="${gap}" height="${gap}"><text x="50%" y="50%" font-family="Be Vietnam Pro, sans-serif" font-size="14" font-weight="700" fill="#45210E" fill-opacity="${opacity}" text-anchor="middle" transform="rotate(${rotate} ${gap / 2} ${gap / 2})">${String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;")}</text></svg>`
1254
+ `<svg xmlns="http://www.w3.org/2000/svg" width="${gap}" height="${gap}"><text x="50%" y="50%" font-family="Be Vietnam Pro, sans-serif" font-size="14" font-weight="700" fill="${fill}" fill-opacity="${opacity}" text-anchor="middle" transform="rotate(${rotate} ${gap / 2} ${gap / 2})">${String(text).replace(/&/g, "&amp;").replace(/</g, "&lt;")}</text></svg>`
993
1255
  );
994
- return /* @__PURE__ */ React.createElement("div", { className: cx("cs-watermark", className), style: { position: "relative" } }, children, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, pointerEvents: "none", backgroundImage: `url("data:image/svg+xml,${svg}")` } }));
1256
+ return /* @__PURE__ */ React.createElement("div", { ref: host, className: cx("cs-watermark", className), style: { position: "relative" } }, children, /* @__PURE__ */ React.createElement("div", { "aria-hidden": "true", style: { position: "absolute", inset: 0, pointerEvents: "none", backgroundImage: `url("data:image/svg+xml,${svg}")` } }));
995
1257
  }
996
1258
  Object.assign(__ds_scope, { Watermark });
997
1259
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Watermark.jsx", error: String((e && e.message) || e) }); }
@@ -1131,145 +1393,6 @@ function QRCode({ value = "", size = 128, color = "var(--cs-color-text-primary)"
1131
1393
  Object.assign(__ds_scope, { QRCode });
1132
1394
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/QRCode.jsx", error: String((e && e.message) || e) }); }
1133
1395
 
1134
- // components/datatable/DataGrid.jsx
1135
- try { (() => {
1136
- const { makeT, useLang } = __ds_scope;
1137
- const { cx } = __ds_scope;
1138
- function DataGrid({
1139
- columns = [],
1140
- rows = [],
1141
- rowKey = "id",
1142
- selectable = false,
1143
- selected = [],
1144
- onSelect,
1145
- height = 280,
1146
- caption,
1147
- empty,
1148
- lang,
1149
- className,
1150
- filterText,
1151
- filterKeys,
1152
- /** When true (or rows exceed virtualThreshold), only paint a window of rows. */
1153
- virtual = false,
1154
- /** Row threshold that auto-enables virtualization. Default 80. */
1155
- virtualThreshold = 80,
1156
- /** Approximate row height for windowing. Default 36. */
1157
- rowHeight = 36,
1158
- /** localStorage key — when set, column key order is persisted across reloads. */
1159
- persistKey
1160
- }) {
1161
- const [sort, setSort] = React.useState(null);
1162
- const [scrollTop, setScrollTop] = React.useState(0);
1163
- const [colOrder, setColOrder] = React.useState(() => {
1164
- if (!persistKey) return null;
1165
- try {
1166
- const raw = localStorage.getItem("cs:datagrid:cols:" + persistKey);
1167
- const arr = raw ? JSON.parse(raw) : null;
1168
- return Array.isArray(arr) ? arr : null;
1169
- } catch (e) {
1170
- return null;
1171
- }
1172
- });
1173
- const [ref, L] = useLang(lang);
1174
- const t = makeT("DataGrid", L);
1175
- const orderedColumns = React.useMemo(() => {
1176
- if (!colOrder || !colOrder.length) return columns;
1177
- const map = new Map(columns.map((c) => [c.key, c]));
1178
- const out = [];
1179
- for (const k of colOrder) if (map.has(k)) {
1180
- out.push(map.get(k));
1181
- map.delete(k);
1182
- }
1183
- for (const c of map.values()) out.push(c);
1184
- return out;
1185
- }, [columns, colOrder]);
1186
- const filtered = React.useMemo(() => {
1187
- const q = (filterText == null ? "" : String(filterText)).trim().toLowerCase();
1188
- if (!q) return rows;
1189
- const keys = filterKeys && filterKeys.length ? filterKeys : orderedColumns.map((c) => c.key);
1190
- return rows.filter((r) => keys.some((k) => String(r[k] == null ? "" : r[k]).toLowerCase().includes(q)));
1191
- }, [rows, filterText, filterKeys, orderedColumns]);
1192
- const sorted = React.useMemo(() => {
1193
- if (!sort) return filtered;
1194
- const col = orderedColumns.find((c) => c.key === sort.key);
1195
- const val = (r) => col && col.sortValue ? col.sortValue(r) : r[sort.key];
1196
- return [...filtered].sort((a, b) => {
1197
- const x = val(a), y = val(b);
1198
- return (x > y ? 1 : x < y ? -1 : 0) * (sort.dir === "asc" ? 1 : -1);
1199
- });
1200
- }, [filtered, sort, orderedColumns]);
1201
- const useVirtual = virtual || sorted.length >= virtualThreshold;
1202
- const bodyH = Math.max(0, height - 40);
1203
- const start = useVirtual ? Math.max(0, Math.floor(scrollTop / rowHeight) - 4) : 0;
1204
- const visibleCount = useVirtual ? Math.ceil(bodyH / rowHeight) + 8 : sorted.length;
1205
- const end = useVirtual ? Math.min(sorted.length, start + visibleCount) : sorted.length;
1206
- const slice = sorted.slice(start, end);
1207
- const padTop = useVirtual ? start * rowHeight : 0;
1208
- const padBottom = useVirtual ? Math.max(0, (sorted.length - end) * rowHeight) : 0;
1209
- const allSel = selectable && filtered.length && filtered.every((r) => selected.includes(r[rowKey]));
1210
- const toggleAll = () => onSelect && onSelect(allSel ? [] : filtered.map((r) => r[rowKey]));
1211
- const toggle = (k) => onSelect && onSelect(selected.includes(k) ? selected.filter((x) => x !== k) : [...selected, k]);
1212
- const pinColumn = (key) => {
1213
- if (!persistKey) return;
1214
- const keys = orderedColumns.map((c) => c.key);
1215
- const next = [key, ...keys.filter((k) => k !== key)];
1216
- setColOrder(next);
1217
- try {
1218
- localStorage.setItem("cs:datagrid:cols:" + persistKey, JSON.stringify(next));
1219
- } catch (e) {
1220
- }
1221
- };
1222
- return /* @__PURE__ */ React.createElement(
1223
- "div",
1224
- {
1225
- ref,
1226
- className: cx("cs-datagrid", useVirtual && "cs-datagrid--virtual", className),
1227
- style: { maxBlockSize: height, overflow: "auto" },
1228
- onScroll: useVirtual ? (e) => setScrollTop(e.currentTarget.scrollTop) : void 0,
1229
- "data-virtual": useVirtual ? "true" : "false",
1230
- "data-row-count": sorted.length
1231
- },
1232
- /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, selectable ? /* @__PURE__ */ React.createElement("th", { scope: "col", className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectAll"), checked: !!allSel, onChange: toggleAll })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement(
1233
- "th",
1234
- {
1235
- key: c.key,
1236
- scope: "col",
1237
- className: c.pinned ? "cs-datagrid__pinned" : void 0,
1238
- style: c.pinned ? { position: "sticky", insetInlineStart: 0, zIndex: 1, background: "var(--cs-color-surface-panel)" } : void 0,
1239
- "aria-sort": sort && sort.key === c.key ? sort.dir === "asc" ? "ascending" : "descending" : void 0
1240
- },
1241
- c.sortable ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-datagrid__sort", onClick: () => setSort((s) => !s || s.key !== c.key ? { key: c.key, dir: "asc" } : s.dir === "asc" ? { key: c.key, dir: "desc" } : null) }, c.header, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, sort && sort.key === c.key ? sort.dir === "asc" ? " ▲" : " ▼" : " ↕")) : c.header,
1242
- persistKey ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-datagrid__pin", title: "Pin column first", onClick: () => pinColumn(c.key), "aria-label": "Pin " + c.key }, "⟂") : null
1243
- )))), /* @__PURE__ */ React.createElement("tbody", null, sorted.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), className: "cs-table__empty" }, empty != null ? empty : t("empty"))) : /* @__PURE__ */ React.createElement(React.Fragment, null, padTop > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padTop, padding: 0, border: 0 } })) : null, slice.map((r, i) => {
1244
- const k = r[rowKey] != null ? r[rowKey] : start + i;
1245
- return /* @__PURE__ */ React.createElement("tr", { key: k, className: selected.includes(k) ? "is-selected" : void 0, style: useVirtual ? { height: rowHeight } : void 0 }, selectable ? /* @__PURE__ */ React.createElement("td", { className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectRow"), checked: selected.includes(k), onChange: () => toggle(k) })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key, className: c.pinned ? "cs-datagrid__pinned" : void 0, style: c.pinned ? { position: "sticky", insetInlineStart: 0, background: "var(--cs-color-surface-panel)" } : void 0 }, c.render ? c.render(r) : r[c.key])));
1246
- }), padBottom > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padBottom, padding: 0, border: 0 } })) : null)))
1247
- );
1248
- }
1249
- Object.assign(__ds_scope, { DataGrid });
1250
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataGrid.jsx", error: String((e && e.message) || e) }); }
1251
-
1252
- // components/datatable/DataTable.jsx
1253
- try { (() => {
1254
- const { makeT, useLang } = __ds_scope;
1255
- const { cx } = __ds_scope;
1256
- function DataTable({
1257
- caption,
1258
- columns,
1259
- rows,
1260
- rowKey = "id",
1261
- emptyState,
1262
- lang,
1263
- className
1264
- }) {
1265
- const normalized = Array.isArray(rows) ? rows : [];
1266
- const [ref, L] = useLang(lang);
1267
- const es = emptyState != null ? emptyState : makeT("DataTable", L)("empty");
1268
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", className) }, /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, columns.map((c) => /* @__PURE__ */ React.createElement("th", { key: c.key, scope: "col" }, c.header)))), /* @__PURE__ */ React.createElement("tbody", null, normalized.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: columns.length, className: "cs-table__empty" }, es)) : normalized.map((row, i) => /* @__PURE__ */ React.createElement("tr", { key: row[rowKey] ?? i }, columns.map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key }, c.render ? c.render(row) : row[c.key])))))));
1269
- }
1270
- Object.assign(__ds_scope, { DataTable });
1271
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataTable.jsx", error: String((e && e.message) || e) }); }
1272
-
1273
1396
  // components/datatable/TreeTable.jsx
1274
1397
  try { (() => {
1275
1398
  const { makeT, useLang } = __ds_scope;
@@ -1290,6 +1413,7 @@ Object.assign(__ds_scope, { TreeTable });
1290
1413
 
1291
1414
  // components/feedback/Alert.jsx
1292
1415
  try { (() => {
1416
+ const { makeT, useLang } = __ds_scope;
1293
1417
  const { cx } = __ds_scope;
1294
1418
  const PATHS = {
1295
1419
  info: "M12 8h.01M11 12h1v4h1 M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z",
@@ -1300,8 +1424,10 @@ const PATHS = {
1300
1424
  function DefaultIcon({ variant }) {
1301
1425
  return /* @__PURE__ */ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: PATHS[variant] || PATHS.info }));
1302
1426
  }
1303
- function Alert({ variant = "info", title, icon, children, className, ...props }) {
1304
- return /* @__PURE__ */ React.createElement("div", { role: "status", className: cx("cs-alert", `cs-alert--${variant}`, className), ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-alert__icon" }, icon ?? /* @__PURE__ */ React.createElement(DefaultIcon, { variant })), /* @__PURE__ */ React.createElement("div", null, title ? /* @__PURE__ */ React.createElement("p", { className: "cs-alert__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-alert__body" }, children) : null));
1427
+ function Alert({ variant = "info", title, icon, children, onDismiss, lang, className, ...props }) {
1428
+ const [ref, L] = useLang(lang);
1429
+ const t = makeT("Alert", L);
1430
+ return /* @__PURE__ */ React.createElement("div", { ref, role: "status", className: cx("cs-alert", `cs-alert--${variant}`, className), ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-alert__icon" }, icon ?? /* @__PURE__ */ React.createElement(DefaultIcon, { variant })), /* @__PURE__ */ React.createElement("div", null, title ? /* @__PURE__ */ React.createElement("p", { className: "cs-alert__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-alert__body" }, children) : null), onDismiss ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-alert__dismiss", "aria-label": t("dismiss"), onClick: onDismiss }, "×") : null);
1305
1431
  }
1306
1432
  Object.assign(__ds_scope, { Alert });
1307
1433
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Alert.jsx", error: String((e && e.message) || e) }); }
@@ -1317,27 +1443,36 @@ Object.assign(__ds_scope, { Badge });
1317
1443
 
1318
1444
  // components/feedback/EmptyState.jsx
1319
1445
  try { (() => {
1446
+ const { makeT, useLang } = __ds_scope;
1320
1447
  const { cx } = __ds_scope;
1321
- function EmptyState({ icon, title, children, actions, className }) {
1322
- return /* @__PURE__ */ React.createElement("div", { className: cx("cs-empty", className) }, /* @__PURE__ */ React.createElement("span", { className: "cs-empty__icon" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M3 8l2-4h14l2 4M3 8v10a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V8M3 8h6l1 3h4l1-3h6" }))), title ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__body" }, children) : null, actions ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__actions" }, actions) : null);
1448
+ function EmptyState({ icon, title, children, actions, lang, className }) {
1449
+ const [ref, L] = useLang(lang);
1450
+ const t = makeT("EmptyState", L);
1451
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-empty", className) }, /* @__PURE__ */ React.createElement("span", { className: "cs-empty__icon" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "26", height: "26", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M3 8l2-4h14l2 4M3 8v10a1 1 0 0 0 1 1h16a1 1 0 0 0 1-1V8M3 8h6l1 3h4l1-3h6" }))), /* @__PURE__ */ React.createElement("div", { className: "cs-empty__title" }, title ?? t("title")), children ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__body" }, children) : null, actions ? /* @__PURE__ */ React.createElement("div", { className: "cs-empty__actions" }, actions) : null);
1323
1452
  }
1324
1453
  Object.assign(__ds_scope, { EmptyState });
1325
1454
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/EmptyState.jsx", error: String((e && e.message) || e) }); }
1326
1455
 
1327
1456
  // components/feedback/ProgressBar.jsx
1328
1457
  try { (() => {
1458
+ const { makeT, useLang } = __ds_scope;
1329
1459
  const { cx } = __ds_scope;
1330
- function ProgressBar({ value = 0, max = 100, variant, label, className }) {
1331
- const pct = Math.max(0, Math.min(100, value / max * 100));
1460
+ function ProgressBar({ value = 0, max = 100, variant, label, lang, className }) {
1461
+ const safeMax = max > 0 ? max : 100;
1462
+ const clamped = Math.max(0, Math.min(safeMax, Number(value) || 0));
1463
+ const pct = Math.max(0, Math.min(100, clamped / safeMax * 100));
1464
+ const [ref, L] = useLang(lang);
1465
+ const t = makeT("ProgressBar", L);
1332
1466
  return /* @__PURE__ */ React.createElement(
1333
1467
  "div",
1334
1468
  {
1469
+ ref,
1335
1470
  className: cx("cs-progress", variant && `cs-progress--${variant}`, className),
1336
1471
  role: "progressbar",
1337
- "aria-valuenow": Math.round(value),
1472
+ "aria-valuenow": Math.round(clamped),
1338
1473
  "aria-valuemin": 0,
1339
- "aria-valuemax": max,
1340
- "aria-label": label
1474
+ "aria-valuemax": safeMax,
1475
+ "aria-label": label ?? t("label")
1341
1476
  },
1342
1477
  /* @__PURE__ */ React.createElement("span", { className: "cs-progress__fill", style: { inlineSize: pct + "%", width: pct + "%" } })
1343
1478
  );
@@ -1383,6 +1518,41 @@ function Skeleton({ variant = "block", width, height, lines, radius, className,
1383
1518
  Object.assign(__ds_scope, { Skeleton });
1384
1519
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Skeleton.jsx", error: String((e && e.message) || e) }); }
1385
1520
 
1521
+ // components/datatable/DataTable.jsx
1522
+ try { (() => {
1523
+ const { makeT, useLang } = __ds_scope;
1524
+ const { Skeleton } = __ds_scope;
1525
+ const { Result } = __ds_scope;
1526
+ const { cx } = __ds_scope;
1527
+ function DataTable({
1528
+ caption,
1529
+ columns,
1530
+ rows,
1531
+ rowKey = "id",
1532
+ emptyState,
1533
+ state = "idle",
1534
+ errorState,
1535
+ loadingState,
1536
+ loadingRows = 5,
1537
+ lang,
1538
+ className
1539
+ }) {
1540
+ const normalized = Array.isArray(rows) ? rows : [];
1541
+ const [ref, L] = useLang(lang);
1542
+ const t = makeT("DataTable", L);
1543
+ const es = emptyState != null ? emptyState : t("empty");
1544
+ const colCount = Math.max(1, columns && columns.length || 1);
1545
+ if (state === "loading") {
1546
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", className), "aria-busy": "true" }, /* @__PURE__ */ React.createElement("span", { className: "cs-sr-only" }, t("loading")), loadingState != null ? loadingState : /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, (columns || []).map((c) => /* @__PURE__ */ React.createElement("th", { key: c.key, scope: "col" }, c.header)))), /* @__PURE__ */ React.createElement("tbody", { "aria-hidden": "true" }, Array.from({ length: Math.max(1, loadingRows) }).map((_, i) => /* @__PURE__ */ React.createElement("tr", { key: i }, Array.from({ length: colCount }).map((__, j) => /* @__PURE__ */ React.createElement("td", { key: j }, /* @__PURE__ */ React.createElement(Skeleton, { lines: 1 }))))))));
1547
+ }
1548
+ if (state === "error") {
1549
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", "cs-table-wrap--status", className), role: "alert" }, errorState != null ? errorState : /* @__PURE__ */ React.createElement(Result, { status: "error", lang: L, title: t("error") }, t("errorHint")));
1550
+ }
1551
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-table-wrap", className) }, /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, (columns || []).map((c) => /* @__PURE__ */ React.createElement("th", { key: c.key, scope: "col" }, c.header)))), /* @__PURE__ */ React.createElement("tbody", null, normalized.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: colCount, className: "cs-table__empty" }, es)) : normalized.map((row, i) => /* @__PURE__ */ React.createElement("tr", { key: row[rowKey] ?? i }, (columns || []).map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key }, c.render ? c.render(row) : row[c.key])))))));
1552
+ }
1553
+ Object.assign(__ds_scope, { DataTable });
1554
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataTable.jsx", error: String((e && e.message) || e) }); }
1555
+
1386
1556
  // components/feedback/Spinner.jsx
1387
1557
  try { (() => {
1388
1558
  const { makeT, useLang } = __ds_scope;
@@ -1420,13 +1590,21 @@ Object.assign(__ds_scope, { Tag });
1420
1590
  try { (() => {
1421
1591
  const { makeT, useLang } = __ds_scope;
1422
1592
  const { cx } = __ds_scope;
1593
+ const PATHS = {
1594
+ default: "M12 8h.01M11 12h1v4h1 M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z",
1595
+ success: "M4 12.5l5 5 11-11",
1596
+ danger: "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18z M9 9l6 6 M15 9l-6 6"
1597
+ };
1598
+ function DefaultIcon({ variant }) {
1599
+ return /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: PATHS[variant] || PATHS.default }));
1600
+ }
1423
1601
  function ToastStack({ children, lang, className }) {
1424
1602
  const [ref, L] = useLang(lang);
1425
1603
  return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-toast-stack", className), role: "region", "aria-label": makeT("Toast", L)("notifications") }, children);
1426
1604
  }
1427
1605
  function Toast({ variant = "default", title, icon, onClose, lang, children, className, ...props }) {
1428
1606
  const [ref, L] = useLang(lang);
1429
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-toast", `cs-toast--${variant}`, className), role: "status", ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-toast__icon", "aria-hidden": "true" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M4 12.5l5 5 11-11" }))), /* @__PURE__ */ React.createElement("div", null, title ? /* @__PURE__ */ React.createElement("div", { className: "cs-toast__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-toast__body" }, children) : null), onClose ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-toast__close", "aria-label": makeT("Toast", L)("dismiss"), onClick: onClose }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 6l12 12M18 6L6 18" }))) : null);
1607
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-toast", `cs-toast--${variant}`, className), role: "status", ...props }, /* @__PURE__ */ React.createElement("span", { className: "cs-toast__icon", "aria-hidden": "true" }, icon ?? /* @__PURE__ */ React.createElement(DefaultIcon, { variant })), /* @__PURE__ */ React.createElement("div", null, title ? /* @__PURE__ */ React.createElement("div", { className: "cs-toast__title" }, title) : null, children ? /* @__PURE__ */ React.createElement("div", { className: "cs-toast__body" }, children) : null), onClose ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-toast__close", "aria-label": makeT("Toast", L)("dismiss"), onClick: onClose }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 6l12 12M18 6L6 18" }))) : null);
1430
1608
  }
1431
1609
  Object.assign(__ds_scope, { ToastStack, Toast });
1432
1610
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/feedback/Toast.jsx", error: String((e && e.message) || e) }); }
@@ -1475,7 +1653,8 @@ Object.assign(__ds_scope, { Calendar });
1475
1653
  try { (() => {
1476
1654
  const { makeT, useLang } = __ds_scope;
1477
1655
  const { cx } = __ds_scope;
1478
- function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabled = false, lang, className }) {
1656
+ const EMPTY_PATH = [];
1657
+ function Cascader({ nodes = [], value = EMPTY_PATH, onChange, placeholder, label, disabled = false, lang, className }) {
1479
1658
  const [open, setOpen] = React.useState(false);
1480
1659
  const [path, setPath] = React.useState(value);
1481
1660
  const wrap = React.useRef(null);
@@ -1483,24 +1662,60 @@ function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabl
1483
1662
  const [ref, L] = useLang(lang);
1484
1663
  const t = makeT("Cascader", L);
1485
1664
  const ph = placeholder != null ? placeholder : t("placeholder");
1665
+ const valueKey = Array.isArray(value) ? value.join("\0") : "";
1486
1666
  React.useEffect(() => {
1487
- setPath(value);
1488
- }, [open]);
1667
+ setPath(Array.isArray(value) ? value : EMPTY_PATH);
1668
+ }, [valueKey]);
1489
1669
  React.useEffect(() => {
1490
1670
  if (!open) return;
1491
1671
  const d = (e) => {
1492
1672
  if (wrap.current && !wrap.current.contains(e.target)) setOpen(false);
1493
1673
  };
1494
1674
  const k = (e) => {
1495
- if (e.key === "Escape") setOpen(false);
1496
- };
1675
+ if (e.key === "Escape") {
1676
+ setOpen(false);
1677
+ const field = wrap.current && wrap.current.querySelector(".cs-treeselect__field");
1678
+ field && field.focus && field.focus();
1679
+ return;
1680
+ }
1681
+ if (!wrap.current) return;
1682
+ const opts = [...wrap.current.querySelectorAll('[role="option"]')];
1683
+ if (!opts.length) return;
1684
+ const active = document.activeElement;
1685
+ const idx = opts.indexOf(active);
1686
+ if (e.key === "ArrowDown") {
1687
+ e.preventDefault();
1688
+ (opts[idx < 0 ? 0 : Math.min(opts.length - 1, idx + 1)] || opts[0]).focus();
1689
+ } else if (e.key === "ArrowUp") {
1690
+ e.preventDefault();
1691
+ (opts[idx < 0 ? opts.length - 1 : Math.max(0, idx - 1)] || opts[0]).focus();
1692
+ } else if (e.key === "Home") {
1693
+ e.preventDefault();
1694
+ opts[0].focus();
1695
+ } else if (e.key === "End") {
1696
+ e.preventDefault();
1697
+ opts[opts.length - 1].focus();
1698
+ } else if (e.key === "ArrowRight" && idx >= 0) {
1699
+ active.click && active.click();
1700
+ } else if (e.key === "ArrowLeft") {
1701
+ e.preventDefault();
1702
+ if (path.length) {
1703
+ const next = path.slice(0, -1);
1704
+ setPath(next);
1705
+ } else {
1706
+ setOpen(false);
1707
+ const field = wrap.current.querySelector(".cs-treeselect__field");
1708
+ field && field.focus && field.focus();
1709
+ }
1710
+ }
1711
+ };
1497
1712
  document.addEventListener("mousedown", d);
1498
1713
  document.addEventListener("keydown", k);
1499
1714
  return () => {
1500
1715
  document.removeEventListener("mousedown", d);
1501
1716
  document.removeEventListener("keydown", k);
1502
1717
  };
1503
- }, [open]);
1718
+ }, [open, path]);
1504
1719
  const cols = [];
1505
1720
  let level = nodes;
1506
1721
  for (let i = 0; level && level.length; i++) {
@@ -1521,7 +1736,27 @@ function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabl
1521
1736
  return /* @__PURE__ */ React.createElement("div", { ref: (el) => {
1522
1737
  wrap.current = el;
1523
1738
  ref.current = el;
1524
- }, className: cx("cs-cascader", className) }, /* @__PURE__ */ React.createElement("button", { type: "button", id: fieldId, className: "cs-treeselect__field", disabled, "aria-haspopup": "listbox", "aria-expanded": open, "aria-label": label, onClick: () => setOpen((o) => !o) }, /* @__PURE__ */ React.createElement("span", { className: labels.length ? void 0 : "ph" }, labels.length ? labels.join(" / ") : ph), /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "▾")), open ? /* @__PURE__ */ React.createElement("div", { className: "cs-cascader__pop" }, cols.map((col, i) => /* @__PURE__ */ React.createElement("ul", { key: i, role: "listbox", "aria-labelledby": fieldId, className: "cs-cascader__col" }, col.map((n) => (
1739
+ }, className: cx("cs-cascader", className) }, /* @__PURE__ */ React.createElement(
1740
+ "button",
1741
+ {
1742
+ type: "button",
1743
+ id: fieldId,
1744
+ className: "cs-treeselect__field",
1745
+ disabled,
1746
+ "aria-haspopup": "listbox",
1747
+ "aria-expanded": open,
1748
+ "aria-label": label,
1749
+ onClick: () => setOpen((o) => !o),
1750
+ onKeyDown: (e) => {
1751
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
1752
+ e.preventDefault();
1753
+ setOpen(true);
1754
+ }
1755
+ }
1756
+ },
1757
+ /* @__PURE__ */ React.createElement("span", { className: labels.length ? void 0 : "ph" }, labels.length ? labels.join(" / ") : ph),
1758
+ /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, "▾")
1759
+ ), open ? /* @__PURE__ */ React.createElement("div", { className: "cs-cascader__pop" }, cols.map((col, i) => /* @__PURE__ */ React.createElement("ul", { key: i, role: "listbox", "aria-labelledby": fieldId, className: "cs-cascader__col" }, col.map((n) => (
1525
1760
  // role="none" — the <li> wrapper must not sit between listbox and option.
1526
1761
  /* @__PURE__ */ React.createElement("li", { key: n.key, role: "none" }, /* @__PURE__ */ React.createElement(
1527
1762
  "button",
@@ -1530,6 +1765,7 @@ function Cascader({ nodes = [], value = [], onChange, placeholder, label, disabl
1530
1765
  role: "option",
1531
1766
  "aria-selected": path[i] === n.key,
1532
1767
  className: cx("cs-cascader__opt", path[i] === n.key && "on"),
1768
+ "data-cs-focusable": "",
1533
1769
  onClick: () => {
1534
1770
  const next = [...path.slice(0, i), n.key];
1535
1771
  setPath(next);
@@ -1759,8 +1995,24 @@ Object.assign(__ds_scope, { DatePicker });
1759
1995
  try { (() => {
1760
1996
  const { makeT, useLang } = __ds_scope;
1761
1997
  const { cx } = __ds_scope;
1998
+ function sanitizeHtml(html) {
1999
+ if (html == null || html === "") return "";
2000
+ if (typeof document === "undefined") {
2001
+ return String(html).replace(/<script[\s\S]*?>[\s\S]*?<\/script>/gi, "").replace(/on\w+\s*=\s*("[^"]*"|'[^']*'|[^\s>]+)/gi, "").replace(/javascript:/gi, "");
2002
+ }
2003
+ const tpl = document.createElement("template");
2004
+ tpl.innerHTML = String(html);
2005
+ tpl.content.querySelectorAll("script,style,iframe,object,embed,link").forEach((n) => n.remove());
2006
+ tpl.content.querySelectorAll("*").forEach((n) => {
2007
+ [...n.attributes].forEach((a) => {
2008
+ if (/^on/i.test(a.name) || /javascript:/i.test(a.value)) n.removeAttribute(a.name);
2009
+ });
2010
+ });
2011
+ return tpl.innerHTML;
2012
+ }
1762
2013
  function Editor({ defaultValue = "", onChange, minHeight = 120, lang, className }) {
1763
2014
  const box = React.useRef(null);
2015
+ const seeded = React.useRef(false);
1764
2016
  const [ref, L] = useLang(lang);
1765
2017
  const t = makeT("Editor", L);
1766
2018
  const cmd = (c) => {
@@ -1771,6 +2023,11 @@ function Editor({ defaultValue = "", onChange, minHeight = 120, lang, className
1771
2023
  const emit = () => {
1772
2024
  onChange && box.current && onChange(box.current.innerHTML);
1773
2025
  };
2026
+ React.useLayoutEffect(() => {
2027
+ if (!box.current || seeded.current) return;
2028
+ box.current.innerHTML = sanitizeHtml(defaultValue);
2029
+ seeded.current = true;
2030
+ }, [defaultValue]);
1774
2031
  const B = ({ c, label, children }) => /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-toolbar__btn", "aria-label": label, onMouseDown: (e) => {
1775
2032
  e.preventDefault();
1776
2033
  cmd(c);
@@ -1786,8 +2043,7 @@ function Editor({ defaultValue = "", onChange, minHeight = 120, lang, className
1786
2043
  "aria-multiline": "true",
1787
2044
  "aria-label": t("area"),
1788
2045
  style: { minHeight },
1789
- onInput: emit,
1790
- dangerouslySetInnerHTML: { __html: defaultValue }
2046
+ onInput: emit
1791
2047
  }
1792
2048
  ));
1793
2049
  }
@@ -1800,6 +2056,7 @@ const { makeT, useLang } = __ds_scope;
1800
2056
  const { cx } = __ds_scope;
1801
2057
  function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang, className }) {
1802
2058
  const [drag, setDrag] = React.useState(false);
2059
+ const inputRef = React.useRef(null);
1803
2060
  const pick = (files) => {
1804
2061
  if (files && files.length && onFiles) onFiles(Array.from(files));
1805
2062
  };
@@ -1807,8 +2064,9 @@ function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang
1807
2064
  const t = makeT("FileUpload", L);
1808
2065
  const tt = title != null ? title : t("title");
1809
2066
  const hh = hint != null ? hint : t("hint");
2067
+ const id = React.useId();
1810
2068
  return /* @__PURE__ */ React.createElement(
1811
- "label",
2069
+ "div",
1812
2070
  {
1813
2071
  ref,
1814
2072
  className: cx("cs-dropzone", drag && "is-dragging", className),
@@ -1821,12 +2079,26 @@ function FileUpload({ title, hint, accept, multiple = false, onFiles, icon, lang
1821
2079
  e.preventDefault();
1822
2080
  setDrag(false);
1823
2081
  pick(e.dataTransfer.files);
1824
- }
2082
+ },
2083
+ onClick: () => inputRef.current && inputRef.current.click()
1825
2084
  },
1826
- /* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__icon" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: "M12 16V4M7 9l5-5 5 5" }), /* @__PURE__ */ React.createElement("path", { d: "M5 16v3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3" }))),
1827
- /* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__title" }, tt),
1828
- /* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__hint" }, hh),
1829
- /* @__PURE__ */ React.createElement("input", { type: "file", accept, multiple, style: { display: "none" }, onChange: (e) => pick(e.target.files) })
2085
+ /* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__icon", "aria-hidden": "true" }, icon ?? /* @__PURE__ */ React.createElement("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M12 16V4M7 9l5-5 5 5" }), /* @__PURE__ */ React.createElement("path", { d: "M5 16v3a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-3" }))),
2086
+ /* @__PURE__ */ React.createElement("label", { className: "cs-dropzone__title", htmlFor: id }, tt),
2087
+ /* @__PURE__ */ React.createElement("span", { className: "cs-dropzone__hint", id: id + "-hint" }, hh),
2088
+ /* @__PURE__ */ React.createElement(
2089
+ "input",
2090
+ {
2091
+ id,
2092
+ ref: inputRef,
2093
+ type: "file",
2094
+ accept,
2095
+ multiple,
2096
+ className: "cs-sr-only",
2097
+ "aria-describedby": id + "-hint",
2098
+ onChange: (e) => pick(e.target.files),
2099
+ onClick: (e) => e.stopPropagation()
2100
+ }
2101
+ )
1830
2102
  );
1831
2103
  }
1832
2104
  Object.assign(__ds_scope, { FileUpload });
@@ -1873,6 +2145,7 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
1873
2145
  const [values, setValues] = React.useState(() => initialValues || {});
1874
2146
  const [ruleErrors, setRuleErrors] = React.useState({});
1875
2147
  const [pending, setPending] = React.useState(false);
2148
+ const pendingRef = React.useRef(false);
1876
2149
  const setValue = React.useCallback((name, v) => {
1877
2150
  setValues((s) => String(name).includes(".") ? setPath(s, name, v) : { ...s, [name]: v });
1878
2151
  setRuleErrors((e) => e[name] ? { ...e, [name]: void 0 } : e);
@@ -1919,8 +2192,10 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
1919
2192
  ref,
1920
2193
  className: cx("cs-form", pending && "is-pending", className),
1921
2194
  noValidate: true,
2195
+ "aria-busy": pending || void 0,
1922
2196
  onSubmit: async (e) => {
1923
2197
  e.preventDefault();
2198
+ if (pendingRef.current) return;
1924
2199
  const fd = new FormData(e.currentTarget);
1925
2200
  const v = {};
1926
2201
  fd.forEach((val, k) => {
@@ -1932,6 +2207,7 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
1932
2207
  setRuleErrors(errs);
1933
2208
  return;
1934
2209
  }
2210
+ pendingRef.current = true;
1935
2211
  setPending(true);
1936
2212
  try {
1937
2213
  const aerrs = await runAsyncRules(v);
@@ -1939,6 +2215,7 @@ function Form({ onSubmit, errors = {}, rules, asyncRules, initialValues, childre
1939
2215
  if (Object.keys(aerrs).some((k) => aerrs[k])) return;
1940
2216
  onSubmit && onSubmit(v);
1941
2217
  } finally {
2218
+ pendingRef.current = false;
1942
2219
  setPending(false);
1943
2220
  }
1944
2221
  }
@@ -2239,6 +2516,7 @@ Object.assign(__ds_scope, { InputOTP });
2239
2516
  try { (() => {
2240
2517
  const { makeT, useLang } = __ds_scope;
2241
2518
  const { cx } = __ds_scope;
2519
+ let mentionsUid = 0;
2242
2520
  function Mentions({ value, defaultValue = "", onChange, users = [], placeholder, rows = 3, lang, className }) {
2243
2521
  const [inner, setInner] = React.useState(defaultValue);
2244
2522
  const val = value != null ? value : inner;
@@ -2247,23 +2525,80 @@ function Mentions({ value, defaultValue = "", onChange, users = [], placeholder,
2247
2525
  onChange && onChange(v);
2248
2526
  };
2249
2527
  const [q, setQ] = React.useState(null);
2528
+ const [activeIndex, setActiveIndex] = React.useState(0);
2529
+ const [listId] = React.useState(() => "cs-mentions-" + ++mentionsUid);
2250
2530
  const [ref, L] = useLang(lang);
2251
2531
  const t = makeT("Mentions", L);
2252
2532
  const ph = placeholder != null ? placeholder : t("placeholder");
2533
+ const hits = q == null ? [] : users.filter((u) => u.toLowerCase().includes(q)).slice(0, 6);
2534
+ const open = hits.length > 0;
2535
+ React.useEffect(() => {
2536
+ setActiveIndex((i) => hits.length ? Math.min(i, hits.length - 1) : 0);
2537
+ }, [q, users]);
2253
2538
  const onInput = (v) => {
2254
2539
  set(v);
2255
2540
  const m = /(^|\s)@(\w*)$/.exec(v);
2256
- setQ(m ? m[2].toLowerCase() : null);
2541
+ const next = m ? m[2].toLowerCase() : null;
2542
+ setQ(next);
2543
+ if (next != null) setActiveIndex(0);
2257
2544
  };
2258
- const hits = q == null ? [] : users.filter((u) => u.toLowerCase().includes(q)).slice(0, 6);
2259
2545
  const pick = (u) => {
2260
2546
  set(val.replace(/(^|\s)@\w*$/, "$1@" + u + " "));
2261
2547
  setQ(null);
2262
2548
  };
2263
- return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-mentions", className) }, /* @__PURE__ */ React.createElement("textarea", { className: "cs-field__control", rows, value: val, placeholder: ph, onChange: (e) => onInput(e.target.value) }), hits.length ? /* @__PURE__ */ React.createElement("span", { className: "cs-mentions__pop", role: "listbox" }, hits.map((u) => /* @__PURE__ */ React.createElement("button", { key: u, type: "button", role: "option", onMouseDown: (e) => {
2264
- e.preventDefault();
2265
- pick(u);
2266
- } }, "@", u))) : null);
2549
+ const optionId = (i) => listId + "-opt-" + i;
2550
+ const onKeyDown = (e) => {
2551
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
2552
+ if (!open) return;
2553
+ if (e.key === "ArrowDown") {
2554
+ e.preventDefault();
2555
+ setActiveIndex((i) => Math.min(hits.length - 1, i + 1));
2556
+ } else if (e.key === "ArrowUp") {
2557
+ e.preventDefault();
2558
+ setActiveIndex((i) => Math.max(0, i - 1));
2559
+ } else if (e.key === "Enter") {
2560
+ if (hits[activeIndex]) {
2561
+ e.preventDefault();
2562
+ pick(hits[activeIndex]);
2563
+ }
2564
+ } else if (e.key === "Escape") {
2565
+ e.preventDefault();
2566
+ setQ(null);
2567
+ }
2568
+ };
2569
+ return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-mentions", className) }, /* @__PURE__ */ React.createElement(
2570
+ "textarea",
2571
+ {
2572
+ className: "cs-field__control",
2573
+ rows,
2574
+ value: val,
2575
+ placeholder: ph,
2576
+ role: "combobox",
2577
+ "aria-autocomplete": "list",
2578
+ "aria-expanded": open,
2579
+ "aria-controls": listId,
2580
+ "aria-activedescendant": open && hits[activeIndex] ? optionId(activeIndex) : void 0,
2581
+ onChange: (e) => onInput(e.target.value),
2582
+ onKeyDown
2583
+ }
2584
+ ), open ? /* @__PURE__ */ React.createElement("span", { className: "cs-mentions__pop", role: "listbox", id: listId }, hits.map((u, i) => /* @__PURE__ */ React.createElement(
2585
+ "button",
2586
+ {
2587
+ key: u,
2588
+ type: "button",
2589
+ id: optionId(i),
2590
+ role: "option",
2591
+ "aria-selected": i === activeIndex,
2592
+ className: cx(i === activeIndex && "hl"),
2593
+ onMouseEnter: () => setActiveIndex(i),
2594
+ onMouseDown: (e) => {
2595
+ e.preventDefault();
2596
+ pick(u);
2597
+ }
2598
+ },
2599
+ "@",
2600
+ u
2601
+ ))) : null);
2267
2602
  }
2268
2603
  Object.assign(__ds_scope, { Mentions });
2269
2604
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Mentions.jsx", error: String((e && e.message) || e) }); }
@@ -2393,15 +2728,57 @@ const STAR = "M12 2l2.9 6.2 6.6.8-4.9 4.6 1.3 6.5L12 16.9 6.1 20l1.3-6.5L2.5 9l6
2393
2728
  function Rating({ value, defaultValue = 0, onChange, max = 5, readOnly = false, label, lang, className }) {
2394
2729
  const [inner, setInner] = React.useState(defaultValue);
2395
2730
  const val = value != null ? value : inner;
2396
- const set = (n) => {
2731
+ const commit = (n) => {
2397
2732
  if (readOnly) return;
2398
- const v = n === val ? 0 : n;
2399
- if (value == null) setInner(v);
2400
- onChange && onChange(v);
2733
+ if (value == null) setInner(n);
2734
+ onChange && onChange(n);
2401
2735
  };
2736
+ const set = (n) => commit(n === val ? 0 : n);
2402
2737
  const [ref, L] = useLang(lang);
2403
2738
  const lbl = label != null ? label : makeT("Rating", L)("label");
2404
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-rating", className), role: "radiogroup", "aria-label": lbl + ": " + val + " / " + max, "data-readonly": readOnly ? "true" : void 0 }, Array.from({ length: max }).map((_, i) => /* @__PURE__ */ React.createElement("button", { key: i, type: "button", role: "radio", "aria-checked": i < val, "aria-label": i + 1 + " / " + max, className: i < val ? "on" : void 0, onClick: () => set(i + 1), tabIndex: readOnly ? -1 : 0 }, /* @__PURE__ */ React.createElement("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: i < val ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: STAR })))));
2739
+ const refs = React.useRef([]);
2740
+ const focusIdx = val > 0 ? Math.min(max, val) - 1 : 0;
2741
+ const move = (from, delta) => {
2742
+ if (readOnly) return;
2743
+ const next = Math.max(0, Math.min(max - 1, from + delta));
2744
+ commit(next + 1);
2745
+ const b = refs.current[next];
2746
+ if (b) b.focus();
2747
+ };
2748
+ const onKeyDown = (e, i) => {
2749
+ if (readOnly || e.nativeEvent.isComposing || e.keyCode === 229) return;
2750
+ if (e.key === "ArrowRight" || e.key === "ArrowUp") {
2751
+ e.preventDefault();
2752
+ move(i, 1);
2753
+ } else if (e.key === "ArrowLeft" || e.key === "ArrowDown") {
2754
+ e.preventDefault();
2755
+ move(i, -1);
2756
+ } else if (e.key === "Home") {
2757
+ e.preventDefault();
2758
+ move(i, -i);
2759
+ } else if (e.key === "End") {
2760
+ e.preventDefault();
2761
+ move(i, max - 1 - i);
2762
+ }
2763
+ };
2764
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-rating", className), role: "radiogroup", "aria-label": lbl + ": " + val + " / " + max, "data-readonly": readOnly ? "true" : void 0 }, Array.from({ length: max }).map((_, i) => /* @__PURE__ */ React.createElement(
2765
+ "button",
2766
+ {
2767
+ key: i,
2768
+ type: "button",
2769
+ role: "radio",
2770
+ "aria-checked": i < val,
2771
+ "aria-label": i + 1 + " / " + max,
2772
+ className: i < val ? "on" : void 0,
2773
+ onClick: () => set(i + 1),
2774
+ tabIndex: readOnly ? -1 : i === focusIdx ? 0 : -1,
2775
+ ref: (el) => {
2776
+ refs.current[i] = el;
2777
+ },
2778
+ onKeyDown: (e) => onKeyDown(e, i)
2779
+ },
2780
+ /* @__PURE__ */ React.createElement("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: i < val ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: STAR }))
2781
+ )));
2405
2782
  }
2406
2783
  Object.assign(__ds_scope, { Rating });
2407
2784
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Rating.jsx", error: String((e && e.message) || e) }); }
@@ -2464,32 +2841,53 @@ Object.assign(__ds_scope, { SegmentedControl });
2464
2841
 
2465
2842
  // components/forms/Select.jsx
2466
2843
  try { (() => {
2844
+ const { makeT, useLang } = __ds_scope;
2467
2845
  const { cx } = __ds_scope;
2468
- function Select({ id, label, description, error, options, children, disabled = false, className, ...props }) {
2846
+ function Select({ id, label, description, error, options, children, disabled = false, lang, className, placeholder, ...props }) {
2847
+ const [ref, L] = useLang(lang);
2848
+ const t = makeT("Select", L);
2469
2849
  const gid = React.useId();
2470
2850
  const sid = id ?? gid;
2471
2851
  const descId = description ? sid + "-desc" : void 0;
2472
2852
  const errId = error ? sid + "-err" : void 0;
2473
2853
  const describedBy = [descId, errId].filter(Boolean).join(" ") || void 0;
2474
- return /* @__PURE__ */ React.createElement("label", { className: cx("cs-field", disabled && "is-disabled", error && "is-invalid", className), htmlFor: sid }, label ? /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, label) : null, description ? /* @__PURE__ */ React.createElement("span", { id: descId, className: "cs-field__description" }, description) : null, /* @__PURE__ */ React.createElement("span", { className: "cs-select" }, /* @__PURE__ */ React.createElement("select", { ...props, id: sid, disabled, "aria-invalid": error ? true : void 0, "aria-describedby": describedBy, className: "cs-field__control" }, options ? options.map((o) => /* @__PURE__ */ React.createElement("option", { key: o.value, value: o.value }, o.label)) : children), /* @__PURE__ */ React.createElement("span", { className: "cs-select__chevron", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 9l6 6 6-6" })))), error ? /* @__PURE__ */ React.createElement("span", { id: errId, className: "cs-field__error", role: "alert" }, error) : null);
2854
+ return /* @__PURE__ */ React.createElement("label", { ref, className: cx("cs-field", disabled && "is-disabled", error && "is-invalid", className), htmlFor: sid }, label ? /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, label) : null, description ? /* @__PURE__ */ React.createElement("span", { id: descId, className: "cs-field__description" }, description) : null, /* @__PURE__ */ React.createElement("span", { className: "cs-select" }, /* @__PURE__ */ React.createElement("select", { ...props, id: sid, disabled, "aria-invalid": error ? true : void 0, "aria-describedby": describedBy, className: "cs-field__control" }, placeholder != null || !children ? /* @__PURE__ */ React.createElement("option", { value: "", disabled: true, hidden: true }, placeholder ?? t("placeholder")) : null, options ? options.map((o) => /* @__PURE__ */ React.createElement("option", { key: o.value, value: o.value }, o.label)) : children), /* @__PURE__ */ React.createElement("span", { className: "cs-select__chevron", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 9l6 6 6-6" })))), error ? /* @__PURE__ */ React.createElement("span", { id: errId, className: "cs-field__error", role: "alert" }, error) : null);
2475
2855
  }
2476
2856
  Object.assign(__ds_scope, { Select });
2477
2857
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Select.jsx", error: String((e && e.message) || e) }); }
2478
2858
 
2479
2859
  // components/forms/Slider.jsx
2480
2860
  try { (() => {
2861
+ const { makeT, useLang } = __ds_scope;
2481
2862
  const { cx } = __ds_scope;
2482
- function Slider({ className, children, ...props }) {
2483
- return /* @__PURE__ */ React.createElement("input", { type: "range", className: cx("cs-slider", className), ...props });
2863
+ function Slider({ className, children, lang, "aria-label": ariaLabel, ...props }) {
2864
+ const [ref, L] = useLang(lang);
2865
+ const t = makeT("Slider", L);
2866
+ return /* @__PURE__ */ React.createElement("input", { ref, type: "range", className: cx("cs-slider", className), "aria-label": ariaLabel ?? t("label"), ...props });
2484
2867
  }
2485
2868
  Object.assign(__ds_scope, { Slider });
2486
2869
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Slider.jsx", error: String((e && e.message) || e) }); }
2487
2870
 
2488
2871
  // components/forms/Switch.jsx
2489
2872
  try { (() => {
2873
+ const { makeT, useLang } = __ds_scope;
2490
2874
  const { cx } = __ds_scope;
2491
- function Switch({ label, disabled = false, className, children, ...props }) {
2492
- return /* @__PURE__ */ React.createElement("label", { className: cx("cs-switch", disabled && "is-disabled", className) }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", role: "switch", disabled, ...props }), /* @__PURE__ */ React.createElement("span", { className: "cs-switch__track", "aria-hidden": "true" }), label ? /* @__PURE__ */ React.createElement("span", { className: "cs-switch__label" }, label) : null);
2875
+ function Switch({ label, disabled = false, className, children, lang, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledby, ...props }) {
2876
+ const [ref, L] = useLang(lang);
2877
+ const t = makeT("Switch", L);
2878
+ const named = !!(label || ariaLabel || ariaLabelledby);
2879
+ const fallback = props.checked || props.defaultChecked ? t("on") : t("off");
2880
+ return /* @__PURE__ */ React.createElement("label", { ref, className: cx("cs-switch", disabled && "is-disabled", className) }, /* @__PURE__ */ React.createElement(
2881
+ "input",
2882
+ {
2883
+ type: "checkbox",
2884
+ role: "switch",
2885
+ disabled,
2886
+ "aria-label": ariaLabel ?? (named ? void 0 : fallback),
2887
+ "aria-labelledby": ariaLabelledby,
2888
+ ...props
2889
+ }
2890
+ ), /* @__PURE__ */ React.createElement("span", { className: "cs-switch__track", "aria-hidden": "true" }), label ? /* @__PURE__ */ React.createElement("span", { className: "cs-switch__label" }, label) : null);
2493
2891
  }
2494
2892
  Object.assign(__ds_scope, { Switch });
2495
2893
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Switch.jsx", error: String((e && e.message) || e) }); }
@@ -2589,8 +2987,10 @@ const CLOCK = /* @__PURE__ */ React.createElement("svg", { width: "16", height:
2589
2987
  function TimePicker({ value = "09:00", onChange, step = 30, label, disabled = false, lang, className }) {
2590
2988
  const [ref, L] = useLang(lang);
2591
2989
  const t = makeT("TimePicker", L);
2990
+ const stepMin = Number(step);
2991
+ const safeStep = Number.isFinite(stepMin) && stepMin > 0 ? stepMin : 30;
2592
2992
  const opts = [];
2593
- for (let m = 0; m < 24 * 60; m += step) {
2993
+ for (let m = 0; m < 24 * 60; m += safeStep) {
2594
2994
  opts.push(String(Math.floor(m / 60)).padStart(2, "0") + ":" + String(m % 60).padStart(2, "0"));
2595
2995
  }
2596
2996
  return /* @__PURE__ */ React.createElement("span", { ref, className: cx("cs-timepicker", className) }, CLOCK, /* @__PURE__ */ React.createElement("select", { "aria-label": label != null ? label : t("label"), disabled, value, onChange: (e) => onChange && onChange(e.target.value) }, opts.map((o) => /* @__PURE__ */ React.createElement("option", { key: o, value: o }, o))));
@@ -2600,16 +3000,33 @@ Object.assign(__ds_scope, { TimePicker });
2600
3000
 
2601
3001
  // components/forms/Toggle.jsx
2602
3002
  try { (() => {
3003
+ const { makeT, useLang } = __ds_scope;
2603
3004
  const { cx } = __ds_scope;
2604
- function Toggle({ pressed, defaultPressed = false, onChange, icon, children, disabled = false, className, ...props }) {
3005
+ function Toggle({ pressed, defaultPressed = false, onChange, icon, children, disabled = false, lang, className, "aria-label": ariaLabel, ...props }) {
2605
3006
  const [inner, setInner] = React.useState(defaultPressed);
2606
3007
  const on = pressed != null ? pressed : inner;
3008
+ const [ref, L] = useLang(lang);
3009
+ const t = makeT("Toggle", L);
2607
3010
  const flip = () => {
2608
3011
  const v = !on;
2609
3012
  if (pressed == null) setInner(v);
2610
3013
  onChange && onChange(v);
2611
3014
  };
2612
- return /* @__PURE__ */ React.createElement("button", { type: "button", className: cx("cs-toggle", className), "aria-pressed": on, disabled, onClick: flip, ...props }, icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", style: { display: "inline-flex" } }, icon) : null, children);
3015
+ return /* @__PURE__ */ React.createElement(
3016
+ "button",
3017
+ {
3018
+ ref,
3019
+ type: "button",
3020
+ className: cx("cs-toggle", className),
3021
+ "aria-pressed": on,
3022
+ "aria-label": ariaLabel ?? (on ? t("pressed") : t("unpressed")),
3023
+ disabled,
3024
+ onClick: flip,
3025
+ ...props
3026
+ },
3027
+ icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true", style: { display: "inline-flex" } }, icon) : null,
3028
+ children
3029
+ );
2613
3030
  }
2614
3031
  Object.assign(__ds_scope, { Toggle });
2615
3032
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/forms/Toggle.jsx", error: String((e && e.message) || e) }); }
@@ -2730,7 +3147,8 @@ const CS_ICONS = {
2730
3147
  edit: { viewBox: "0 0 24 24", els: [["path", { d: "M4 20h4L18.5 9.5a2.12 2.12 0 0 0-3-3L5 17z" }], ["path", { d: "M13.5 6.5l3 3" }]] },
2731
3148
  copy: { viewBox: "0 0 24 24", els: [["rect", { x: 8, y: 8, width: 12, height: 12, rx: 2 }], ["path", { d: "M4 16V6a2 2 0 0 1 2-2h10" }]] },
2732
3149
  info: { viewBox: "0 0 24 24", els: [["circle", { cx: 12, cy: 12, r: 9 }], ["path", { d: "M12 11v5M12 7.75v.01" }]] },
2733
- "alert-triangle": { viewBox: "0 0 24 24", els: [["path", { d: "M12 3.5l9 16H3z" }], ["path", { d: "M12 10v4M12 17.5v.01" }]] }
3150
+ "alert-triangle": { viewBox: "0 0 24 24", els: [["path", { d: "M12 3.5l9 16H3z" }], ["path", { d: "M12 10v4M12 17.5v.01" }]] },
3151
+ pin: { viewBox: "0 0 24 24", els: [["path", { d: "M12 21v-7" }], ["path", { d: "M8.5 3.5h7l-1.2 6.5h2.7L12 14.5 6.999 10h2.7z" }]] }
2734
3152
  };
2735
3153
  function Icon({ name, size = "md", label, className, strokeWidth = 1.75, ...props }) {
2736
3154
  const def = CS_ICONS[name] || CS_ICONS.sparkle;
@@ -2774,16 +3192,26 @@ function Image({ src, alt = "", ratio, preview = false, fallback, lang, classNam
2774
3192
  document.addEventListener("keydown", k);
2775
3193
  return () => document.removeEventListener("keydown", k);
2776
3194
  }, [zoom]);
3195
+ const openPreview = () => setZoom(true);
3196
+ const onPreviewKey = (e) => {
3197
+ if (e.key === "Enter" || e.key === " ") {
3198
+ e.preventDefault();
3199
+ openPreview();
3200
+ }
3201
+ };
2777
3202
  const body = state === "error" ? /* @__PURE__ */ React.createElement("span", { className: "cs-image__fallback" }, fallback || /* @__PURE__ */ React.createElement("svg", { width: "28", height: "28", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }), /* @__PURE__ */ React.createElement("circle", { cx: "9", cy: "9", r: "2" }), /* @__PURE__ */ React.createElement("path", { d: "M21 15l-5-5-9 9" }))) : /* @__PURE__ */ React.createElement("img", { ...props, src, alt, onLoad: () => setState("ok"), onError: () => setState("error") });
3203
+ const canPreview = preview && state === "ok";
2778
3204
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
2779
3205
  "span",
2780
3206
  {
2781
3207
  ref,
2782
- className: cx("cs-image", state === "loading" && "is-loading", preview && state === "ok" && "is-zoomable", className),
3208
+ className: cx("cs-image", state === "loading" && "is-loading", canPreview && "is-zoomable", className),
2783
3209
  style: ratio ? { aspectRatio: ratio } : void 0,
2784
- onClick: preview && state === "ok" ? () => setZoom(true) : void 0,
2785
- role: preview && state === "ok" ? "button" : void 0,
2786
- "aria-label": preview && state === "ok" ? t("preview") + (alt ? ": " + alt : "") : void 0
3210
+ onClick: canPreview ? openPreview : void 0,
3211
+ onKeyDown: canPreview ? onPreviewKey : void 0,
3212
+ role: canPreview ? "button" : void 0,
3213
+ tabIndex: canPreview ? 0 : void 0,
3214
+ "aria-label": canPreview ? t("preview") + (alt ? ": " + alt : "") : void 0
2787
3215
  },
2788
3216
  body
2789
3217
  ), zoom ? /* @__PURE__ */ React.createElement("span", { className: "cs-image__zoom", role: "dialog", "aria-label": alt || t("preview"), onClick: () => setZoom(false) }, /* @__PURE__ */ React.createElement("img", { src, alt }), /* @__PURE__ */ React.createElement("button", { type: "button", "aria-label": t("close"), onClick: () => setZoom(false) }, /* @__PURE__ */ React.createElement(Icon, { name: "close", size: "sm", style: { verticalAlign: "middle" } }))) : null);
@@ -2791,133 +3219,165 @@ function Image({ src, alt = "", ratio, preview = false, fallback, lang, classNam
2791
3219
  Object.assign(__ds_scope, { Image });
2792
3220
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/data/Image.jsx", error: String((e && e.message) || e) }); }
2793
3221
 
2794
- // components/dialog/Dialog.jsx
3222
+ // components/datatable/DataGrid.jsx
2795
3223
  try { (() => {
2796
3224
  const { makeT, useLang } = __ds_scope;
2797
3225
  const { Icon } = __ds_scope;
2798
3226
  const { cx } = __ds_scope;
2799
- const FOCUSABLE = [
2800
- "a[href]",
2801
- "area[href]",
2802
- "button:not([disabled])",
2803
- 'input:not([disabled]):not([type="hidden"])',
2804
- "select:not([disabled])",
2805
- "textarea:not([disabled])",
2806
- "audio[controls]",
2807
- "video[controls]",
2808
- "summary",
2809
- "iframe",
2810
- '[contenteditable]:not([contenteditable="false"])',
2811
- '[tabindex]:not([tabindex="-1"])'
2812
- ].join(",");
2813
- function Dialog({
2814
- open,
2815
- title,
2816
- children,
2817
- actions,
2818
- onClose,
2819
- className,
2820
- closeLabel,
3227
+ function DataGrid({
3228
+ columns = [],
3229
+ rows = [],
3230
+ rowKey = "id",
3231
+ selectable = false,
3232
+ selected = [],
3233
+ onSelect,
3234
+ height = 280,
3235
+ caption,
3236
+ empty,
2821
3237
  lang,
2822
- ...props
3238
+ className,
3239
+ filterText,
3240
+ filterKeys,
3241
+ /** When true (or rows exceed virtualThreshold), only paint a window of rows. */
3242
+ virtual = false,
3243
+ /** Row threshold that auto-enables virtualization. Default 80. */
3244
+ virtualThreshold = 80,
3245
+ /** Approximate row height for windowing. Default 36. */
3246
+ rowHeight = 36,
3247
+ /** localStorage key — when set, column key order is persisted across reloads. */
3248
+ persistKey
2823
3249
  }) {
2824
- const baseId = React.useId();
2825
- const titleId = baseId + "-title";
2826
- const bodyId = children == null ? void 0 : baseId + "-body";
3250
+ const [sort, setSort] = React.useState(null);
3251
+ const [scrollTop, setScrollTop] = React.useState(0);
3252
+ const [colOrder, setColOrder] = React.useState(() => {
3253
+ if (!persistKey) return null;
3254
+ try {
3255
+ const raw = localStorage.getItem("cs:datagrid:cols:" + persistKey);
3256
+ const arr = raw ? JSON.parse(raw) : null;
3257
+ return Array.isArray(arr) ? arr : null;
3258
+ } catch (e) {
3259
+ return null;
3260
+ }
3261
+ });
2827
3262
  const [ref, L] = useLang(lang);
2828
- const panel = React.useRef(null);
2829
- const closeRef = React.useRef(onClose);
2830
- closeRef.current = onClose;
2831
- React.useEffect(() => {
2832
- if (!open) return;
2833
- const prev = document.activeElement;
2834
- const prevOverflow = document.body.style.overflow;
2835
- document.body.style.overflow = "hidden";
2836
- const focusables = () => panel.current ? [...panel.current.querySelectorAll(FOCUSABLE)] : [];
2837
- const first = focusables()[0];
2838
- (first || panel.current) && (first || panel.current).focus();
2839
- const k = (e) => {
2840
- if (e.key === "Escape") {
2841
- closeRef.current && closeRef.current();
2842
- return;
2843
- }
2844
- if (e.key !== "Tab" || !panel.current) return;
2845
- const f = focusables();
2846
- if (!f.length) {
2847
- e.preventDefault();
2848
- panel.current.focus();
2849
- return;
2850
- }
2851
- const a = f[0], z = f[f.length - 1];
2852
- const active = document.activeElement;
2853
- const inside = panel.current.contains(active);
2854
- if (e.shiftKey && (!inside || active === a)) {
2855
- e.preventDefault();
2856
- z.focus();
2857
- } else if (!e.shiftKey && (!inside || active === z)) {
2858
- e.preventDefault();
2859
- a.focus();
2860
- }
2861
- };
2862
- document.addEventListener("keydown", k);
2863
- return () => {
2864
- document.removeEventListener("keydown", k);
2865
- document.body.style.overflow = prevOverflow;
2866
- if (prev && prev.focus) prev.focus();
2867
- };
2868
- }, [open]);
2869
- const cl = closeLabel != null ? closeLabel : makeT("Dialog", L)("close");
2870
- if (!open) return null;
2871
- return /* @__PURE__ */ React.createElement("div", { ref, className: "cs-dialog-layer" }, /* @__PURE__ */ React.createElement("div", { className: "cs-dialog__overlay", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
2872
- "section",
2873
- {
2874
- ...props,
2875
- ref: panel,
2876
- tabIndex: -1,
2877
- role: "dialog",
2878
- "aria-modal": "true",
2879
- "aria-labelledby": titleId,
2880
- "aria-describedby": bodyId != null ? bodyId : props["aria-describedby"],
2881
- className: cx("cs-dialog", className)
2882
- },
2883
- /* @__PURE__ */ React.createElement("header", { className: "cs-dialog__header" }, /* @__PURE__ */ React.createElement("h2", { id: titleId, className: "cs-dialog__title" }, title), onClose ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--sm", onClick: onClose, "aria-label": cl }, /* @__PURE__ */ React.createElement(Icon, { name: "close", size: "sm" })) : null),
2884
- /* @__PURE__ */ React.createElement("div", { id: bodyId, className: "cs-dialog__body" }, children),
2885
- actions ? /* @__PURE__ */ React.createElement("footer", { className: "cs-dialog__actions" }, actions) : null
2886
- ));
2887
- }
2888
- Object.assign(__ds_scope, { Dialog });
2889
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/dialog/Dialog.jsx", error: String((e && e.message) || e) }); }
2890
-
2891
- // components/logo/logo-data.js
2892
- try { (() => {
2893
- const CS_LOGO_VIEWBOX = "0 0 1007 1007";
2894
- const CS_LOGO_MARK_INNER = `<path fill="#45210E" opacity="1.000000" stroke="none"
2895
- d="
2896
- M560.000000,1008.000000
2897
- C373.333344,1008.000000 187.166672,1008.000000 1.000000,1008.000000
2898
- C1.000000,672.333313 1.000000,336.666656 1.000000,1.000000
2899
- C336.666656,1.000000 672.333313,1.000000 1008.000000,1.000000
2900
- C1008.000000,336.666656 1008.000000,672.333313 1008.000000,1008.000000
2901
- C858.833313,1008.000000 709.666687,1008.000000 560.000000,1008.000000
2902
- M440.743774,105.242386
2903
- C438.572540,107.309639 436.344940,109.321198 434.238464,111.452431
2904
- C396.510132,149.623566 372.276398,194.939011 363.356293,247.940109
2905
- C360.996429,261.961884 358.946777,276.673065 360.603027,290.610352
2906
- C365.030243,327.864777 394.910004,357.905762 431.834900,364.830811
2907
- C436.723083,365.747589 441.737213,365.993011 447.763702,366.669525
2908
- C446.254150,363.858124 445.507843,362.409912 444.709564,360.990906
2909
- C439.517578,351.761383 436.885193,341.740936 437.455444,331.277863
2910
- C439.589569,292.120056 458.815063,264.154144 494.094208,247.402267
2911
- C503.544983,242.914673 513.742554,238.863556 524.373901,240.455948
2912
- C548.543213,244.076111 568.703491,255.555466 584.000793,274.713074
2913
- C599.847656,294.559021 607.550598,317.209198 603.764282,342.677429
2914
- C602.575073,350.677063 598.728027,358.281586 595.936157,366.544159
2915
- C600.137085,366.074554 604.740540,365.758667 609.275574,365.021576
2916
- C646.763123,358.928711 678.394348,326.946808 681.791199,289.350922
2917
- C683.086792,275.012085 681.404419,259.972870 678.550415,245.760666
2918
- C660.577881,156.262833 608.759338,93.666801 525.888672,56.228947
2919
- C522.563171,54.726585 519.736023,54.769550 516.419739,56.200459
2920
- C488.640594,68.186523 463.511292,84.231010 440.743774,105.242386
3263
+ const t = makeT("DataGrid", L);
3264
+ const orderedColumns = React.useMemo(() => {
3265
+ if (!colOrder || !colOrder.length) return columns;
3266
+ const map = new Map(columns.map((c) => [c.key, c]));
3267
+ const out = [];
3268
+ for (const k of colOrder) if (map.has(k)) {
3269
+ out.push(map.get(k));
3270
+ map.delete(k);
3271
+ }
3272
+ for (const c of map.values()) out.push(c);
3273
+ return out;
3274
+ }, [columns, colOrder]);
3275
+ const filtered = React.useMemo(() => {
3276
+ const q = (filterText == null ? "" : String(filterText)).trim().toLowerCase();
3277
+ if (!q) return rows;
3278
+ const keys = filterKeys && filterKeys.length ? filterKeys : orderedColumns.map((c) => c.key);
3279
+ return rows.filter((r) => keys.some((k) => String(r[k] == null ? "" : r[k]).toLowerCase().includes(q)));
3280
+ }, [rows, filterText, filterKeys, orderedColumns]);
3281
+ const sorted = React.useMemo(() => {
3282
+ if (!sort) return filtered;
3283
+ const col = orderedColumns.find((c) => c.key === sort.key);
3284
+ const val = (r) => col && col.sortValue ? col.sortValue(r) : r[sort.key];
3285
+ return [...filtered].sort((a, b) => {
3286
+ const x = val(a), y = val(b);
3287
+ return (x > y ? 1 : x < y ? -1 : 0) * (sort.dir === "asc" ? 1 : -1);
3288
+ });
3289
+ }, [filtered, sort, orderedColumns]);
3290
+ const useVirtual = virtual || sorted.length >= virtualThreshold;
3291
+ const bodyH = Math.max(0, height - 40);
3292
+ const start = useVirtual ? Math.max(0, Math.floor(scrollTop / rowHeight) - 4) : 0;
3293
+ const visibleCount = useVirtual ? Math.ceil(bodyH / rowHeight) + 8 : sorted.length;
3294
+ const end = useVirtual ? Math.min(sorted.length, start + visibleCount) : sorted.length;
3295
+ const slice = sorted.slice(start, end);
3296
+ const padTop = useVirtual ? start * rowHeight : 0;
3297
+ const padBottom = useVirtual ? Math.max(0, (sorted.length - end) * rowHeight) : 0;
3298
+ const allSel = selectable && filtered.length && filtered.every((r) => selected.includes(r[rowKey]));
3299
+ const toggleAll = () => onSelect && onSelect(allSel ? [] : filtered.map((r) => r[rowKey]));
3300
+ const toggle = (k) => onSelect && onSelect(selected.includes(k) ? selected.filter((x) => x !== k) : [...selected, k]);
3301
+ const pinColumn = (key) => {
3302
+ if (!persistKey) return;
3303
+ const keys = orderedColumns.map((c) => c.key);
3304
+ const next = [key, ...keys.filter((k) => k !== key)];
3305
+ setColOrder(next);
3306
+ try {
3307
+ localStorage.setItem("cs:datagrid:cols:" + persistKey, JSON.stringify(next));
3308
+ } catch (e) {
3309
+ }
3310
+ };
3311
+ return /* @__PURE__ */ React.createElement(
3312
+ "div",
3313
+ {
3314
+ ref,
3315
+ className: cx("cs-datagrid", useVirtual && "cs-datagrid--virtual", className),
3316
+ style: { maxBlockSize: height, overflow: "auto" },
3317
+ onScroll: useVirtual ? (e) => setScrollTop(e.currentTarget.scrollTop) : void 0,
3318
+ "data-virtual": useVirtual ? "true" : "false",
3319
+ "data-row-count": sorted.length
3320
+ },
3321
+ /* @__PURE__ */ React.createElement("table", { className: "cs-table" }, caption ? /* @__PURE__ */ React.createElement("caption", null, caption) : null, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, selectable ? /* @__PURE__ */ React.createElement("th", { scope: "col", className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectAll"), checked: !!allSel, onChange: toggleAll })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement(
3322
+ "th",
3323
+ {
3324
+ key: c.key,
3325
+ scope: "col",
3326
+ className: c.pinned ? "cs-datagrid__pinned" : void 0,
3327
+ style: c.pinned ? { position: "sticky", insetInlineStart: 0, zIndex: 1, background: "var(--cs-color-surface-panel)" } : void 0,
3328
+ "aria-sort": sort && sort.key === c.key ? sort.dir === "asc" ? "ascending" : "descending" : void 0
3329
+ },
3330
+ c.sortable ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-datagrid__sort", onClick: () => setSort((s) => !s || s.key !== c.key ? { key: c.key, dir: "asc" } : s.dir === "asc" ? { key: c.key, dir: "desc" } : null) }, c.header, /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, sort && sort.key === c.key ? sort.dir === "asc" ? " ▲" : " ▼" : " ↕")) : c.header,
3331
+ persistKey ? /* @__PURE__ */ React.createElement(
3332
+ "button",
3333
+ {
3334
+ type: "button",
3335
+ className: "cs-datagrid__pin",
3336
+ title: t("pin"),
3337
+ onClick: () => pinColumn(c.key),
3338
+ "aria-label": t("pinNamed").replace("{name}", String(c.header != null ? c.header : c.key))
3339
+ },
3340
+ /* @__PURE__ */ React.createElement(Icon, { name: "pin", size: "sm" })
3341
+ ) : null
3342
+ )))), /* @__PURE__ */ React.createElement("tbody", null, sorted.length === 0 ? /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), className: "cs-table__empty" }, empty != null ? empty : t("empty"))) : /* @__PURE__ */ React.createElement(React.Fragment, null, padTop > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padTop, padding: 0, border: 0 } })) : null, slice.map((r, i) => {
3343
+ const k = r[rowKey] != null ? r[rowKey] : start + i;
3344
+ return /* @__PURE__ */ React.createElement("tr", { key: k, className: selected.includes(k) ? "is-selected" : void 0, style: useVirtual ? { height: rowHeight } : void 0 }, selectable ? /* @__PURE__ */ React.createElement("td", { className: "cs-datagrid__selcol" }, /* @__PURE__ */ React.createElement("input", { type: "checkbox", "aria-label": t("selectRow"), checked: selected.includes(k), onChange: () => toggle(k) })) : null, orderedColumns.map((c) => /* @__PURE__ */ React.createElement("td", { key: c.key, className: c.pinned ? "cs-datagrid__pinned" : void 0, style: c.pinned ? { position: "sticky", insetInlineStart: 0, background: "var(--cs-color-surface-panel)" } : void 0 }, c.render ? c.render(r) : r[c.key])));
3345
+ }), padBottom > 0 ? /* @__PURE__ */ React.createElement("tr", { "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("td", { colSpan: orderedColumns.length + (selectable ? 1 : 0), style: { height: padBottom, padding: 0, border: 0 } })) : null)))
3346
+ );
3347
+ }
3348
+ Object.assign(__ds_scope, { DataGrid });
3349
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/datatable/DataGrid.jsx", error: String((e && e.message) || e) }); }
3350
+
3351
+ // components/logo/logo-data.js
3352
+ try { (() => {
3353
+ const CS_LOGO_VIEWBOX = "0 0 1007 1007";
3354
+ const CS_LOGO_MARK_INNER = `<path fill="#45210E" opacity="1.000000" stroke="none"
3355
+ d="
3356
+ M560.000000,1008.000000
3357
+ C373.333344,1008.000000 187.166672,1008.000000 1.000000,1008.000000
3358
+ C1.000000,672.333313 1.000000,336.666656 1.000000,1.000000
3359
+ C336.666656,1.000000 672.333313,1.000000 1008.000000,1.000000
3360
+ C1008.000000,336.666656 1008.000000,672.333313 1008.000000,1008.000000
3361
+ C858.833313,1008.000000 709.666687,1008.000000 560.000000,1008.000000
3362
+ M440.743774,105.242386
3363
+ C438.572540,107.309639 436.344940,109.321198 434.238464,111.452431
3364
+ C396.510132,149.623566 372.276398,194.939011 363.356293,247.940109
3365
+ C360.996429,261.961884 358.946777,276.673065 360.603027,290.610352
3366
+ C365.030243,327.864777 394.910004,357.905762 431.834900,364.830811
3367
+ C436.723083,365.747589 441.737213,365.993011 447.763702,366.669525
3368
+ C446.254150,363.858124 445.507843,362.409912 444.709564,360.990906
3369
+ C439.517578,351.761383 436.885193,341.740936 437.455444,331.277863
3370
+ C439.589569,292.120056 458.815063,264.154144 494.094208,247.402267
3371
+ C503.544983,242.914673 513.742554,238.863556 524.373901,240.455948
3372
+ C548.543213,244.076111 568.703491,255.555466 584.000793,274.713074
3373
+ C599.847656,294.559021 607.550598,317.209198 603.764282,342.677429
3374
+ C602.575073,350.677063 598.728027,358.281586 595.936157,366.544159
3375
+ C600.137085,366.074554 604.740540,365.758667 609.275574,365.021576
3376
+ C646.763123,358.928711 678.394348,326.946808 681.791199,289.350922
3377
+ C683.086792,275.012085 681.404419,259.972870 678.550415,245.760666
3378
+ C660.577881,156.262833 608.759338,93.666801 525.888672,56.228947
3379
+ C522.563171,54.726585 519.736023,54.769550 516.419739,56.200459
3380
+ C488.640594,68.186523 463.511292,84.231010 440.743774,105.242386
2921
3381
  M286.676636,492.678345
2922
3382
  C287.329773,493.793732 287.835724,495.033447 288.657562,496.006378
2923
3383
  C309.770172,521.000305 334.755707,541.223022 364.045837,555.811401
@@ -3193,37 +3653,6 @@ function Breadcrumb({ items = [], lang, className, ...props }) {
3193
3653
  Object.assign(__ds_scope, { Breadcrumb });
3194
3654
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/Breadcrumb.jsx", error: String((e && e.message) || e) }); }
3195
3655
 
3196
- // components/navigation/CommandPalette.jsx
3197
- try { (() => {
3198
- const { makeT, useLang } = __ds_scope;
3199
- const { cx } = __ds_scope;
3200
- function CommandPalette({ open, onClose, placeholder, groups = [], lang, className }) {
3201
- const [q, setQ] = React.useState("");
3202
- React.useEffect(() => {
3203
- if (!open) return;
3204
- const onKey = (e) => {
3205
- if (e.key === "Escape") onClose && onClose();
3206
- };
3207
- document.addEventListener("keydown", onKey);
3208
- return () => document.removeEventListener("keydown", onKey);
3209
- }, [open]);
3210
- const [ref, L] = useLang(lang);
3211
- const t = makeT("CommandPalette", L);
3212
- const ph = placeholder != null ? placeholder : t("placeholder");
3213
- if (!open) return null;
3214
- const needle = q.trim().toLowerCase();
3215
- return /* @__PURE__ */ React.createElement("div", { ref, className: "cs-cmdk-scrim", onClick: onClose }, /* @__PURE__ */ React.createElement("div", { className: cx("cs-cmdk", className), role: "dialog", "aria-modal": "true", "aria-label": t("aria"), onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__search" }, /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("circle", { cx: "11", cy: "11", r: "7" }), /* @__PURE__ */ React.createElement("path", { d: "M21 21l-4.3-4.3" })), /* @__PURE__ */ React.createElement("input", { autoFocus: true, value: q, onChange: (e) => setQ(e.target.value), placeholder: ph }), /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, t("esc"))), /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__list" }, groups.map((g, gi) => {
3216
- const items = (g.items || []).filter((it) => !needle || String(it.label).toLowerCase().includes(needle));
3217
- if (!items.length) return null;
3218
- return /* @__PURE__ */ React.createElement(React.Fragment, { key: gi }, g.label ? /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__label" }, g.label) : null, items.map((it, ii) => /* @__PURE__ */ React.createElement("button", { key: ii, type: "button", className: "cs-cmdk__item", onClick: () => {
3219
- it.onSelect && it.onSelect();
3220
- onClose && onClose();
3221
- } }, it.icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, it.icon) : null, /* @__PURE__ */ React.createElement("span", null, it.label), it.shortcut ? /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, it.shortcut) : null)));
3222
- }))));
3223
- }
3224
- Object.assign(__ds_scope, { CommandPalette });
3225
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/CommandPalette.jsx", error: String((e && e.message) || e) }); }
3226
-
3227
3656
  // components/navigation/Dock.jsx
3228
3657
  try { (() => {
3229
3658
  const { cx } = __ds_scope;
@@ -3313,21 +3742,51 @@ Object.assign(__ds_scope, { Link });
3313
3742
 
3314
3743
  // components/navigation/Menu.jsx
3315
3744
  try { (() => {
3745
+ const { makeT, useLang } = __ds_scope;
3316
3746
  const { cx } = __ds_scope;
3317
- function Menu({ trigger, children, align = "start", open: controlledOpen, onOpenChange, className }) {
3747
+ function Menu({ trigger, children, align = "start", open: controlledOpen, onOpenChange, lang, className }) {
3318
3748
  const [uOpen, setUOpen] = React.useState(false);
3319
3749
  const open = controlledOpen != null ? controlledOpen : uOpen;
3320
3750
  const set = (v) => {
3321
3751
  onOpenChange ? onOpenChange(v) : setUOpen(v);
3322
3752
  };
3323
- const ref = React.useRef(null);
3753
+ const [ref, L] = useLang(lang);
3754
+ const t = makeT("Menu", L);
3755
+ const triggerRef = React.useRef(null);
3756
+ const listRef = React.useRef(null);
3324
3757
  React.useEffect(() => {
3325
3758
  if (!open) return;
3759
+ const items = () => listRef.current ? [...listRef.current.querySelectorAll('[role="menuitem"]:not([disabled])')] : [];
3760
+ const first = items()[0];
3761
+ if (first && first.focus) first.focus();
3326
3762
  const onDoc = (e) => {
3327
3763
  if (ref.current && !ref.current.contains(e.target)) set(false);
3328
3764
  };
3329
3765
  const onKey = (e) => {
3330
- if (e.key === "Escape") set(false);
3766
+ const opts = items();
3767
+ if (e.key === "Escape") {
3768
+ e.preventDefault();
3769
+ set(false);
3770
+ const el = triggerRef.current;
3771
+ if (el && el.focus) el.focus();
3772
+ return;
3773
+ }
3774
+ if (!opts.length) return;
3775
+ const active = document.activeElement;
3776
+ const idx = opts.indexOf(active);
3777
+ if (e.key === "ArrowDown") {
3778
+ e.preventDefault();
3779
+ (opts[idx < 0 ? 0 : Math.min(opts.length - 1, idx + 1)] || opts[0]).focus();
3780
+ } else if (e.key === "ArrowUp") {
3781
+ e.preventDefault();
3782
+ (opts[idx < 0 ? opts.length - 1 : Math.max(0, idx - 1)] || opts[0]).focus();
3783
+ } else if (e.key === "Home") {
3784
+ e.preventDefault();
3785
+ opts[0].focus();
3786
+ } else if (e.key === "End") {
3787
+ e.preventDefault();
3788
+ opts[opts.length - 1].focus();
3789
+ }
3331
3790
  };
3332
3791
  document.addEventListener("mousedown", onDoc);
3333
3792
  document.addEventListener("keydown", onKey);
@@ -3338,14 +3797,29 @@ function Menu({ trigger, children, align = "start", open: controlledOpen, onOpen
3338
3797
  }, [open]);
3339
3798
  const toggle = () => set(!open);
3340
3799
  const triggerNode = React.isValidElement(trigger) ? React.cloneElement(trigger, {
3800
+ ref: (node) => {
3801
+ triggerRef.current = node;
3802
+ const r = trigger.ref;
3803
+ if (typeof r === "function") r(node);
3804
+ else if (r && typeof r === "object") r.current = node;
3805
+ },
3341
3806
  "aria-haspopup": "menu",
3342
3807
  "aria-expanded": open,
3343
3808
  onClick: (e) => {
3344
3809
  if (typeof trigger.props.onClick === "function") trigger.props.onClick(e);
3345
3810
  if (!e.defaultPrevented) toggle();
3346
3811
  }
3347
- }) : /* @__PURE__ */ React.createElement("button", { type: "button", "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle }, trigger);
3348
- return /* @__PURE__ */ React.createElement("div", { className: cx("cs-menu", className), ref }, triggerNode, open ? /* @__PURE__ */ React.createElement("div", { className: cx("cs-menu__list", align === "end" && "cs-menu__list--end"), role: "menu" }, children) : null);
3812
+ }) : /* @__PURE__ */ React.createElement("button", { type: "button", ref: triggerRef, "aria-haspopup": "menu", "aria-expanded": open, onClick: toggle }, trigger);
3813
+ return /* @__PURE__ */ React.createElement("div", { className: cx("cs-menu", className), ref }, triggerNode, open ? /* @__PURE__ */ React.createElement(
3814
+ "div",
3815
+ {
3816
+ ref: listRef,
3817
+ className: cx("cs-menu__list", align === "end" && "cs-menu__list--end"),
3818
+ role: "menu",
3819
+ "aria-label": t("menu")
3820
+ },
3821
+ children
3822
+ ) : null);
3349
3823
  }
3350
3824
  function MenuItem({ danger = false, icon, children, className, ...props }) {
3351
3825
  return /* @__PURE__ */ React.createElement("button", { type: "button", role: "menuitem", className: cx("cs-menu__item", danger && "cs-menu__item--danger", className), ...props }, icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, icon) : null, children);
@@ -3506,11 +3980,14 @@ Object.assign(__ds_scope, { Sidebar, NavItem });
3506
3980
 
3507
3981
  // components/navigation/Steps.jsx
3508
3982
  try { (() => {
3983
+ const { makeT, useLang } = __ds_scope;
3509
3984
  const { cx } = __ds_scope;
3510
- function Steps({ steps = [], current = 0, className }) {
3511
- return /* @__PURE__ */ React.createElement("div", { className: cx("cs-steps", className) }, steps.map((s, i) => {
3985
+ function Steps({ steps = [], current = 0, lang, className }) {
3986
+ const [ref, L] = useLang(lang);
3987
+ const t = makeT("Steps", L);
3988
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-steps", className), role: "list", "aria-label": t("label") }, steps.map((s, i) => {
3512
3989
  const state = i < current ? "done" : i === current ? "active" : "todo";
3513
- return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("cs-step", `cs-step--${state}`) }, /* @__PURE__ */ React.createElement("span", { className: "cs-step__marker" }, state === "done" ? "✓" : s.n || i + 1), /* @__PURE__ */ React.createElement("span", { className: "cs-step__title" }, s.title), s.body ? /* @__PURE__ */ React.createElement("span", { className: "cs-step__body" }, s.body) : null);
3990
+ return /* @__PURE__ */ React.createElement("div", { key: i, className: cx("cs-step", `cs-step--${state}`), role: "listitem" }, /* @__PURE__ */ React.createElement("span", { className: "cs-step__marker" }, state === "done" ? "✓" : s.n || i + 1), /* @__PURE__ */ React.createElement("span", { className: "cs-step__title" }, s.title), s.body ? /* @__PURE__ */ React.createElement("span", { className: "cs-step__body" }, s.body) : null);
3514
3991
  }));
3515
3992
  }
3516
3993
  Object.assign(__ds_scope, { Steps });
@@ -3518,10 +3995,13 @@ Object.assign(__ds_scope, { Steps });
3518
3995
 
3519
3996
  // components/navigation/Tabs.jsx
3520
3997
  try { (() => {
3998
+ const { makeT, useLang } = __ds_scope;
3521
3999
  const { cx } = __ds_scope;
3522
- function Tabs({ tabs = [], value, onChange, className, ...props }) {
4000
+ function Tabs({ tabs = [], value, onChange, lang, className, "aria-label": ariaLabel, ...props }) {
4001
+ const [ref, L] = useLang(lang);
4002
+ const t = makeT("Tabs", L);
3523
4003
  const refs = React.useRef([]);
3524
- const idx = Math.max(0, tabs.findIndex((t) => t.value === value));
4004
+ const idx = Math.max(0, tabs.findIndex((tab) => tab.value === value));
3525
4005
  const key = (e, i) => {
3526
4006
  let n = null;
3527
4007
  if (e.key === "ArrowRight") n = (i + 1) % tabs.length;
@@ -3534,21 +4014,21 @@ function Tabs({ tabs = [], value, onChange, className, ...props }) {
3534
4014
  const b = refs.current[n];
3535
4015
  if (b) b.focus();
3536
4016
  };
3537
- return /* @__PURE__ */ React.createElement("div", { role: "tablist", className: cx("cs-tabs", className), ...props }, tabs.map((t, i) => /* @__PURE__ */ React.createElement(
4017
+ return /* @__PURE__ */ React.createElement("div", { ref, role: "tablist", "aria-label": ariaLabel ?? t("list"), className: cx("cs-tabs", className), ...props }, tabs.map((t2, i) => /* @__PURE__ */ React.createElement(
3538
4018
  "button",
3539
4019
  {
3540
- key: t.value,
4020
+ key: t2.value,
3541
4021
  type: "button",
3542
4022
  role: "tab",
3543
- "aria-selected": value === t.value,
4023
+ "aria-selected": value === t2.value,
3544
4024
  tabIndex: i === idx ? 0 : -1,
3545
4025
  ref: (el) => refs.current[i] = el,
3546
4026
  onKeyDown: (e) => key(e, i),
3547
4027
  className: "cs-tab",
3548
- onClick: () => onChange && onChange(t.value)
4028
+ onClick: () => onChange && onChange(t2.value)
3549
4029
  },
3550
- t.label,
3551
- t.count != null ? /* @__PURE__ */ React.createElement("span", { className: "cs-tab__count" }, t.count) : null
4030
+ t2.label,
4031
+ t2.count != null ? /* @__PURE__ */ React.createElement("span", { className: "cs-tab__count" }, t2.count) : null
3552
4032
  )));
3553
4033
  }
3554
4034
  function Tab({ selected = false, count, children, className, ...props }) {
@@ -3563,11 +4043,21 @@ const { makeT, useLang } = __ds_scope;
3563
4043
  const { cx } = __ds_scope;
3564
4044
  function Toolbar({ items = [], overflowAfter, label, lang, className }) {
3565
4045
  const [open, setOpen] = React.useState(false);
4046
+ const [focusIdx, setFocusIdx] = React.useState(0);
3566
4047
  const wrap = React.useRef(null);
4048
+ const btnRefs = React.useRef([]);
4049
+ const menuRefs = React.useRef([]);
3567
4050
  const [ref, L] = useLang(lang);
3568
4051
  const t = makeT("Toolbar", L);
3569
4052
  const cut = overflowAfter != null ? overflowAfter : items.length;
3570
- const head = items.slice(0, cut), tail = items.filter((x) => x !== "-").slice(cut);
4053
+ const head = items.slice(0, cut);
4054
+ const tail = items.slice(cut).filter((x) => x !== "-");
4055
+ const hasMore = tail.length > 0;
4056
+ const controlMeta = [];
4057
+ head.forEach((it, i) => {
4058
+ if (it !== "-") controlMeta.push({ kind: "head", headIndex: i });
4059
+ });
4060
+ if (hasMore) controlMeta.push({ kind: "more" });
3571
4061
  React.useEffect(() => {
3572
4062
  if (!open) return;
3573
4063
  const d = (e) => {
@@ -3576,22 +4066,210 @@ function Toolbar({ items = [], overflowAfter, label, lang, className }) {
3576
4066
  document.addEventListener("mousedown", d);
3577
4067
  return () => document.removeEventListener("mousedown", d);
3578
4068
  }, [open]);
4069
+ React.useEffect(() => {
4070
+ if (focusIdx >= controlMeta.length) setFocusIdx(Math.max(0, controlMeta.length - 1));
4071
+ }, [focusIdx, controlMeta.length]);
4072
+ const focusControl = (i) => {
4073
+ const next = Math.max(0, Math.min(controlMeta.length - 1, i));
4074
+ setFocusIdx(next);
4075
+ requestAnimationFrame(() => {
4076
+ const el = btnRefs.current[next];
4077
+ if (el) el.focus();
4078
+ });
4079
+ };
4080
+ const onToolbarKeyDown = (e, i) => {
4081
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
4082
+ if (e.key === "ArrowRight") {
4083
+ e.preventDefault();
4084
+ focusControl(i + 1);
4085
+ } else if (e.key === "ArrowLeft") {
4086
+ e.preventDefault();
4087
+ focusControl(i - 1);
4088
+ } else if (e.key === "Home") {
4089
+ e.preventDefault();
4090
+ focusControl(0);
4091
+ } else if (e.key === "End") {
4092
+ e.preventDefault();
4093
+ focusControl(controlMeta.length - 1);
4094
+ } else if (e.key === "Escape" && open) {
4095
+ e.preventDefault();
4096
+ setOpen(false);
4097
+ }
4098
+ };
4099
+ const onMenuKeyDown = (e, i) => {
4100
+ if (e.nativeEvent.isComposing || e.keyCode === 229) return;
4101
+ if (e.key === "ArrowDown") {
4102
+ e.preventDefault();
4103
+ const n = Math.min(tail.length - 1, i + 1);
4104
+ const el = menuRefs.current[n];
4105
+ if (el) el.focus();
4106
+ } else if (e.key === "ArrowUp") {
4107
+ e.preventDefault();
4108
+ const n = Math.max(0, i - 1);
4109
+ const el = menuRefs.current[n];
4110
+ if (el) el.focus();
4111
+ } else if (e.key === "Escape") {
4112
+ e.preventDefault();
4113
+ setOpen(false);
4114
+ const moreIdx = controlMeta.findIndex((c) => c.kind === "more");
4115
+ if (moreIdx >= 0) focusControl(moreIdx);
4116
+ } else if (e.key === "Home") {
4117
+ e.preventDefault();
4118
+ const el = menuRefs.current[0];
4119
+ if (el) el.focus();
4120
+ } else if (e.key === "End") {
4121
+ e.preventDefault();
4122
+ const el = menuRefs.current[tail.length - 1];
4123
+ if (el) el.focus();
4124
+ }
4125
+ };
4126
+ let controlI = 0;
3579
4127
  return /* @__PURE__ */ React.createElement("div", { ref: (el) => {
3580
4128
  wrap.current = el;
3581
4129
  ref.current = el;
3582
- }, className: cx("cs-toolbar", className), role: "toolbar", "aria-label": label }, head.map((it, i) => it === "-" ? /* @__PURE__ */ React.createElement("span", { key: i, className: "cs-toolbar__sep", "aria-hidden": "true" }) : /* @__PURE__ */ React.createElement("button", { key: i, type: "button", className: "cs-toolbar__btn", onClick: () => it.onSelect && it.onSelect() }, it.icon, it.label ? /* @__PURE__ */ React.createElement("span", null, it.label) : null)), tail.length ? /* @__PURE__ */ React.createElement("span", { className: "cs-toolbar__more" }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-toolbar__btn", "aria-haspopup": "menu", "aria-expanded": open, "aria-label": t("more"), onClick: () => setOpen((o) => !o) }, "⋯"), open ? /* @__PURE__ */ React.createElement("span", { className: "cs-menu__list", role: "menu" }, tail.map((it, i) => /* @__PURE__ */ React.createElement("button", { key: i, type: "button", role: "menuitem", className: "cs-menu__item", onClick: () => {
3583
- setOpen(false);
3584
- it.onSelect && it.onSelect();
3585
- } }, it.label))) : null) : null);
4130
+ }, className: cx("cs-toolbar", className), role: "toolbar", "aria-label": label, "aria-orientation": "horizontal" }, head.map((it, i) => {
4131
+ if (it === "-") return /* @__PURE__ */ React.createElement("span", { key: "sep-" + i, className: "cs-toolbar__sep", "aria-hidden": "true" });
4132
+ const ci = controlI++;
4133
+ return /* @__PURE__ */ React.createElement(
4134
+ "button",
4135
+ {
4136
+ key: "btn-" + i,
4137
+ type: "button",
4138
+ className: "cs-toolbar__btn",
4139
+ tabIndex: ci === focusIdx ? 0 : -1,
4140
+ ref: (el) => {
4141
+ btnRefs.current[ci] = el;
4142
+ },
4143
+ onFocus: () => setFocusIdx(ci),
4144
+ onKeyDown: (e) => onToolbarKeyDown(e, ci),
4145
+ onClick: () => it.onSelect && it.onSelect()
4146
+ },
4147
+ it.icon,
4148
+ it.label ? /* @__PURE__ */ React.createElement("span", null, it.label) : null
4149
+ );
4150
+ }), hasMore ? (() => {
4151
+ const ci = controlI++;
4152
+ return /* @__PURE__ */ React.createElement("span", { className: "cs-toolbar__more" }, /* @__PURE__ */ React.createElement(
4153
+ "button",
4154
+ {
4155
+ type: "button",
4156
+ className: "cs-toolbar__btn",
4157
+ "aria-haspopup": "menu",
4158
+ "aria-expanded": open,
4159
+ "aria-label": t("more"),
4160
+ tabIndex: ci === focusIdx ? 0 : -1,
4161
+ ref: (el) => {
4162
+ btnRefs.current[ci] = el;
4163
+ },
4164
+ onFocus: () => setFocusIdx(ci),
4165
+ onKeyDown: (e) => {
4166
+ onToolbarKeyDown(e, ci);
4167
+ if (e.key === "ArrowDown" || e.key === "Enter" || e.key === " ") {
4168
+ if (!open) {
4169
+ e.preventDefault();
4170
+ setOpen(true);
4171
+ requestAnimationFrame(() => {
4172
+ const el = menuRefs.current[0];
4173
+ if (el) el.focus();
4174
+ });
4175
+ }
4176
+ }
4177
+ },
4178
+ onClick: () => setOpen((o) => !o)
4179
+ },
4180
+ "⋯"
4181
+ ), open ? /* @__PURE__ */ React.createElement("span", { className: "cs-menu__list", role: "menu" }, tail.map((it, i) => /* @__PURE__ */ React.createElement(
4182
+ "button",
4183
+ {
4184
+ key: i,
4185
+ type: "button",
4186
+ role: "menuitem",
4187
+ className: "cs-menu__item",
4188
+ ref: (el) => {
4189
+ menuRefs.current[i] = el;
4190
+ },
4191
+ onKeyDown: (e) => onMenuKeyDown(e, i),
4192
+ onClick: () => {
4193
+ setOpen(false);
4194
+ it.onSelect && it.onSelect();
4195
+ }
4196
+ },
4197
+ it.label
4198
+ ))) : null);
4199
+ })() : null);
3586
4200
  }
3587
4201
  Object.assign(__ds_scope, { Toolbar });
3588
4202
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/Toolbar.jsx", error: String((e && e.message) || e) }); }
3589
4203
 
3590
- // components/overlays/AlertDialog.jsx
4204
+ // components/overlays/ContextMenu.jsx
3591
4205
  try { (() => {
3592
4206
  const { makeT, useLang } = __ds_scope;
3593
4207
  const { cx } = __ds_scope;
3594
- const FOCUSABLE = [
4208
+ function ContextMenu({ items = [], children, lang, className }) {
4209
+ const [pos, setPos] = React.useState(null);
4210
+ const [ref, L] = useLang(lang);
4211
+ const t = makeT("ContextMenu", L);
4212
+ React.useEffect(() => {
4213
+ if (!pos) return;
4214
+ const close = () => setPos(null);
4215
+ const k = (e) => {
4216
+ if (e.key === "Escape") setPos(null);
4217
+ };
4218
+ document.addEventListener("click", close);
4219
+ document.addEventListener("keydown", k);
4220
+ return () => {
4221
+ document.removeEventListener("click", close);
4222
+ document.removeEventListener("keydown", k);
4223
+ };
4224
+ }, [pos]);
4225
+ return /* @__PURE__ */ React.createElement(
4226
+ "div",
4227
+ {
4228
+ ref,
4229
+ className: cx("cs-ctxmenu-zone", className),
4230
+ onContextMenu: (e) => {
4231
+ e.preventDefault();
4232
+ const r = e.currentTarget.getBoundingClientRect();
4233
+ setPos({ x: e.clientX - r.left, y: e.clientY - r.top });
4234
+ }
4235
+ },
4236
+ children,
4237
+ pos ? /* @__PURE__ */ React.createElement("div", { className: "cs-menu__list", role: "menu", "aria-label": t("menu"), style: { position: "absolute", insetInlineStart: pos.x, insetBlockStart: pos.y } }, items.map((it, i) => it === "-" ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-menu__sep" }) : /* @__PURE__ */ React.createElement("button", { key: i, type: "button", role: "menuitem", className: cx("cs-menu__item", it.danger && "cs-menu__item--danger"), onClick: () => {
4238
+ setPos(null);
4239
+ it.onSelect && it.onSelect();
4240
+ } }, it.label))) : null
4241
+ );
4242
+ }
4243
+ Object.assign(__ds_scope, { ContextMenu });
4244
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/ContextMenu.jsx", error: String((e && e.message) || e) }); }
4245
+
4246
+ // components/overlays/HoverCard.jsx
4247
+ try { (() => {
4248
+ const { cx } = __ds_scope;
4249
+ function HoverCard({ trigger, children, openDelay = 150, closeDelay = 200, className }) {
4250
+ const [open, setOpen] = React.useState(false);
4251
+ const t1 = React.useRef();
4252
+ const t2 = React.useRef();
4253
+ const show = () => {
4254
+ clearTimeout(t2.current);
4255
+ t1.current = setTimeout(() => setOpen(true), openDelay);
4256
+ };
4257
+ const hide = () => {
4258
+ clearTimeout(t1.current);
4259
+ t2.current = setTimeout(() => setOpen(false), closeDelay);
4260
+ };
4261
+ React.useEffect(() => () => {
4262
+ clearTimeout(t1.current);
4263
+ clearTimeout(t2.current);
4264
+ }, []);
4265
+ return /* @__PURE__ */ React.createElement("span", { className: cx("cs-hovercard", className), onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide }, trigger, open ? /* @__PURE__ */ React.createElement("span", { className: "cs-hovercard__panel", role: "dialog" }, children) : null);
4266
+ }
4267
+ Object.assign(__ds_scope, { HoverCard });
4268
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/HoverCard.jsx", error: String((e && e.message) || e) }); }
4269
+
4270
+ // components/overlays/OverlayManager.jsx
4271
+ try { (() => {
4272
+ const focusableSelector = [
3595
4273
  "a[href]",
3596
4274
  "area[href]",
3597
4275
  "button:not([disabled])",
@@ -3605,6 +4283,270 @@ const FOCUSABLE = [
3605
4283
  '[contenteditable]:not([contenteditable="false"])',
3606
4284
  '[tabindex]:not([tabindex="-1"])'
3607
4285
  ].join(",");
4286
+ function attachFocusTrap(panelEl, { handleEscape = false, onEscape } = {}) {
4287
+ if (!panelEl) return () => {
4288
+ };
4289
+ const focusables = () => [...panelEl.querySelectorAll(focusableSelector)];
4290
+ const k = (e) => {
4291
+ if (handleEscape && e.key === "Escape") {
4292
+ onEscape && onEscape();
4293
+ return;
4294
+ }
4295
+ if (e.key !== "Tab") return;
4296
+ const f = focusables();
4297
+ if (!f.length) {
4298
+ e.preventDefault();
4299
+ panelEl.focus();
4300
+ return;
4301
+ }
4302
+ const a = f[0];
4303
+ const z = f[f.length - 1];
4304
+ const active = document.activeElement;
4305
+ const inside = panelEl.contains(active);
4306
+ if (e.shiftKey && (!inside || active === a)) {
4307
+ e.preventDefault();
4308
+ z.focus();
4309
+ } else if (!e.shiftKey && (!inside || active === z)) {
4310
+ e.preventDefault();
4311
+ a.focus();
4312
+ }
4313
+ };
4314
+ document.addEventListener("keydown", k);
4315
+ return () => document.removeEventListener("keydown", k);
4316
+ }
4317
+ function createOverlayManager() {
4318
+ const layers = [];
4319
+ let prevOverflow = "";
4320
+ let locked = false;
4321
+ let escapeBound = false;
4322
+ const resort = () => {
4323
+ layers.sort((a, b) => {
4324
+ const pa = a.panelEl;
4325
+ const pb = b.panelEl;
4326
+ if (pa && pb && pa !== pb) {
4327
+ if (pa.contains(pb)) return -1;
4328
+ if (pb.contains(pa)) return 1;
4329
+ }
4330
+ return (a.seq || 0) - (b.seq || 0);
4331
+ });
4332
+ };
4333
+ const applyScroll = () => {
4334
+ const need = layers.some((l) => l.kind === "modal" && l.lockScroll !== false);
4335
+ if (need && !locked) {
4336
+ prevOverflow = document.body.style.overflow;
4337
+ document.body.style.overflow = "hidden";
4338
+ locked = true;
4339
+ } else if (!need && locked) {
4340
+ document.body.style.overflow = prevOverflow;
4341
+ locked = false;
4342
+ }
4343
+ };
4344
+ const onDocEscape = (e) => {
4345
+ if (e.key !== "Escape") return;
4346
+ if (!layers.length) return;
4347
+ resort();
4348
+ const top = layers[layers.length - 1];
4349
+ if (!top || typeof top.onEscape !== "function") return;
4350
+ e.preventDefault();
4351
+ e.stopPropagation();
4352
+ top.onEscape();
4353
+ };
4354
+ const ensureEscape = () => {
4355
+ if (escapeBound) return;
4356
+ escapeBound = true;
4357
+ document.addEventListener("keydown", onDocEscape, true);
4358
+ };
4359
+ let seq = 0;
4360
+ return {
4361
+ register(layer) {
4362
+ ensureEscape();
4363
+ const id = layer.id || `cs-ov-${++seq}`;
4364
+ const entry = { ...layer, id, seq: ++seq };
4365
+ layers.push(entry);
4366
+ resort();
4367
+ applyScroll();
4368
+ return () => {
4369
+ resort();
4370
+ const i = layers.findIndex((l) => l.id === id);
4371
+ const wasTop = i === layers.length - 1;
4372
+ if (i >= 0) layers.splice(i, 1);
4373
+ applyScroll();
4374
+ if (wasTop && entry.restoreEl && typeof entry.restoreEl.focus === "function") {
4375
+ try {
4376
+ entry.restoreEl.focus();
4377
+ } catch {
4378
+ }
4379
+ }
4380
+ };
4381
+ },
4382
+ top() {
4383
+ resort();
4384
+ return layers[layers.length - 1] || null;
4385
+ },
4386
+ get scrollLocked() {
4387
+ return locked;
4388
+ },
4389
+ get depth() {
4390
+ return layers.length;
4391
+ }
4392
+ };
4393
+ }
4394
+ const defaultManager = createOverlayManager();
4395
+ const OverlayContext = React.createContext(null);
4396
+ function getOverlayManager() {
4397
+ return defaultManager;
4398
+ }
4399
+ function OverlayProvider({ children }) {
4400
+ const api = React.useMemo(() => createOverlayManager(), []);
4401
+ return /* @__PURE__ */ React.createElement(OverlayContext.Provider, { value: api }, /* @__PURE__ */ React.createElement("div", { id: "cs-overlay-root", "data-cs-overlay-root": "" }), children);
4402
+ }
4403
+ function useOverlayLayer({
4404
+ open,
4405
+ kind = "modal",
4406
+ trapFocus = false,
4407
+ lockScroll,
4408
+ onEscape,
4409
+ panelRef,
4410
+ preferFocusSelector
4411
+ }) {
4412
+ const ctx = React.useContext(OverlayContext);
4413
+ const mgr = ctx || defaultManager;
4414
+ const escapeRef = React.useRef(onEscape);
4415
+ escapeRef.current = onEscape;
4416
+ React.useLayoutEffect(() => {
4417
+ if (!open) return void 0;
4418
+ const restoreEl = typeof document !== "undefined" ? document.activeElement : null;
4419
+ const panel = panelRef && panelRef.current;
4420
+ const unregister = mgr.register({
4421
+ kind,
4422
+ lockScroll: lockScroll != null ? lockScroll : kind === "modal",
4423
+ onEscape: () => escapeRef.current && escapeRef.current(),
4424
+ restoreEl,
4425
+ panelEl: panel || null
4426
+ });
4427
+ let detachTrap = () => {
4428
+ };
4429
+ const top = mgr.top();
4430
+ const isTop = top && top.panelEl === panel;
4431
+ if (trapFocus && panel && isTop) {
4432
+ const preferred = preferFocusSelector && panel.querySelector(preferFocusSelector) || panel.querySelector(focusableSelector) || panel;
4433
+ preferred && preferred.focus && preferred.focus();
4434
+ detachTrap = attachFocusTrap(panel, { handleEscape: false });
4435
+ }
4436
+ return () => {
4437
+ detachTrap();
4438
+ unregister();
4439
+ };
4440
+ }, [open, kind, trapFocus, lockScroll, mgr, panelRef, preferFocusSelector]);
4441
+ return { manager: mgr };
4442
+ }
4443
+ Object.assign(__ds_scope, { focusableSelector, attachFocusTrap, getOverlayManager, OverlayProvider, useOverlayLayer });
4444
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/OverlayManager.jsx", error: String((e && e.message) || e) }); }
4445
+
4446
+ // components/dialog/Dialog.jsx
4447
+ try { (() => {
4448
+ const { makeT, useLang } = __ds_scope;
4449
+ const { Icon } = __ds_scope;
4450
+ const { cx } = __ds_scope;
4451
+ const { useOverlayLayer } = __ds_scope;
4452
+ function Dialog({
4453
+ open,
4454
+ title,
4455
+ children,
4456
+ actions,
4457
+ onClose,
4458
+ className,
4459
+ closeLabel,
4460
+ lang,
4461
+ ...props
4462
+ }) {
4463
+ const baseId = React.useId();
4464
+ const titleId = baseId + "-title";
4465
+ const bodyId = children == null ? void 0 : baseId + "-body";
4466
+ const [ref, L] = useLang(lang);
4467
+ const panel = React.useRef(null);
4468
+ const closeRef = React.useRef(onClose);
4469
+ closeRef.current = onClose;
4470
+ useOverlayLayer({
4471
+ open: !!open,
4472
+ kind: "modal",
4473
+ trapFocus: true,
4474
+ onEscape: () => closeRef.current && closeRef.current(),
4475
+ panelRef: panel
4476
+ });
4477
+ const cl = closeLabel != null ? closeLabel : makeT("Dialog", L)("close");
4478
+ if (!open) return null;
4479
+ return /* @__PURE__ */ React.createElement("div", { ref, className: "cs-dialog-layer" }, /* @__PURE__ */ React.createElement("div", { className: "cs-dialog__overlay", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
4480
+ "section",
4481
+ {
4482
+ ...props,
4483
+ ref: panel,
4484
+ tabIndex: -1,
4485
+ role: "dialog",
4486
+ "aria-modal": "true",
4487
+ "aria-labelledby": titleId,
4488
+ "aria-describedby": bodyId != null ? bodyId : props["aria-describedby"],
4489
+ className: cx("cs-dialog", className)
4490
+ },
4491
+ /* @__PURE__ */ React.createElement("header", { className: "cs-dialog__header" }, /* @__PURE__ */ React.createElement("h2", { id: titleId, className: "cs-dialog__title" }, title), onClose ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--sm", onClick: onClose, "aria-label": cl }, /* @__PURE__ */ React.createElement(Icon, { name: "close", size: "sm" })) : null),
4492
+ /* @__PURE__ */ React.createElement("div", { id: bodyId, className: "cs-dialog__body" }, children),
4493
+ actions ? /* @__PURE__ */ React.createElement("footer", { className: "cs-dialog__actions" }, actions) : null
4494
+ ));
4495
+ }
4496
+ Object.assign(__ds_scope, { Dialog });
4497
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/dialog/Dialog.jsx", error: String((e && e.message) || e) }); }
4498
+
4499
+ // components/navigation/CommandPalette.jsx
4500
+ try { (() => {
4501
+ const { makeT, useLang } = __ds_scope;
4502
+ const { cx } = __ds_scope;
4503
+ const { useOverlayLayer } = __ds_scope;
4504
+ function CommandPalette({ open, onClose, placeholder, groups = [], lang, className }) {
4505
+ const [q, setQ] = React.useState("");
4506
+ const panel = React.useRef(null);
4507
+ const closeRef = React.useRef(onClose);
4508
+ closeRef.current = onClose;
4509
+ useOverlayLayer({
4510
+ open: !!open,
4511
+ kind: "modal",
4512
+ trapFocus: true,
4513
+ preferFocusSelector: ".cs-cmdk__search input",
4514
+ onEscape: () => closeRef.current && closeRef.current(),
4515
+ panelRef: panel
4516
+ });
4517
+ const [ref, L] = useLang(lang);
4518
+ const t = makeT("CommandPalette", L);
4519
+ const ph = placeholder != null ? placeholder : t("placeholder");
4520
+ if (!open) return null;
4521
+ const needle = q.trim().toLowerCase();
4522
+ return /* @__PURE__ */ React.createElement(
4523
+ "div",
4524
+ {
4525
+ ref: (el) => {
4526
+ ref.current = el;
4527
+ panel.current = el;
4528
+ },
4529
+ className: "cs-cmdk-scrim",
4530
+ onClick: onClose
4531
+ },
4532
+ /* @__PURE__ */ React.createElement("div", { className: cx("cs-cmdk", className), role: "dialog", "aria-modal": "true", "aria-label": t("aria"), onClick: (e) => e.stopPropagation() }, /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__search" }, /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("circle", { cx: "11", cy: "11", r: "7" }), /* @__PURE__ */ React.createElement("path", { d: "M21 21l-4.3-4.3" })), /* @__PURE__ */ React.createElement("input", { autoFocus: true, value: q, onChange: (e) => setQ(e.target.value), placeholder: ph }), /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, t("esc"))), /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__list" }, groups.map((g, gi) => {
4533
+ const items = (g.items || []).filter((it) => !needle || String(it.label).toLowerCase().includes(needle));
4534
+ if (!items.length) return null;
4535
+ return /* @__PURE__ */ React.createElement(React.Fragment, { key: gi }, g.label ? /* @__PURE__ */ React.createElement("div", { className: "cs-cmdk__label" }, g.label) : null, items.map((it, ii) => /* @__PURE__ */ React.createElement("button", { key: ii, type: "button", className: "cs-cmdk__item", onClick: () => {
4536
+ it.onSelect && it.onSelect();
4537
+ onClose && onClose();
4538
+ } }, it.icon ? /* @__PURE__ */ React.createElement("span", { "aria-hidden": "true" }, it.icon) : null, /* @__PURE__ */ React.createElement("span", null, it.label), it.shortcut ? /* @__PURE__ */ React.createElement("span", { className: "cs-kbd" }, it.shortcut) : null)));
4539
+ })))
4540
+ );
4541
+ }
4542
+ Object.assign(__ds_scope, { CommandPalette });
4543
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/navigation/CommandPalette.jsx", error: String((e && e.message) || e) }); }
4544
+
4545
+ // components/overlays/AlertDialog.jsx
4546
+ try { (() => {
4547
+ const { makeT, useLang } = __ds_scope;
4548
+ const { cx } = __ds_scope;
4549
+ const { useOverlayLayer } = __ds_scope;
3608
4550
  function AlertDialog({
3609
4551
  open,
3610
4552
  defaultOpen = false,
@@ -3640,45 +4582,19 @@ function AlertDialog({
3640
4582
  confirmRef.current = onConfirm;
3641
4583
  const cancelRef = React.useRef(onCancel);
3642
4584
  cancelRef.current = onCancel;
3643
- React.useEffect(() => {
3644
- if (!isOpen) return;
3645
- const prev = document.activeElement;
3646
- const prevOverflow = document.body.style.overflow;
3647
- document.body.style.overflow = "hidden";
3648
- const focusables = () => panel.current ? [...panel.current.querySelectorAll(FOCUSABLE)] : [];
3649
- const preferred = panel.current?.querySelector(".cs-alert-dialog__confirm") || focusables()[0];
3650
- (preferred || panel.current)?.focus?.();
3651
- const k = (e) => {
3652
- if (e.key === "Escape") {
3653
- setOpen(false);
3654
- cancelRef.current?.();
3655
- return;
3656
- }
3657
- if (e.key !== "Tab" || !panel.current) return;
3658
- const f = focusables();
3659
- if (!f.length) {
3660
- e.preventDefault();
3661
- panel.current.focus();
3662
- return;
3663
- }
3664
- const a = f[0], z = f[f.length - 1];
3665
- const active = document.activeElement;
3666
- const inside = panel.current.contains(active);
3667
- if (e.shiftKey && (!inside || active === a)) {
3668
- e.preventDefault();
3669
- z.focus();
3670
- } else if (!e.shiftKey && (!inside || active === z)) {
3671
- e.preventDefault();
3672
- a.focus();
3673
- }
3674
- };
3675
- document.addEventListener("keydown", k);
3676
- return () => {
3677
- document.removeEventListener("keydown", k);
3678
- document.body.style.overflow = prevOverflow;
3679
- if (prev && prev.focus) prev.focus();
3680
- };
3681
- }, [isOpen]);
4585
+ const setOpenRef = React.useRef(setOpen);
4586
+ setOpenRef.current = setOpen;
4587
+ useOverlayLayer({
4588
+ open: isOpen,
4589
+ kind: "modal",
4590
+ trapFocus: true,
4591
+ preferFocusSelector: ".cs-alert-dialog__confirm",
4592
+ onEscape: () => {
4593
+ setOpenRef.current(false);
4594
+ cancelRef.current?.();
4595
+ },
4596
+ panelRef: panel
4597
+ });
3682
4598
  const cl = cancelLabel != null ? cancelLabel : t("cancel");
3683
4599
  const cf = confirmLabel != null ? confirmLabel : t("confirm");
3684
4600
  const body = description ?? children;
@@ -3718,134 +4634,69 @@ function AlertDialog({
3718
4634
  cancelRef.current?.();
3719
4635
  }
3720
4636
  },
3721
- cl
3722
- ), /* @__PURE__ */ React.createElement(
3723
- "button",
3724
- {
3725
- type: "button",
3726
- className: cx(
3727
- "cs-button",
3728
- "cs-button--md",
3729
- "cs-alert-dialog__confirm",
3730
- destructive ? "cs-button--danger" : "cs-button--primary"
3731
- ),
3732
- onClick: () => {
3733
- setOpen(false);
3734
- confirmRef.current?.();
3735
- }
3736
- },
3737
- cf
3738
- ))
3739
- ));
3740
- }
3741
- Object.assign(__ds_scope, { AlertDialog });
3742
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/AlertDialog.jsx", error: String((e && e.message) || e) }); }
3743
-
3744
- // components/overlays/ContextMenu.jsx
3745
- try { (() => {
3746
- const { cx } = __ds_scope;
3747
- function ContextMenu({ items = [], children, className }) {
3748
- const [pos, setPos] = React.useState(null);
3749
- React.useEffect(() => {
3750
- if (!pos) return;
3751
- const close = () => setPos(null);
3752
- const k = (e) => {
3753
- if (e.key === "Escape") setPos(null);
3754
- };
3755
- document.addEventListener("click", close);
3756
- document.addEventListener("keydown", k);
3757
- return () => {
3758
- document.removeEventListener("click", close);
3759
- document.removeEventListener("keydown", k);
3760
- };
3761
- }, [pos]);
3762
- return /* @__PURE__ */ React.createElement(
3763
- "div",
3764
- {
3765
- className: cx("cs-ctxmenu-zone", className),
3766
- onContextMenu: (e) => {
3767
- e.preventDefault();
3768
- const r = e.currentTarget.getBoundingClientRect();
3769
- setPos({ x: e.clientX - r.left, y: e.clientY - r.top });
3770
- }
3771
- },
3772
- children,
3773
- pos ? /* @__PURE__ */ React.createElement("div", { className: "cs-menu__list", role: "menu", style: { position: "absolute", insetInlineStart: pos.x, insetBlockStart: pos.y } }, items.map((it, i) => it === "-" ? /* @__PURE__ */ React.createElement("div", { key: i, className: "cs-menu__sep" }) : /* @__PURE__ */ React.createElement("button", { key: i, type: "button", role: "menuitem", className: cx("cs-menu__item", it.danger && "cs-menu__item--danger"), onClick: () => {
3774
- setPos(null);
3775
- it.onSelect && it.onSelect();
3776
- } }, it.label))) : null
3777
- );
4637
+ cl
4638
+ ), /* @__PURE__ */ React.createElement(
4639
+ "button",
4640
+ {
4641
+ type: "button",
4642
+ className: cx(
4643
+ "cs-button",
4644
+ "cs-button--md",
4645
+ "cs-alert-dialog__confirm",
4646
+ destructive ? "cs-button--danger" : "cs-button--primary"
4647
+ ),
4648
+ onClick: () => {
4649
+ setOpen(false);
4650
+ confirmRef.current?.();
4651
+ }
4652
+ },
4653
+ cf
4654
+ ))
4655
+ ));
3778
4656
  }
3779
- Object.assign(__ds_scope, { ContextMenu });
3780
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/ContextMenu.jsx", error: String((e && e.message) || e) }); }
4657
+ Object.assign(__ds_scope, { AlertDialog });
4658
+ })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/AlertDialog.jsx", error: String((e && e.message) || e) }); }
3781
4659
 
3782
4660
  // components/overlays/Drawer.jsx
3783
4661
  try { (() => {
3784
4662
  const { makeT, useLang } = __ds_scope;
3785
4663
  const { cx } = __ds_scope;
4664
+ const { useOverlayLayer } = __ds_scope;
3786
4665
  function Drawer({ open, onClose, title, side = "right", children, actions, lang, className }) {
3787
4666
  const [ref, L] = useLang(lang);
3788
- React.useEffect(() => {
3789
- if (!open) return;
3790
- const prev = document.activeElement;
3791
- const SEL = 'a[href],button:not([disabled]),input:not([disabled]),select:not([disabled]),textarea:not([disabled]),[tabindex]:not([tabindex="-1"])';
3792
- const p = ref.current;
3793
- const first = p ? p.querySelector(SEL) : null;
3794
- (first || p) && (first || p).focus();
3795
- const k = (e) => {
3796
- if (e.key === "Escape") {
3797
- onClose && onClose();
3798
- return;
3799
- }
3800
- if (e.key !== "Tab" || !ref.current) return;
3801
- const f = [...ref.current.querySelectorAll(SEL)];
3802
- if (!f.length) return;
3803
- const a = f[0], z = f[f.length - 1];
3804
- if (e.shiftKey && document.activeElement === a) {
3805
- e.preventDefault();
3806
- z.focus();
3807
- } else if (!e.shiftKey && document.activeElement === z) {
3808
- e.preventDefault();
3809
- a.focus();
3810
- }
3811
- };
3812
- document.addEventListener("keydown", k);
3813
- return () => {
3814
- document.removeEventListener("keydown", k);
3815
- if (prev && prev.focus) prev.focus();
3816
- };
3817
- }, [open]);
4667
+ const panel = React.useRef(null);
4668
+ const closeRef = React.useRef(onClose);
4669
+ closeRef.current = onClose;
4670
+ useOverlayLayer({
4671
+ open: !!open,
4672
+ kind: "modal",
4673
+ trapFocus: true,
4674
+ onEscape: () => closeRef.current && closeRef.current(),
4675
+ panelRef: panel
4676
+ });
3818
4677
  const t = makeT("Drawer", L);
3819
4678
  if (!open) return null;
3820
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "cs-drawer-scrim", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement("aside", { ref, tabIndex: -1, className: cx("cs-drawer", side === "left" && "cs-drawer--left", className), role: "dialog", "aria-modal": "true", "aria-label": typeof title === "string" ? title : t("panel") }, /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__header" }, title ? /* @__PURE__ */ React.createElement("h2", { className: "cs-drawer__title" }, title) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-drawer__close", "aria-label": t("close"), onClick: onClose }, /* @__PURE__ */ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 6l12 12M18 6L6 18" })))), /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__body", tabIndex: 0 }, children), actions ? /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__footer" }, actions) : null));
4679
+ return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "cs-drawer-scrim", onClick: onClose, "aria-hidden": "true" }), /* @__PURE__ */ React.createElement(
4680
+ "aside",
4681
+ {
4682
+ ref: (el) => {
4683
+ panel.current = el;
4684
+ ref.current = el;
4685
+ },
4686
+ tabIndex: -1,
4687
+ className: cx("cs-drawer", side === "left" && "cs-drawer--left", className),
4688
+ role: "dialog",
4689
+ "aria-modal": "true",
4690
+ "aria-label": typeof title === "string" ? title : t("panel")
4691
+ },
4692
+ /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__header" }, title ? /* @__PURE__ */ React.createElement("h2", { className: "cs-drawer__title" }, title) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-drawer__close", "aria-label": t("close"), onClick: onClose }, /* @__PURE__ */ React.createElement("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M6 6l12 12M18 6L6 18" })))),
4693
+ /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__body", tabIndex: 0 }, children),
4694
+ actions ? /* @__PURE__ */ React.createElement("div", { className: "cs-drawer__footer" }, actions) : null
4695
+ ));
3821
4696
  }
3822
4697
  Object.assign(__ds_scope, { Drawer });
3823
4698
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/Drawer.jsx", error: String((e && e.message) || e) }); }
3824
4699
 
3825
- // components/overlays/HoverCard.jsx
3826
- try { (() => {
3827
- const { cx } = __ds_scope;
3828
- function HoverCard({ trigger, children, openDelay = 150, closeDelay = 200, className }) {
3829
- const [open, setOpen] = React.useState(false);
3830
- const t1 = React.useRef();
3831
- const t2 = React.useRef();
3832
- const show = () => {
3833
- clearTimeout(t2.current);
3834
- t1.current = setTimeout(() => setOpen(true), openDelay);
3835
- };
3836
- const hide = () => {
3837
- clearTimeout(t1.current);
3838
- t2.current = setTimeout(() => setOpen(false), closeDelay);
3839
- };
3840
- React.useEffect(() => () => {
3841
- clearTimeout(t1.current);
3842
- clearTimeout(t2.current);
3843
- }, []);
3844
- return /* @__PURE__ */ React.createElement("span", { className: cx("cs-hovercard", className), onMouseEnter: show, onMouseLeave: hide, onFocus: show, onBlur: hide }, trigger, open ? /* @__PURE__ */ React.createElement("span", { className: "cs-hovercard__panel", role: "dialog" }, children) : null);
3845
- }
3846
- Object.assign(__ds_scope, { HoverCard });
3847
- })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/HoverCard.jsx", error: String((e && e.message) || e) }); }
3848
-
3849
4700
  // components/overlays/Popconfirm.jsx
3850
4701
  try { (() => {
3851
4702
  const { makeT, useLang } = __ds_scope;
@@ -3937,30 +4788,37 @@ const { cx } = __ds_scope;
3937
4788
  function Tour({ steps = [], open, onClose, lang, className }) {
3938
4789
  const [i, setI] = React.useState(0);
3939
4790
  const [rect, setRect] = React.useState(null);
4791
+ const [pop, setPop] = React.useState({ top: 80, left: 40 });
3940
4792
  const [ref, L] = useLang(lang);
3941
4793
  const t = makeT("Tour", L);
3942
4794
  React.useEffect(() => {
3943
4795
  if (open) setI(0);
3944
4796
  }, [open]);
3945
4797
  React.useEffect(() => {
3946
- if (!open || !steps[i]) return;
4798
+ if (!open || !steps[i]) return void 0;
3947
4799
  const el = document.querySelector(steps[i].target);
3948
4800
  if (el) {
3949
4801
  const r = el.getBoundingClientRect();
3950
- setRect({ x: r.left - 6, y: r.top - 6, w: r.width + 12, h: r.height + 12 });
3951
- } else setRect(null);
4802
+ const next = { x: r.left - 6, y: r.top - 6, w: r.width + 12, h: r.height + 12 };
4803
+ setRect(next);
4804
+ setPop({
4805
+ top: Math.min(window.innerHeight - 170, next.y + next.h + 12),
4806
+ left: Math.max(12, Math.min(window.innerWidth - 292, next.x))
4807
+ });
4808
+ } else {
4809
+ setRect(null);
4810
+ setPop({ top: 80, left: 40 });
4811
+ }
3952
4812
  const k = (e) => {
3953
4813
  if (e.key === "Escape") onClose && onClose();
3954
4814
  };
3955
4815
  document.addEventListener("keydown", k);
3956
4816
  return () => document.removeEventListener("keydown", k);
3957
- }, [open, i, steps]);
4817
+ }, [open, i, steps, onClose]);
3958
4818
  if (!open || !steps.length) return /* @__PURE__ */ React.createElement("span", { ref, style: { display: "none" } });
3959
4819
  const s = steps[i];
3960
4820
  const last = i === steps.length - 1;
3961
- const popTop = rect ? Math.min(window.innerHeight - 170, rect.y + rect.h + 12) : 80;
3962
- const popLeft = rect ? Math.max(12, Math.min(window.innerWidth - 292, rect.x)) : 40;
3963
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-tour", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__scrim", onClick: onClose }), rect ? /* @__PURE__ */ React.createElement("div", { className: "cs-tour__hole", style: { left: rect.x, top: rect.y, width: rect.w, height: rect.h } }) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__pop", role: "dialog", "aria-label": typeof s.title === "string" ? s.title : void 0, style: { left: popLeft, top: popTop } }, /* @__PURE__ */ React.createElement("b", null, s.title), s.body ? /* @__PURE__ */ React.createElement("p", null, s.body) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__bar" }, /* @__PURE__ */ React.createElement("span", { className: "cs-tour__count" }, i + 1 + " / " + steps.length), /* @__PURE__ */ React.createElement("span", { className: "cs-tour__btns" }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--xs", onClick: onClose }, t("skip")), i > 0 ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--secondary cs-button--xs", onClick: () => setI(i - 1) }, t("back")) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--primary cs-button--xs", onClick: () => last ? onClose && onClose() : setI(i + 1) }, last ? t("done") : t("next"))))));
4821
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("cs-tour", className) }, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__scrim", onClick: onClose }), rect ? /* @__PURE__ */ React.createElement("div", { className: "cs-tour__hole", style: { left: rect.x, top: rect.y, width: rect.w, height: rect.h } }) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__pop", role: "dialog", "aria-label": typeof s.title === "string" ? s.title : void 0, style: { left: pop.left, top: pop.top } }, /* @__PURE__ */ React.createElement("b", null, s.title), s.body ? /* @__PURE__ */ React.createElement("p", null, s.body) : null, /* @__PURE__ */ React.createElement("div", { className: "cs-tour__bar" }, /* @__PURE__ */ React.createElement("span", { className: "cs-tour__count" }, i + 1 + " / " + steps.length), /* @__PURE__ */ React.createElement("span", { className: "cs-tour__btns" }, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--ghost cs-button--xs", onClick: onClose }, t("skip")), i > 0 ? /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--secondary cs-button--xs", onClick: () => setI(i - 1) }, t("back")) : null, /* @__PURE__ */ React.createElement("button", { type: "button", className: "cs-button cs-button--primary cs-button--xs", onClick: () => last ? onClose && onClose() : setI(i + 1) }, last ? t("done") : t("next"))))));
3964
4822
  }
3965
4823
  Object.assign(__ds_scope, { Tour });
3966
4824
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/overlays/Tour.jsx", error: String((e && e.message) || e) }); }
@@ -4011,733 +4869,12 @@ function TextField({
4011
4869
  Object.assign(__ds_scope, { TextField });
4012
4870
  })(); } catch (e) { __ds_ns.__errors.push({ path: "components/textfield/TextField.jsx", error: String((e && e.message) || e) }); }
4013
4871
 
4014
- // image-slot.js
4015
- try { (() => {
4016
- (() => {
4017
- const STATE_FILE = ".image-slots.state.json";
4018
- const UNSPLASH_HOMEPAGE_HREF = "https://unsplash.com/?utm_source=claude_design&utm_medium=referral";
4019
- const isUnsplashHost = (u) => {
4020
- try {
4021
- return /(^|\.)unsplash\.com$/.test(
4022
- new URL(u, document.baseURI).hostname.replace(/\.$/, "")
4023
- );
4024
- } catch {
4025
- return false;
4026
- }
4027
- };
4028
- const withReferral = (href) => {
4029
- try {
4030
- const u = new URL(href);
4031
- if (!/(^|\.)unsplash\.com$/.test(u.hostname.replace(/\.$/, ""))) {
4032
- return href;
4033
- }
4034
- if (!u.searchParams.has("utm_source")) {
4035
- u.searchParams.set("utm_source", "claude_design");
4036
- }
4037
- if (!u.searchParams.has("utm_medium")) {
4038
- u.searchParams.set("utm_medium", "referral");
4039
- }
4040
- return u.toString();
4041
- } catch (e) {
4042
- return href;
4043
- }
4044
- };
4045
- const MAX_DIM = 1200;
4046
- const ACCEPT = ["image/png", "image/jpeg", "image/webp", "image/avif"];
4047
- const subs = /* @__PURE__ */ new Set();
4048
- let slots = {};
4049
- const tombstones = /* @__PURE__ */ new Set();
4050
- let loaded = false;
4051
- let loadP = null;
4052
- function load() {
4053
- if (loadP) return loadP;
4054
- loadP = fetch(STATE_FILE).then((r) => r.ok ? r.json() : null).then((j) => {
4055
- if (j && typeof j === "object") {
4056
- const merged = Object.assign({}, j, slots);
4057
- for (const k in slots) {
4058
- if (merged[k] && !merged[k].u && j[k]) {
4059
- merged[k].u = typeof j[k] === "string" ? j[k] : j[k].u;
4060
- }
4061
- }
4062
- for (const id of tombstones) delete merged[id];
4063
- slots = merged;
4064
- }
4065
- tombstones.clear();
4066
- }).catch(() => {
4067
- }).then(() => {
4068
- loaded = true;
4069
- subs.forEach((fn) => fn());
4070
- });
4071
- return loadP;
4072
- }
4073
- let saving = false;
4074
- let saveDirty = false;
4075
- function flushNow() {
4076
- if (!loaded) return;
4077
- const w = window.omelette && window.omelette.writeFile;
4078
- if (!w) return;
4079
- try {
4080
- Promise.resolve(w(STATE_FILE, JSON.stringify(slots))).catch(() => {
4081
- });
4082
- } catch (e) {
4083
- }
4084
- }
4085
- function save() {
4086
- if (saving) {
4087
- saveDirty = true;
4088
- return;
4089
- }
4090
- const w = window.omelette && window.omelette.writeFile;
4091
- if (!w) return;
4092
- saving = true;
4093
- Promise.resolve(w(STATE_FILE, JSON.stringify(slots))).catch(() => {
4094
- }).then(() => {
4095
- saving = false;
4096
- if (saveDirty) {
4097
- saveDirty = false;
4098
- save();
4099
- }
4100
- });
4101
- }
4102
- const S_MAX = 5;
4103
- const clampS = (s) => Math.max(1, Math.min(S_MAX, s));
4104
- function getSlot(id) {
4105
- const v = slots[id];
4106
- if (!v) return null;
4107
- return typeof v === "string" ? { u: v, s: 1, x: 0, y: 0 } : v;
4108
- }
4109
- function setSlot(id, val) {
4110
- if (!id) return;
4111
- if (val) {
4112
- slots[id] = val;
4113
- tombstones.delete(id);
4114
- } else {
4115
- delete slots[id];
4116
- if (!loaded) tombstones.add(id);
4117
- }
4118
- subs.forEach((fn) => fn());
4119
- if (loaded) save();
4120
- else load().then(save);
4121
- }
4122
- async function toDataUrl(file, targetW) {
4123
- const bitmap = await createImageBitmap(file);
4124
- try {
4125
- const cap = Math.min(MAX_DIM, Math.max(1, Math.round(targetW * 2)) || MAX_DIM);
4126
- const scale = Math.min(1, cap / Math.max(bitmap.width, bitmap.height));
4127
- const w = Math.max(1, Math.round(bitmap.width * scale));
4128
- const h = Math.max(1, Math.round(bitmap.height * scale));
4129
- const canvas = document.createElement("canvas");
4130
- canvas.width = w;
4131
- canvas.height = h;
4132
- canvas.getContext("2d").drawImage(bitmap, 0, 0, w, h);
4133
- return canvas.toDataURL("image/webp", 0.85);
4134
- } finally {
4135
- bitmap.close && bitmap.close();
4136
- }
4137
- }
4138
- const stylesheet = (
4139
- // Fill the container by default: slots are usually placed inside a
4140
- // sized wrapper (a hero frame, a grid cell, an inset:0 layer) and are
4141
- // expected to take that box — a fixed intrinsic size would render as
4142
- // a small tile in the corner of a full-bleed wrapper instead.
4143
- // aspect-ratio is the companion fallback that keeps a bare slot
4144
- // visible when the parent's height is indefinite: height:100%
4145
- // resolves to auto there, and the ratio then derives height from
4146
- // width instead of letting the slot collapse to zero height.
4147
- // Explicit width/height on the element override all of this.
4148
- ':host{display:block;position:relative; font:13px/1.3 system-ui,-apple-system,sans-serif;color:rgba(0,0,0,.55); width:100%;height:100%;aspect-ratio:3/2}.frame{position:absolute;inset:0;overflow:hidden;background:rgba(0,0,0,.04)}.frame img{position:absolute;max-width:none;transform:translate(-50%,-50%); -webkit-user-drag:none;user-select:none;touch-action:none}.spill{position:fixed;margin:0;inset:auto;border:0;padding:0;background:transparent; overflow:visible;transform:translate(-50%,-50%);z-index:1;cursor:grab;touch-action:none}:host([data-panning]) .spill{cursor:grabbing}.spill .ghost{position:absolute;inset:0;width:100%;height:100%;opacity:.35; pointer-events:none;-webkit-user-drag:none;user-select:none; box-shadow:0 0 0 1px rgba(0,0,0,.2),0 12px 32px rgba(0,0,0,.2)}.spill .handle{position:absolute;width:12px;height:12px;border-radius:50%; background:#fff;box-shadow:0 0 0 1.5px #c96442,0 1px 3px rgba(0,0,0,.3); transform:translate(-50%,-50%)}.spill .handle[data-c=nw]{left:0;top:0;cursor:nwse-resize}.spill .handle[data-c=ne]{left:100%;top:0;cursor:nesw-resize}.spill .handle[data-c=sw]{left:0;top:100%;cursor:nesw-resize}.spill .handle[data-c=se]{left:100%;top:100%;cursor:nwse-resize}:host([data-reframe]){z-index:10}:host([data-reframe]) .frame{box-shadow:0 0 0 2px #c96442}.empty{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center; justify-content:center;gap:6px;text-align:center;padding:12px;box-sizing:border-box; cursor:pointer;user-select:none}.empty svg{opacity:.45}.empty .cap{max-width:90%;font-weight:500;letter-spacing:.01em}.empty .sub{font-size:11px}.empty .sub u{text-underline-offset:2px;text-decoration-color:rgba(0,0,0,.25)}.empty:hover .sub u{color:rgba(0,0,0,.75);text-decoration-color:currentColor}:host([data-over]) .frame{outline:2px solid #c96442;outline-offset:-2px; background:rgba(201,100,66,.10)}.ring{position:absolute;inset:0;pointer-events:none;border:1.5px dashed rgba(0,0,0,.25); transition:border-color .12s}:host([data-over]) .ring{border-color:#c96442}:host([data-filled]) .ring{display:none}.ctl{position:absolute;inset:auto;top:8px;right:8px;margin:0;border:0;padding:0; background:transparent;overflow:visible; display:flex;gap:6px;opacity:0;pointer-events:none;transition:opacity .12s;z-index:2; white-space:nowrap}.ctl:popover-open{position:fixed;inset:auto;transform:translateX(-100%)}:host([data-filled][data-editable]:hover) .ctl,:host([data-reframe]) .ctl {opacity:1;pointer-events:auto}.ctl button{appearance:none;border:0;border-radius:6px;padding:5px 10px;cursor:pointer; background:rgba(0,0,0,.65);color:#fff;font:11px/1 system-ui,-apple-system,sans-serif; backdrop-filter:blur(6px)}.ctl button:hover{background:rgba(0,0,0,.8)}.err{position:absolute;left:8px;bottom:8px;right:8px;color:#b3261e;font-size:11px; background:rgba(255,255,255,.85);padding:4px 6px;border-radius:5px;pointer-events:none}:host([data-swapping]) .frame img{visibility:hidden}.loading{position:absolute;inset:0;display:none;align-items:center; justify-content:center;pointer-events:none}:host([data-swapping]) .loading{display:flex}.loading::after{content:"";width:22px;height:22px;border-radius:50%; border:2px solid rgba(0,0,0,.12);border-top-color:rgba(0,0,0,.45); animation:om-slot-spin .7s linear infinite}@keyframes om-slot-spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){.loading::after{animation:none}}.credit{position:absolute;left:6px;bottom:6px;max-width:calc(100% - 12px);display:none; padding:3px 7px;border-radius:5px;background:rgba(0,0,0,.55);color:#fff; font:10px/1.2 system-ui,-apple-system,sans-serif;text-decoration:none; white-space:nowrap;overflow:hidden;text-overflow:ellipsis;backdrop-filter:blur(6px)}.credit a{color:inherit;text-decoration:none}.credit a:hover,.credit a:focus-visible{text-decoration:underline}:host([data-filled][data-credit]) .credit{display:block}:host-context([data-om-exporting]) .ctl,:host-context([data-om-exporting]) .credit{display:none !important}.attr-error{position:absolute;inset:0;display:none;flex-direction:column;align-items:center; justify-content:center;gap:6px;text-align:center;padding:12px;box-sizing:border-box; background:#f2f1ef;color:#6e6c66;user-select:none; font:13px/1.45 system-ui,-apple-system,sans-serif}.attr-error svg{opacity:.55}.attr-error .cap{max-width:92%;font-weight:500;letter-spacing:.01em}:host([data-attribution-error]) .attr-error{display:flex}:host([data-attribution-error]) .ring{display:none}'
4149
- );
4150
- const icon = '<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="m21 15-5-5L5 21"/></svg>';
4151
- const warnIcon = '<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.46 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"/><path d="M12 9v4"/><path d="M12 17h.01"/></svg>';
4152
- class ImageSlot extends HTMLElement {
4153
- static get observedAttributes() {
4154
- return ["shape", "radius", "mask", "fit", "placeholder", "src", "id", "credit", "credit-href"];
4155
- }
4156
- /** Duplicate-slide hook (called by deck-stage, see its
4157
- * _remintDuplicateIds): copy this id's stored image, if any, under a
4158
- * freshly minted key and return that key — so a duplicated slide's
4159
- * slot keeps its dropped photo instead of reverting to the
4160
- * placeholder. 'isFree' is the caller's uniqueness check (document
4161
- * ids); candidates must ALSO be unused in the sidecar, which can
4162
- * hold keys from other pages sharing the project root. (An EMPTY
4163
- * slot on another page leaves no sidecar entry, so its id is not
4164
- * detectable here — a minted key can collide with it and that slot
4165
- * would show this photo. Same blast radius as two pages reusing an
4166
- * id by hand, which the shared sidecar already permits.) Returns null
4167
- * when no id could be minted (caller strips the id, today's
4168
- * behavior). */
4169
- static cloneSlot(fromId, isFree) {
4170
- if (typeof fromId !== "string" || !fromId) return null;
4171
- if (!loaded) return null;
4172
- const stem = fromId.replace(/-\d+$/, "") || fromId;
4173
- for (let n = 2; n < 100; n++) {
4174
- const toId = stem + "-" + n;
4175
- if (toId === fromId) continue;
4176
- if (slots[toId] !== void 0) {
4177
- const prev = getSlot(toId);
4178
- const cur = getSlot(fromId);
4179
- if (!(prev && cur && prev.u && prev.u === cur.u && prev.s === cur.s && prev.x === cur.x && prev.y === cur.y && (typeof isFree !== "function" || isFree(toId)))) continue;
4180
- return toId;
4181
- }
4182
- if (typeof isFree === "function" && !isFree(toId)) continue;
4183
- const v = getSlot(fromId);
4184
- if (v) setSlot(toId, Object.assign({}, v));
4185
- return toId;
4186
- }
4187
- return null;
4188
- }
4189
- constructor() {
4190
- super();
4191
- const root = this.shadowRoot || this.attachShadow({ mode: "open", clonable: true });
4192
- root.innerHTML = "<style>" + stylesheet + '</style><div class="frame" part="frame"> <img part="image" alt="" draggable="false" style="display:none"> <div class="empty" part="empty">' + icon + ' <div class="cap"></div> <div class="sub">or <u>browse files</u></div></div> <div class="attr-error" part="attribution-error">' + warnIcon + ' <div class="cap">This photo needs attribution</div></div> <div class="loading" part="loading"></div> <div class="ring" part="ring"></div></div><span class="credit" part="credit"></span><div class="spill" popover="manual" data-dc-edit-transparent> <img class="ghost" alt="" draggable="false"> <div class="handle" data-c="nw"></div><div class="handle" data-c="ne"></div> <div class="handle" data-c="sw"></div><div class="handle" data-c="se"></div></div><div class="ctl" popover="manual" data-dc-edit-transparent><button data-act="replace" title="Replace image">Replace</button> <button data-act="edit" title="Reframe image">Edit</button></div><input type="file" accept="' + ACCEPT.join(",") + '" hidden>';
4193
- this._frame = root.querySelector(".frame");
4194
- this._ring = root.querySelector(".ring");
4195
- this._img = root.querySelector(".frame img");
4196
- this._empty = root.querySelector(".empty");
4197
- this._cap = root.querySelector(".cap");
4198
- this._sub = root.querySelector(".sub");
4199
- this._spill = root.querySelector(".spill");
4200
- this._ctl = root.querySelector(".ctl");
4201
- this._credit = root.querySelector(".credit");
4202
- this._attrError = root.querySelector(".attr-error");
4203
- this._credit.addEventListener("click", (e) => e.stopPropagation());
4204
- this._credit.addEventListener("dblclick", (e) => e.stopPropagation());
4205
- this._ghost = root.querySelector(".ghost");
4206
- this._err = null;
4207
- this._input = root.querySelector("input");
4208
- this._depth = 0;
4209
- this._gen = 0;
4210
- this._swapGen = 0;
4211
- this._loadPending = false;
4212
- this._hidShowing = false;
4213
- this._view = { s: 1, x: 0, y: 0 };
4214
- this._subFn = () => this._render();
4215
- this._empty.addEventListener("click", () => this._input.click());
4216
- root.addEventListener("click", (e) => {
4217
- const act = e.target && e.target.getAttribute && e.target.getAttribute("data-act");
4218
- if (!act) return;
4219
- if (!this.hasAttribute("data-editable")) return;
4220
- if (act === "replace") {
4221
- this._exitReframe(true);
4222
- this.dispatchEvent(new CustomEvent("image-slot:pick", {
4223
- bubbles: true,
4224
- composed: true,
4225
- detail: { id: this.id || null }
4226
- }));
4227
- }
4228
- if (act === "edit") {
4229
- if (!this._reframes()) return;
4230
- if (this.hasAttribute("data-reframe")) this._exitReframe(true);
4231
- else this._enterReframe();
4232
- }
4233
- });
4234
- this._input.addEventListener("change", () => {
4235
- const f = this._input.files && this._input.files[0];
4236
- if (f) this._ingest(f);
4237
- this._input.value = "";
4238
- });
4239
- this._img.addEventListener("load", () => {
4240
- this._loadPending = false;
4241
- this._releaseMask(true);
4242
- this._applyView();
4243
- });
4244
- this._img.addEventListener("error", () => {
4245
- this._loadPending = false;
4246
- this._releaseMask(true);
4247
- });
4248
- this.addEventListener("dblclick", (e) => {
4249
- if (!this.hasAttribute("data-editable") || !this._reframes()) return;
4250
- e.preventDefault();
4251
- if (this.hasAttribute("data-reframe")) this._exitReframe(true);
4252
- else this._enterReframe();
4253
- });
4254
- this._spill.addEventListener("pointerdown", (e) => {
4255
- if (e.button !== 0 || !this.hasAttribute("data-reframe")) return;
4256
- e.preventDefault();
4257
- e.stopPropagation();
4258
- this._spill.setPointerCapture(e.pointerId);
4259
- const rect = this.getBoundingClientRect();
4260
- const fw = rect.width || 1, fh = rect.height || 1;
4261
- const corner = e.target.getAttribute && e.target.getAttribute("data-c");
4262
- let move;
4263
- if (corner) {
4264
- const iw = this._img.naturalWidth || 1, ih = this._img.naturalHeight || 1;
4265
- const contain = (this.getAttribute("fit") || "cover").toLowerCase() === "contain";
4266
- const base = contain ? Math.min(fw / iw, fh / ih) : Math.max(fw / iw, fh / ih);
4267
- const sx = corner.includes("e") ? 1 : -1;
4268
- const sy = corner.includes("s") ? 1 : -1;
4269
- const s0 = this._view.s;
4270
- const w0 = iw * base * s0, h0 = ih * base * s0;
4271
- const cx0 = (50 + this._view.x) / 100 * fw;
4272
- const cy0 = (50 + this._view.y) / 100 * fh;
4273
- const ox = cx0 - sx * w0 / 2, oy = cy0 - sy * h0 / 2;
4274
- const diag0 = Math.hypot(w0, h0);
4275
- const ux = sx * w0 / diag0, uy = sy * h0 / diag0;
4276
- move = (ev) => {
4277
- const proj = (ev.clientX - rect.left - ox) * ux + (ev.clientY - rect.top - oy) * uy;
4278
- const s = clampS(s0 * proj / diag0);
4279
- const d = diag0 * s / s0;
4280
- this._view.s = s;
4281
- this._view.x = (ox + ux * d / 2) / fw * 100 - 50;
4282
- this._view.y = (oy + uy * d / 2) / fh * 100 - 50;
4283
- this._clampView();
4284
- this._applyView();
4285
- };
4286
- } else {
4287
- this.setAttribute("data-panning", "");
4288
- const start = { px: e.clientX, py: e.clientY, x: this._view.x, y: this._view.y };
4289
- move = (ev) => {
4290
- this._view.x = start.x + (ev.clientX - start.px) / fw * 100;
4291
- this._view.y = start.y + (ev.clientY - start.py) / fh * 100;
4292
- this._clampView();
4293
- this._applyView();
4294
- };
4295
- }
4296
- const up = () => {
4297
- try {
4298
- this._spill.releasePointerCapture(e.pointerId);
4299
- } catch {
4300
- }
4301
- this._spill.removeEventListener("pointermove", move);
4302
- this._spill.removeEventListener("pointerup", up);
4303
- this._spill.removeEventListener("pointercancel", up);
4304
- this.removeAttribute("data-panning");
4305
- this._dragUp = null;
4306
- };
4307
- this._dragUp = up;
4308
- this._spill.addEventListener("pointermove", move);
4309
- this._spill.addEventListener("pointerup", up);
4310
- this._spill.addEventListener("pointercancel", up);
4311
- });
4312
- this.addEventListener("wheel", (e) => {
4313
- if (!this.hasAttribute("data-reframe")) return;
4314
- e.preventDefault();
4315
- const r = this.getBoundingClientRect();
4316
- const cx = (e.clientX - r.left) / r.width * 100 - 50;
4317
- const cy = (e.clientY - r.top) / r.height * 100 - 50;
4318
- const prev = this._view.s;
4319
- const next = clampS(prev * Math.pow(1.0015, -e.deltaY));
4320
- if (next === prev) return;
4321
- const k = next / prev;
4322
- this._view.s = next;
4323
- this._view.x = cx * (1 - k) + this._view.x * k;
4324
- this._view.y = cy * (1 - k) + this._view.y * k;
4325
- this._clampView();
4326
- this._applyView();
4327
- }, { passive: false });
4328
- }
4329
- connectedCallback() {
4330
- if (!this.id && !ImageSlot._warned) {
4331
- ImageSlot._warned = true;
4332
- console.warn("<image-slot> without an id will not persist its dropped image.");
4333
- }
4334
- this.addEventListener("dragenter", this);
4335
- this.addEventListener("dragover", this);
4336
- this.addEventListener("dragleave", this);
4337
- this.addEventListener("drop", this);
4338
- subs.add(this._subFn);
4339
- this.addEventListener("pointerenter", this._subFn);
4340
- this._ro = new ResizeObserver(() => this._render());
4341
- this._ro.observe(this);
4342
- load();
4343
- this._render();
4344
- }
4345
- disconnectedCallback() {
4346
- subs.delete(this._subFn);
4347
- this.removeEventListener("pointerenter", this._subFn);
4348
- this.removeEventListener("dragenter", this);
4349
- this.removeEventListener("dragover", this);
4350
- this.removeEventListener("dragleave", this);
4351
- this.removeEventListener("drop", this);
4352
- if (this._ro) {
4353
- this._ro.disconnect();
4354
- this._ro = null;
4355
- }
4356
- this._exitReframe(false);
4357
- }
4358
- _enterReframe() {
4359
- if (this.hasAttribute("data-reframe")) return;
4360
- this.setAttribute("data-reframe", "");
4361
- this._signalReframe(true);
4362
- this._pagehide = () => {
4363
- this._exitReframe(true);
4364
- flushNow();
4365
- };
4366
- window.addEventListener("pagehide", this._pagehide);
4367
- try {
4368
- this._spill.showPopover();
4369
- } catch {
4370
- }
4371
- try {
4372
- this._ctl.showPopover();
4373
- } catch {
4374
- }
4375
- this._reposition = () => {
4376
- if (this.hasAttribute("data-reframe")) this._applyView();
4377
- };
4378
- window.addEventListener("scroll", this._reposition, true);
4379
- window.addEventListener("resize", this._reposition);
4380
- this._lastRect = "";
4381
- this._watch = () => {
4382
- if (!this.hasAttribute("data-reframe")) return;
4383
- const r = this.getBoundingClientRect();
4384
- const key = r.left + "," + r.top + "," + r.width + "," + r.height;
4385
- if (key !== this._lastRect) {
4386
- this._lastRect = key;
4387
- this._applyView();
4388
- }
4389
- this._watchId = requestAnimationFrame(this._watch);
4390
- };
4391
- this._watchId = requestAnimationFrame(this._watch);
4392
- this._applyView();
4393
- this._outside = (e) => {
4394
- if (e.composedPath && e.composedPath().includes(this)) return;
4395
- this._exitReframe(true);
4396
- };
4397
- this._esc = (e) => {
4398
- if (e.key === "Escape") this._exitReframe(true);
4399
- };
4400
- document.addEventListener("pointerdown", this._outside, true);
4401
- document.addEventListener("keydown", this._esc, true);
4402
- }
4403
- _exitReframe(commit) {
4404
- if (!this.hasAttribute("data-reframe")) return;
4405
- if (this._dragUp) this._dragUp();
4406
- this.removeAttribute("data-reframe");
4407
- this.removeAttribute("data-panning");
4408
- if (this._outside) document.removeEventListener("pointerdown", this._outside, true);
4409
- if (this._esc) document.removeEventListener("keydown", this._esc, true);
4410
- this._outside = this._esc = null;
4411
- if (this._reposition) {
4412
- window.removeEventListener("scroll", this._reposition, true);
4413
- window.removeEventListener("resize", this._reposition);
4414
- this._reposition = null;
4415
- }
4416
- if (this._watchId) {
4417
- cancelAnimationFrame(this._watchId);
4418
- this._watchId = 0;
4419
- }
4420
- if (this._pagehide) {
4421
- window.removeEventListener("pagehide", this._pagehide);
4422
- this._pagehide = null;
4423
- }
4424
- try {
4425
- this._spill.hidePopover();
4426
- } catch {
4427
- }
4428
- try {
4429
- this._ctl.hidePopover();
4430
- } catch {
4431
- }
4432
- this._ctl.style.left = "";
4433
- this._ctl.style.top = "";
4434
- if (commit) this._commitView();
4435
- this._signalReframe(false);
4436
- }
4437
- // Reframe state lives only in this DOM until commit, invisible to the
4438
- // host's dirty signals — announce enter/exit so the host can hold
4439
- // auto-reloads for exactly the gesture (the guest bundle forwards
4440
- // image-slot:reframe to the host as imageSlotReframe). Dispatched on
4441
- // the element (composed, so it escapes shadow roots) while connected;
4442
- // a disconnected exit (disconnectedCallback) falls back to document so
4443
- // the host still hears it.
4444
- _signalReframe(active) {
4445
- const target = this.isConnected ? this : document;
4446
- target.dispatchEvent(new CustomEvent("image-slot:reframe", {
4447
- bubbles: true,
4448
- composed: true,
4449
- detail: { active, id: this.id || null }
4450
- }));
4451
- }
4452
- // Public: host's "Import from computer" calls this to run local browse.
4453
- openFilePicker() {
4454
- this._exitReframe(true);
4455
- this._input.click();
4456
- }
4457
- attributeChangedCallback() {
4458
- if (this.shadowRoot) this._render();
4459
- }
4460
- // handleEvent — one listener object for all four drag events keeps the
4461
- // add/remove symmetric and the depth counter correct.
4462
- handleEvent(e) {
4463
- if (e.type === "dragenter" || e.type === "dragover") {
4464
- e.preventDefault();
4465
- e.stopPropagation();
4466
- if (e.dataTransfer) e.dataTransfer.dropEffect = "copy";
4467
- if (e.type === "dragenter") this._depth++;
4468
- this.setAttribute("data-over", "");
4469
- } else if (e.type === "dragleave") {
4470
- if (--this._depth <= 0) {
4471
- this._depth = 0;
4472
- this.removeAttribute("data-over");
4473
- }
4474
- } else if (e.type === "drop") {
4475
- e.preventDefault();
4476
- e.stopPropagation();
4477
- this._depth = 0;
4478
- this.removeAttribute("data-over");
4479
- const f = e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files[0];
4480
- if (f) this._ingest(f);
4481
- }
4482
- }
4483
- async _ingest(file) {
4484
- this._setError(null);
4485
- if (!file || ACCEPT.indexOf(file.type) < 0) {
4486
- this._setError("Drop a PNG, JPEG, WebP, or AVIF image.");
4487
- return;
4488
- }
4489
- const gen = ++this._gen;
4490
- if (this.hasAttribute("data-filled")) {
4491
- this.setAttribute("data-swapping", "");
4492
- this._swapGen = gen;
4493
- }
4494
- try {
4495
- const w = this.clientWidth || this.offsetWidth || MAX_DIM;
4496
- const url = await toDataUrl(file, w);
4497
- if (gen !== this._gen) return;
4498
- this._exitReframe(false);
4499
- this._swapGen = 0;
4500
- const val = { u: url, s: 1, x: 0, y: 0 };
4501
- setSlot(this.id || "", val);
4502
- if (!this.id) {
4503
- this._local = val;
4504
- this._render();
4505
- }
4506
- } catch (err) {
4507
- if (gen !== this._gen) return;
4508
- this._swapGen = 0;
4509
- this._releaseMask();
4510
- this._setError("Could not read that image.");
4511
- console.warn("<image-slot> ingest failed:", err);
4512
- }
4513
- }
4514
- _setError(msg) {
4515
- if (this._err) {
4516
- this._err.remove();
4517
- this._err = null;
4518
- }
4519
- if (!msg) return;
4520
- const d = document.createElement("div");
4521
- d.className = "err";
4522
- d.textContent = msg;
4523
- this.shadowRoot.appendChild(d);
4524
- this._err = d;
4525
- setTimeout(() => {
4526
- if (this._err === d) {
4527
- d.remove();
4528
- this._err = null;
4529
- }
4530
- }, 3e3);
4531
- }
4532
- // Reframing (pan/resize) is available on any filled slot — the user can
4533
- // always reposition/scale. `fit` only sets the initial baseline (see
4534
- // _geom): contain starts fully-visible, cover starts frame-filling.
4535
- _reframes() {
4536
- return this.hasAttribute("data-filled");
4537
- }
4538
- // The single release discipline for the replacement-in-flight mask
4539
- // (data-swapping). The mask comes off only when BOTH hold:
4540
- // - no encode is pending (_swapGen) — mid-encode the stored value
4541
- // still resolves to the old image, so any reveal paints it;
4542
- // - the frame img has settled on its current src — an unsettled src
4543
- // means some replacement is still in flight (e.g. a remote pick),
4544
- // whoever started it, and revealing would paint the previous
4545
- // frame. The load/error listeners pass settled=true (the event IS
4546
- // the settlement signal, per spec complete is true by then);
4547
- // other callers rely on the complete flag (covers loaded AND
4548
- // failed).
4549
- // Every release path funnels through here EXCEPT _render's empty
4550
- // branch (the img is being cleared — nothing will ever settle).
4551
- _releaseMask(settled) {
4552
- if (!this._swapGen && !this._loadPending && (settled || this._img.complete)) {
4553
- this.removeAttribute("data-swapping");
4554
- }
4555
- }
4556
- // Baseline geometry, shared by clamp/apply/resize. `base` is the scale at
4557
- // view-scale s=1: cover = fill the frame (overflow on the looser axis),
4558
- // contain = fit fully inside (letterboxed). Zooming a contain image past
4559
- // s where it overflows naturally becomes a crop. Null until the img has
4560
- // loaded (naturalWidth is 0 before that) or when the slot has no layout
4561
- // box — ResizeObserver fires with a 0×0 rect under display:none, and
4562
- // clamping against a degenerate 1×1 frame would silently pull the stored
4563
- // pan toward zero.
4564
- _geom() {
4565
- const iw = this._img.naturalWidth, ih = this._img.naturalHeight;
4566
- const fw = this.clientWidth, fh = this.clientHeight;
4567
- if (!iw || !ih || !fw || !fh) return null;
4568
- const contain = (this.getAttribute("fit") || "cover").toLowerCase() === "contain";
4569
- const base = contain ? Math.min(fw / iw, fh / ih) : Math.max(fw / iw, fh / ih);
4570
- return { iw, ih, fw, fh, base };
4571
- }
4572
- _clampView() {
4573
- const g = this._geom();
4574
- if (!g) return;
4575
- const mx = Math.max(0, (g.iw * g.base * this._view.s / g.fw - 1) * 50);
4576
- const my = Math.max(0, (g.ih * g.base * this._view.s / g.fh - 1) * 50);
4577
- this._view.x = Math.max(-mx, Math.min(mx, this._view.x));
4578
- this._view.y = Math.max(-my, Math.min(my, this._view.y));
4579
- }
4580
- _applyView() {
4581
- const g = this._geom();
4582
- if (this.hasAttribute("data-reframe")) {
4583
- let onTop = false;
4584
- try {
4585
- onTop = this._ctl.matches(":popover-open");
4586
- } catch {
4587
- }
4588
- if (onTop) {
4589
- const r = this.getBoundingClientRect();
4590
- this._ctl.style.left = r.right - 8 + "px";
4591
- this._ctl.style.top = r.top + 8 + "px";
4592
- }
4593
- }
4594
- if (!g) {
4595
- const contain = (this.getAttribute("fit") || "cover").toLowerCase() === "contain";
4596
- this._img.style.width = "100%";
4597
- this._img.style.height = "100%";
4598
- this._img.style.left = "50%";
4599
- this._img.style.top = "50%";
4600
- this._img.style.objectFit = contain ? "contain" : "cover";
4601
- return;
4602
- }
4603
- const k = g.base * this._view.s;
4604
- const w = g.iw * k / g.fw * 100 + "%";
4605
- const h = g.ih * k / g.fh * 100 + "%";
4606
- const l = 50 + this._view.x + "%";
4607
- const t = 50 + this._view.y + "%";
4608
- this._img.style.width = w;
4609
- this._img.style.height = h;
4610
- this._img.style.left = l;
4611
- this._img.style.top = t;
4612
- this._img.style.objectFit = "";
4613
- if (this.hasAttribute("data-reframe")) {
4614
- const r = this.getBoundingClientRect();
4615
- const sx = g.fw ? r.width / g.fw : 1;
4616
- const sy = g.fh ? r.height / g.fh : 1;
4617
- this._spill.style.width = g.iw * k * sx + "px";
4618
- this._spill.style.height = g.ih * k * sy + "px";
4619
- this._spill.style.left = r.left + (50 + this._view.x) / 100 * r.width + "px";
4620
- this._spill.style.top = r.top + (50 + this._view.y) / 100 * r.height + "px";
4621
- }
4622
- }
4623
- _commitView() {
4624
- const v = { s: this._view.s, x: this._view.x, y: this._view.y };
4625
- if (this._userUrl) v.u = this._userUrl;
4626
- if (this.id) setSlot(this.id, v);
4627
- else {
4628
- this._local = v;
4629
- }
4630
- }
4631
- _render() {
4632
- const mask = this.getAttribute("mask");
4633
- const shape = (this.getAttribute("shape") || "rounded").toLowerCase();
4634
- let radius = "";
4635
- if (shape === "circle") radius = "50%";
4636
- else if (shape === "pill") radius = "9999px";
4637
- else if (shape === "rounded") {
4638
- const n = parseFloat(this.getAttribute("radius"));
4639
- radius = (Number.isFinite(n) ? n : 12) + "px";
4640
- }
4641
- this._frame.style.borderRadius = mask ? "" : radius;
4642
- this._frame.style.clipPath = mask || "";
4643
- this._ring.style.borderRadius = mask ? "" : radius;
4644
- this._ring.style.display = mask ? "none" : "";
4645
- const editable = !!(window.omelette && window.omelette.writeFile);
4646
- this.toggleAttribute("data-editable", editable);
4647
- this._sub.style.display = editable ? "" : "none";
4648
- let stored = this.id ? getSlot(this.id) : this._local;
4649
- if (stored && stored.u && !/^data:image\//i.test(stored.u)) stored = null;
4650
- const srcAttr = this.getAttribute("src") || "";
4651
- this._userUrl = stored && stored.u || null;
4652
- const url = this._userUrl || srcAttr;
4653
- if (!this.hasAttribute("data-reframe")) {
4654
- this._view = {
4655
- s: stored && Number.isFinite(stored.s) ? clampS(stored.s) : 1,
4656
- x: stored && Number.isFinite(stored.x) ? stored.x : 0,
4657
- y: stored && Number.isFinite(stored.y) ? stored.y : 0
4658
- };
4659
- }
4660
- this._cap.textContent = this.getAttribute("placeholder") || "Drop an image";
4661
- const credit = (this.getAttribute("credit") || "").trim();
4662
- const attrError = !!(!credit && !this._userUrl && srcAttr && isUnsplashHost(srcAttr));
4663
- this.toggleAttribute("data-attribution-error", attrError);
4664
- if (url && !attrError) {
4665
- const prev = this._img.getAttribute("src");
4666
- if (prev !== url) {
4667
- if (prev || this._hidShowing) this.setAttribute("data-swapping", "");
4668
- this._loadPending = true;
4669
- this._img.src = url;
4670
- this._ghost.src = url;
4671
- } else {
4672
- this._releaseMask();
4673
- }
4674
- this._hidShowing = false;
4675
- this._img.style.display = "block";
4676
- this._empty.style.display = "none";
4677
- this.setAttribute("data-filled", "");
4678
- this._clampView();
4679
- this._applyView();
4680
- } else {
4681
- this.removeAttribute("data-swapping");
4682
- this._loadPending = false;
4683
- this._hidShowing = attrError && !!this._img.getAttribute("src");
4684
- this._img.style.display = "none";
4685
- this._img.removeAttribute("src");
4686
- this._ghost.removeAttribute("src");
4687
- this._empty.style.display = attrError ? "none" : "flex";
4688
- this.removeAttribute("data-filled");
4689
- }
4690
- const showCredit = !!(url && credit && !this._userUrl && !attrError);
4691
- this._credit.textContent = "";
4692
- if (showCredit) {
4693
- let href = "";
4694
- const rawHref = this.getAttribute("credit-href") || "";
4695
- if (rawHref) {
4696
- try {
4697
- const u = new URL(rawHref, document.baseURI);
4698
- if (u.protocol === "http:" || u.protocol === "https:") {
4699
- href = withReferral(u.href);
4700
- }
4701
- } catch {
4702
- }
4703
- }
4704
- const mkLink = (text, linkHref) => {
4705
- const a = document.createElement("a");
4706
- a.setAttribute("target", "_blank");
4707
- a.setAttribute("rel", "noopener noreferrer");
4708
- a.setAttribute("href", linkHref);
4709
- a.textContent = text;
4710
- return a;
4711
- };
4712
- const m = /^Photo by (.+) on Unsplash$/.exec(credit);
4713
- if (m) {
4714
- this._credit.appendChild(document.createTextNode("Photo by "));
4715
- this._credit.appendChild(
4716
- href ? mkLink(m[1], href) : document.createTextNode(m[1])
4717
- );
4718
- this._credit.appendChild(document.createTextNode(" on "));
4719
- this._credit.appendChild(mkLink("Unsplash", UNSPLASH_HOMEPAGE_HREF));
4720
- } else if (href) {
4721
- this._credit.appendChild(mkLink(credit, href));
4722
- } else {
4723
- this._credit.textContent = credit;
4724
- }
4725
- }
4726
- this.toggleAttribute("data-credit", showCredit);
4727
- }
4728
- }
4729
- if (!customElements.get("image-slot")) {
4730
- customElements.define("image-slot", ImageSlot);
4731
- }
4732
- })();
4733
- })(); } catch (e) { __ds_ns.__errors.push({ path: "image-slot.js", error: String((e && e.message) || e) }); }
4734
-
4735
4872
  // tokens/tokens.js
4736
4873
  try { (() => {
4737
4874
  const tokens = {
4738
4875
  "$meta": {
4739
4876
  "system": "CyberSkill Design System",
4740
- "version": "1.1.1",
4877
+ "version": "1.3.8",
4741
4878
  "generated": "2026-07-23",
4742
4879
  "note": "Generated from tokens/*.css. Root grouped by category; theme/element scopes carry overrides only. Regenerate after any token change (see docs/conventions.md)."
4743
4880
  },
@@ -4756,6 +4893,10 @@ const tokens = {
4756
4893
  "--cs-color-semantic-danger": "#B42318",
4757
4894
  "--cs-color-semantic-warning": "#92400E",
4758
4895
  "--cs-color-semantic-info": "#1D4ED8",
4896
+ "--cs-color-semantic-success-fg": "#FFFFFF",
4897
+ "--cs-color-semantic-danger-fg": "#FFFFFF",
4898
+ "--cs-color-semantic-warning-fg": "#FFFFFF",
4899
+ "--cs-color-semantic-info-fg": "#FFFFFF",
4759
4900
  "--cs-color-accent-ochre": "#F4BA17",
4760
4901
  "--cs-color-border-default": "#E7D9C6",
4761
4902
  "--cs-color-link": "#6E3B0E",
@@ -4785,7 +4926,8 @@ const tokens = {
4785
4926
  "--cs-component-textfield-border-default": "#BFA58F",
4786
4927
  "--cs-component-textfield-paddingY": "10px",
4787
4928
  "--cs-component-textfield-paddingX": "12px",
4788
- "--cs-component-textfield-minHeight": "44px"
4929
+ "--cs-component-textfield-minHeight": "44px",
4930
+ "--cs-focus-ring": "3px"
4789
4931
  },
4790
4932
  "accent": {
4791
4933
  "--cs-accent": "#F4BA17",
@@ -4796,7 +4938,8 @@ const tokens = {
4796
4938
  "--cs-accent-ink": "#45210E",
4797
4939
  "--cs-accent-glow": "rgba(244, 186, 23, 0.48)",
4798
4940
  "--cs-accent-grad-a": "#F4BA17",
4799
- "--cs-accent-grad-b": "#FCAF91"
4941
+ "--cs-accent-grad-b": "#F3B958",
4942
+ "--cs-accent-on-strong": "#1A1108"
4800
4943
  },
4801
4944
  "depth": {
4802
4945
  "--cs-depth-bg": "0",
@@ -4804,7 +4947,9 @@ const tokens = {
4804
4947
  "--cs-depth-card": "10",
4805
4948
  "--cs-depth-nav": "50",
4806
4949
  "--cs-depth-modal": "100",
4807
- "--cs-depth-toast": "200"
4950
+ "--cs-depth-toast": "200",
4951
+ "--cs-depth-dropdown": "60",
4952
+ "--cs-depth-overlay": "80"
4808
4953
  },
4809
4954
  "glass": {
4810
4955
  "--cs-glass-whisper-blur": "8px",
@@ -4862,6 +5007,7 @@ const tokens = {
4862
5007
  },
4863
5008
  "breakpoint": {
4864
5009
  "--cs-breakpoint-xs": "0px",
5010
+ "--cs-breakpoint-phone": "360px",
4865
5011
  "--cs-breakpoint-sm": "640px",
4866
5012
  "--cs-breakpoint-md": "768px",
4867
5013
  "--cs-breakpoint-lg": "1024px",
@@ -4869,7 +5015,8 @@ const tokens = {
4869
5015
  "--cs-breakpoint-2xl": "1536px"
4870
5016
  },
4871
5017
  "container": {
4872
- "--cs-container-max": "1320px"
5018
+ "--cs-container-max": "1320px",
5019
+ "--cs-prose-measure": "45rem"
4873
5020
  },
4874
5021
  "font": {
4875
5022
  "--cs-font-family-ui": "'Be Vietnam Pro', ui-sans-serif, system-ui, sans-serif",
@@ -4883,6 +5030,7 @@ const tokens = {
4883
5030
  "--cs-font-lineHeight-body": "1.5",
4884
5031
  "--cs-font-lineHeight-heading": "1.35",
4885
5032
  "--cs-font-lineHeight-tight": "1.15",
5033
+ "--cs-font-size-2xs": "0.6875rem",
4886
5034
  "--cs-font-size-xs": "0.75rem",
4887
5035
  "--cs-font-size-sm": "0.875rem",
4888
5036
  "--cs-font-size-base": "1rem",
@@ -4903,6 +5051,7 @@ const tokens = {
4903
5051
  "--cs-text-body-lg": "var(--cs-font-size-md)",
4904
5052
  "--cs-text-body": "var(--cs-font-size-base)",
4905
5053
  "--cs-text-secondary": "var(--cs-font-size-sm)",
5054
+ "--cs-text-micro": "var(--cs-font-size-2xs)",
4906
5055
  "--cs-text-caption": "var(--cs-font-size-xs)",
4907
5056
  "--cs-text-eyebrow": "var(--cs-font-size-xs)"
4908
5057
  },
@@ -4921,6 +5070,12 @@ const tokens = {
4921
5070
  "--cs-icon-sm": "16px",
4922
5071
  "--cs-icon-md": "20px",
4923
5072
  "--cs-icon-lg": "24px"
5073
+ },
5074
+ "doc": {
5075
+ "--cs-doc-ink": "#1A1614",
5076
+ "--cs-doc-slate": "#3F4C55",
5077
+ "--cs-doc-fill": "#FDE68A",
5078
+ "--cs-doc-fill-ink": "#1A1614"
4924
5079
  }
4925
5080
  },
4926
5081
  "themes": {
@@ -4936,6 +5091,10 @@ const tokens = {
4936
5091
  "--cs-color-semantic-danger": "#F87171",
4937
5092
  "--cs-color-semantic-warning": "#FBBF24",
4938
5093
  "--cs-color-semantic-info": "#7CB2FB",
5094
+ "--cs-color-semantic-success-fg": "#1a1108",
5095
+ "--cs-color-semantic-danger-fg": "#1a1108",
5096
+ "--cs-color-semantic-warning-fg": "#1a1108",
5097
+ "--cs-color-semantic-info-fg": "#1a1108",
4939
5098
  "--cs-color-border-default": "#4a3a2c",
4940
5099
  "--cs-color-link": "#f4ba17",
4941
5100
  "--cs-color-link-hover": "#f5ead9",
@@ -4960,6 +5119,10 @@ const tokens = {
4960
5119
  "--cs-color-semantic-danger": "#F87171",
4961
5120
  "--cs-color-semantic-warning": "#FBBF24",
4962
5121
  "--cs-color-semantic-info": "#7CB2FB",
5122
+ "--cs-color-semantic-success-fg": "#1a1108",
5123
+ "--cs-color-semantic-danger-fg": "#1a1108",
5124
+ "--cs-color-semantic-warning-fg": "#1a1108",
5125
+ "--cs-color-semantic-info-fg": "#1a1108",
4963
5126
  "--cs-color-border-default": "#4a3a2c",
4964
5127
  "--cs-color-link": "#f4ba17",
4965
5128
  "--cs-color-link-hover": "#f5ead9",
@@ -4979,35 +5142,38 @@ const tokens = {
4979
5142
  "--cs-accent-strong": "#90825E",
4980
5143
  "--cs-accent-bright": "#F6EBCC",
4981
5144
  "--cs-accent-on": "#292414",
5145
+ "--cs-accent-on-strong": "#1A1108",
4982
5146
  "--cs-accent-tint": "#FBF7EA",
4983
5147
  "--cs-accent-ink": "#292414",
4984
5148
  "--cs-accent-glow": "rgba(248, 233, 192, 0.48)",
4985
5149
  "--cs-accent-grad-a": "#F8E9C0",
4986
- "--cs-accent-grad-b": "#FCAF91",
5150
+ "--cs-accent-grad-b": "#F3B958",
4987
5151
  "--cs-color-text-accent": "#90825E",
4988
- "--cs-color-link": "#90825E"
5152
+ "--cs-color-link": "#7B6D4A"
4989
5153
  },
4990
5154
  "tho": {
4991
5155
  "--cs-accent": "#F4BA17",
4992
5156
  "--cs-accent-strong": "#A47D17",
4993
5157
  "--cs-accent-bright": "#F4BA17",
4994
5158
  "--cs-accent-on": "#45210E",
5159
+ "--cs-accent-on-strong": "#1A1108",
4995
5160
  "--cs-accent-tint": "#FBF4E9",
4996
5161
  "--cs-accent-ink": "#45210E",
4997
5162
  "--cs-accent-glow": "rgba(244, 186, 23, 0.48)",
4998
5163
  "--cs-accent-grad-a": "#F4BA17",
4999
- "--cs-accent-grad-b": "#FCAF91"
5164
+ "--cs-accent-grad-b": "#F3B958"
5000
5165
  },
5001
5166
  "tho.clay": {
5002
5167
  "--cs-accent": "#513801",
5003
5168
  "--cs-accent-strong": "#201300",
5004
5169
  "--cs-accent-bright": "#DEC7A2",
5005
5170
  "--cs-accent-on": "#2D220E",
5171
+ "--cs-accent-on-strong": "#FFFFFF",
5006
5172
  "--cs-accent-tint": "#FFF6E7",
5007
5173
  "--cs-accent-ink": "#2D220E",
5008
5174
  "--cs-accent-glow": "rgba(81, 56, 1, 0.48)",
5009
5175
  "--cs-accent-grad-a": "#513801",
5010
- "--cs-accent-grad-b": "#FCAF91",
5176
+ "--cs-accent-grad-b": "#F3B958",
5011
5177
  "--cs-color-text-accent": "#201300",
5012
5178
  "--cs-color-link": "#201300"
5013
5179
  },
@@ -5016,32 +5182,35 @@ const tokens = {
5016
5182
  "--cs-accent-strong": "#967C75",
5017
5183
  "--cs-accent-bright": "#FCE6DF",
5018
5184
  "--cs-accent-on": "#2C211E",
5185
+ "--cs-accent-on-strong": "#1A1108",
5019
5186
  "--cs-accent-tint": "#FEF4F2",
5020
5187
  "--cs-accent-ink": "#2C211E",
5021
5188
  "--cs-accent-glow": "rgba(255, 225, 216, 0.48)",
5022
5189
  "--cs-accent-grad-a": "#FFE1D8",
5023
5190
  "--cs-accent-grad-b": "#F4BA17",
5024
5191
  "--cs-color-text-accent": "#967C75",
5025
- "--cs-color-link": "#967C75"
5192
+ "--cs-color-link": "#846B64"
5026
5193
  },
5027
5194
  "hoa": {
5028
5195
  "--cs-accent": "#FCAF91",
5029
5196
  "--cs-accent-strong": "#B47054",
5030
5197
  "--cs-accent-bright": "#FED3C3",
5031
5198
  "--cs-accent-on": "#381B10",
5199
+ "--cs-accent-on-strong": "#1A1108",
5032
5200
  "--cs-accent-tint": "#FFF4F0",
5033
5201
  "--cs-accent-ink": "#381B10",
5034
5202
  "--cs-accent-glow": "rgba(252, 175, 145, 0.48)",
5035
5203
  "--cs-accent-grad-a": "#FCAF91",
5036
5204
  "--cs-accent-grad-b": "#F4BA17",
5037
5205
  "--cs-color-text-accent": "#B47054",
5038
- "--cs-color-link": "#B47054"
5206
+ "--cs-color-link": "#A15E43"
5039
5207
  },
5040
5208
  "hoa.lava": {
5041
5209
  "--cs-accent": "#622B06",
5042
5210
  "--cs-accent-strong": "#280D00",
5043
5211
  "--cs-accent-bright": "#F1BEA4",
5044
5212
  "--cs-accent-on": "#361D0F",
5213
+ "--cs-accent-on-strong": "#FFFFFF",
5045
5214
  "--cs-accent-tint": "#FFF4EF",
5046
5215
  "--cs-accent-ink": "#361D0F",
5047
5216
  "--cs-accent-glow": "rgba(98, 43, 6, 0.48)",
@@ -5055,32 +5224,35 @@ const tokens = {
5055
5224
  "--cs-accent-strong": "#5A8A90",
5056
5225
  "--cs-accent-bright": "#CDF4F9",
5057
5226
  "--cs-accent-on": "#13282B",
5227
+ "--cs-accent-on-strong": "#1A1108",
5058
5228
  "--cs-accent-tint": "#EBFAFC",
5059
5229
  "--cs-accent-ink": "#13282B",
5060
5230
  "--cs-accent-glow": "rgba(192, 245, 252, 0.48)",
5061
5231
  "--cs-accent-grad-a": "#C0F5FC",
5062
5232
  "--cs-accent-grad-b": "#A3D768",
5063
5233
  "--cs-color-text-accent": "#5A8A90",
5064
- "--cs-color-link": "#5A8A90"
5234
+ "--cs-color-link": "#48787E"
5065
5235
  },
5066
5236
  "thuy": {
5067
5237
  "--cs-accent": "#2EDBF7",
5068
5238
  "--cs-accent-strong": "#108EA0",
5069
5239
  "--cs-accent-bright": "#99EEFF",
5070
5240
  "--cs-accent-on": "#012A30",
5241
+ "--cs-accent-on-strong": "#1A1108",
5071
5242
  "--cs-accent-tint": "#E7FBFF",
5072
5243
  "--cs-accent-ink": "#012A30",
5073
5244
  "--cs-accent-glow": "rgba(46, 219, 247, 0.48)",
5074
5245
  "--cs-accent-grad-a": "#2EDBF7",
5075
5246
  "--cs-accent-grad-b": "#A3D768",
5076
5247
  "--cs-color-text-accent": "#108EA0",
5077
- "--cs-color-link": "#108EA0"
5248
+ "--cs-color-link": "#027B8B"
5078
5249
  },
5079
5250
  "thuy.ocean": {
5080
5251
  "--cs-accent": "#004554",
5081
5252
  "--cs-accent-strong": "#011A21",
5082
5253
  "--cs-accent-bright": "#A8D3E0",
5083
5254
  "--cs-accent-on": "#10282F",
5255
+ "--cs-accent-on-strong": "#FFFFFF",
5084
5256
  "--cs-accent-tint": "#EAFAFF",
5085
5257
  "--cs-accent-ink": "#10282F",
5086
5258
  "--cs-accent-glow": "rgba(0, 69, 84, 0.48)",
@@ -5094,32 +5266,35 @@ const tokens = {
5094
5266
  "--cs-accent-strong": "#7C8965",
5095
5267
  "--cs-accent-bright": "#E5F1D2",
5096
5268
  "--cs-accent-on": "#212617",
5269
+ "--cs-accent-on-strong": "#1A1108",
5097
5270
  "--cs-accent-tint": "#F4F9ED",
5098
5271
  "--cs-accent-ink": "#212617",
5099
5272
  "--cs-accent-glow": "rgba(225, 241, 200, 0.48)",
5100
5273
  "--cs-accent-grad-a": "#E1F1C8",
5101
5274
  "--cs-accent-grad-b": "#FCAF91",
5102
5275
  "--cs-color-text-accent": "#7C8965",
5103
- "--cs-color-link": "#7C8965"
5276
+ "--cs-color-link": "#687451"
5104
5277
  },
5105
5278
  "moc": {
5106
5279
  "--cs-accent": "#A3D768",
5107
5280
  "--cs-accent-strong": "#629122",
5108
5281
  "--cs-accent-bright": "#C5ED9C",
5109
5282
  "--cs-accent-on": "#182A00",
5283
+ "--cs-accent-on-strong": "#1A1108",
5110
5284
  "--cs-accent-tint": "#EFFCE3",
5111
5285
  "--cs-accent-ink": "#182A00",
5112
5286
  "--cs-accent-glow": "rgba(163, 215, 104, 0.48)",
5113
5287
  "--cs-accent-grad-a": "#A3D768",
5114
5288
  "--cs-accent-grad-b": "#FCAF91",
5115
5289
  "--cs-color-text-accent": "#629122",
5116
- "--cs-color-link": "#629122"
5290
+ "--cs-color-link": "#517B13"
5117
5291
  },
5118
5292
  "moc.forest": {
5119
5293
  "--cs-accent": "#1A4A01",
5120
5294
  "--cs-accent-strong": "#051D00",
5121
5295
  "--cs-accent-bright": "#B0D8A3",
5122
5296
  "--cs-accent-on": "#162A0E",
5297
+ "--cs-accent-on-strong": "#FFFFFF",
5123
5298
  "--cs-accent-tint": "#ECFDE7",
5124
5299
  "--cs-accent-ink": "#162A0E",
5125
5300
  "--cs-accent-glow": "rgba(26, 74, 1, 0.48)",
@@ -5133,32 +5308,35 @@ const tokens = {
5133
5308
  "--cs-accent-strong": "#947F65",
5134
5309
  "--cs-accent-bright": "#FBE8D2",
5135
5310
  "--cs-accent-on": "#2B2217",
5311
+ "--cs-accent-on-strong": "#1A1108",
5136
5312
  "--cs-accent-tint": "#FDF5ED",
5137
5313
  "--cs-accent-ink": "#2B2217",
5138
5314
  "--cs-accent-glow": "rgba(254, 229, 199, 0.48)",
5139
5315
  "--cs-accent-grad-a": "#FEE5C7",
5140
5316
  "--cs-accent-grad-b": "#2EDBF7",
5141
5317
  "--cs-color-text-accent": "#947F65",
5142
- "--cs-color-link": "#947F65"
5318
+ "--cs-color-link": "#826D54"
5143
5319
  },
5144
5320
  "kim": {
5145
5321
  "--cs-accent": "#F3B958",
5146
5322
  "--cs-accent-strong": "#AC780B",
5147
5323
  "--cs-accent-bright": "#FDD89F",
5148
5324
  "--cs-accent-on": "#312002",
5325
+ "--cs-accent-on-strong": "#1A1108",
5149
5326
  "--cs-accent-tint": "#FFF5E7",
5150
5327
  "--cs-accent-ink": "#312002",
5151
5328
  "--cs-accent-glow": "rgba(243, 185, 88, 0.48)",
5152
5329
  "--cs-accent-grad-a": "#F3B958",
5153
5330
  "--cs-accent-grad-b": "#2EDBF7",
5154
5331
  "--cs-color-text-accent": "#AC780B",
5155
- "--cs-color-link": "#AC780B"
5332
+ "--cs-color-link": "#966700"
5156
5333
  },
5157
5334
  "kim.steel": {
5158
5335
  "--cs-accent": "#503901",
5159
5336
  "--cs-accent-strong": "#1F1400",
5160
5337
  "--cs-accent-bright": "#DCC8A2",
5161
5338
  "--cs-accent-on": "#2D220E",
5339
+ "--cs-accent-on-strong": "#FFFFFF",
5162
5340
  "--cs-accent-tint": "#FEF6E7",
5163
5341
  "--cs-accent-ink": "#2D220E",
5164
5342
  "--cs-accent-glow": "rgba(80, 57, 1, 0.48)",
@@ -5172,424 +5350,6 @@ const tokens = {
5172
5350
  Object.assign(__ds_scope, { tokens, __ds_default_tokens_tokens_9gob35: tokens });
5173
5351
  })(); } catch (e) { __ds_ns.__errors.push({ path: "tokens/tokens.js", error: String((e && e.message) || e) }); }
5174
5352
 
5175
- // ui_kits/status-hub/StatusHub.jsx
5176
- try { (() => {
5177
- const { useState, useMemo } = React;
5178
- const ICON = {
5179
- search: "M11 4a7 7 0 1 0 4.2 12.6L20 21l1.4-1.4-4.4-4.4A7 7 0 0 0 11 4zm0 2a5 5 0 1 1 0 10 5 5 0 0 1 0-10z",
5180
- close: "M6 6l12 12M18 6L6 18",
5181
- sun: null,
5182
- moon: null
5183
- };
5184
- function Glyph({ d, size = 18 }) {
5185
- return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.9", strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d }));
5186
- }
5187
- function SearchGlyph() {
5188
- return /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "currentColor", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: ICON.search }));
5189
- }
5190
- function ThemeGlyph({ dark }) {
5191
- return dark ? /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("circle", { cx: "12", cy: "12", r: "4" }), /* @__PURE__ */ React.createElement("path", { d: "M12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4" })) : /* @__PURE__ */ React.createElement("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round", strokeLinejoin: "round" }, /* @__PURE__ */ React.createElement("path", { d: "M20 13.5A8 8 0 1 1 10.5 4a6.5 6.5 0 0 0 9.5 9.5z" }));
5192
- }
5193
- const T = {
5194
- en: {
5195
- sub: "CyberSkill · one page, three lenses · Hiện Thực Hoá Ý Chí",
5196
- updated: "Updated",
5197
- portfolio: "Portfolio",
5198
- projects: "Projects",
5199
- inprog: "In progress",
5200
- shipped: "Shipped",
5201
- blocked: "Blocked",
5202
- planned: "Planned",
5203
- avg: "Avg progress",
5204
- velocity: "Velocity · tasks shipped / week",
5205
- perShipped: " shipped",
5206
- nowTitle: "Now shipping",
5207
- search: "Search projects…",
5208
- view: "View",
5209
- lenses: { board: "Board", table: "Table", releases: "Releases" },
5210
- status: "Status",
5211
- all: "All",
5212
- of: "of",
5213
- key: "Key",
5214
- project: "Project",
5215
- squad: "Squad",
5216
- owner: "Owner",
5217
- progress: "Progress",
5218
- thisWeek: "This week",
5219
- tasks: "Tasks",
5220
- risk: "Risk",
5221
- close: "Close",
5222
- langBtn: "VN",
5223
- theme: "Toggle theme"
5224
- },
5225
- vi: {
5226
- sub: "CyberSkill · một trang, ba lăng kính · Hiện Thực Hoá Ý Chí",
5227
- updated: "Cập nhật",
5228
- portfolio: "Danh mục",
5229
- projects: "Dự án",
5230
- inprog: "Đang làm",
5231
- shipped: "Đã ra mắt",
5232
- blocked: "Bị chặn",
5233
- planned: "Dự kiến",
5234
- avg: "Tiến độ TB",
5235
- velocity: "Tốc độ · task giao / tuần",
5236
- perShipped: " đã giao",
5237
- nowTitle: "Đang ra mắt",
5238
- search: "Tìm dự án…",
5239
- view: "Chế độ xem",
5240
- lenses: { board: "Bảng công việc", table: "Bảng dữ liệu", releases: "Phát hành" },
5241
- status: "Trạng thái",
5242
- all: "Tất cả",
5243
- of: "trên",
5244
- key: "Mã",
5245
- project: "Dự án",
5246
- squad: "Nhóm",
5247
- owner: "Phụ trách",
5248
- progress: "Tiến độ",
5249
- thisWeek: "Tuần này",
5250
- tasks: "Công việc",
5251
- risk: "Rủi ro",
5252
- close: "Đóng",
5253
- langBtn: "EN",
5254
- theme: "Đổi giao diện"
5255
- }
5256
- };
5257
- const statusLabels = (t) => ({ done: t.shipped, active: t.inprog, hold: t.blocked, todo: t.planned });
5258
- function Segments({ seg, className }) {
5259
- return /* @__PURE__ */ React.createElement("div", { className }, seg.done > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-done", style: { width: seg.done + "%" } }), seg.active > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-active", style: { width: seg.active + "%" } }), seg.hold > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-hold", style: { width: seg.hold + "%" } }), seg.todo > 0 && /* @__PURE__ */ React.createElement("i", { className: "seg-todo", style: { width: seg.todo + "%" } }));
5260
- }
5261
- function Drawer({ p, onClose, t }) {
5262
- if (!p) return null;
5263
- const SL = statusLabels(t);
5264
- return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("div", { className: "scrim", onClick: onClose }), /* @__PURE__ */ React.createElement("aside", { className: "drawer", role: "dialog", "aria-modal": "true", "aria-label": p.name }, /* @__PURE__ */ React.createElement("div", { className: "dw-h" }, /* @__PURE__ */ React.createElement("div", { className: "row" }, /* @__PURE__ */ React.createElement("span", { className: "mono muted", style: { fontWeight: 700 } }, p.key), /* @__PURE__ */ React.createElement("span", { className: "pill " + p.status }, SL[p.status]), /* @__PURE__ */ React.createElement("button", { className: "dw-x", onClick: onClose, "aria-label": t.close }, /* @__PURE__ */ React.createElement(Glyph, { d: ICON.close, size: 20 }))), /* @__PURE__ */ React.createElement("h2", null, p.name)), /* @__PURE__ */ React.createElement("div", { className: "dw-b" }, /* @__PURE__ */ React.createElement("p", { className: "muted", style: { marginTop: 0 } }, p.blurb), /* @__PURE__ */ React.createElement("dl", { className: "meta" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.owner), /* @__PURE__ */ React.createElement("dd", null, p.owner)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.squad), /* @__PURE__ */ React.createElement("dd", null, p.squad)), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.progress), /* @__PURE__ */ React.createElement("dd", null, p.pct, "%")), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("dt", null, t.updated), /* @__PURE__ */ React.createElement("dd", null, p.updated))), /* @__PURE__ */ React.createElement(Segments, { seg: p.seg, className: "minibar" }), /* @__PURE__ */ React.createElement("div", { className: "sect" }, /* @__PURE__ */ React.createElement("h3", null, t.tasks), /* @__PURE__ */ React.createElement("div", { className: "task-chips" }, p.tasks.map((tk) => /* @__PURE__ */ React.createElement("span", { key: tk[0], className: "chip " + tk[2], title: tk[1] }, tk[0]))), /* @__PURE__ */ React.createElement("ul", { className: "tasks" }, p.tasks.map((tk) => /* @__PURE__ */ React.createElement("li", { key: tk[0] }, tk[1], " — ", /* @__PURE__ */ React.createElement("span", { className: "muted" }, SL[tk[2]]))))), p.risk ? /* @__PURE__ */ React.createElement("div", { className: "sect" }, /* @__PURE__ */ React.createElement("h3", null, t.risk), /* @__PURE__ */ React.createElement("div", { className: "risk" }, p.risk)) : null)));
5265
- }
5266
- function StatusHub() {
5267
- const D = window.SH_DATA;
5268
- const [dark, setDark] = useState(false);
5269
- const [lang, setLang] = useState("en");
5270
- const [lens, setLens] = useState("board");
5271
- const [q, setQ] = useState("");
5272
- const [facet, setFacet] = useState("all");
5273
- const [kpi, setKpi] = useState(null);
5274
- const [sel, setSel] = useState(null);
5275
- const t = T[lang];
5276
- const SL = statusLabels(t);
5277
- const VI = lang === "vi" ? window.SH_VI || null : null;
5278
- const loc = (p) => VI ? {
5279
- ...p,
5280
- blurb: VI.blurbs[p.key] || p.blurb,
5281
- risk: VI.risks[p.key] != null ? VI.risks[p.key] : p.risk,
5282
- owner: VI.owners[p.owner] || p.owner,
5283
- squad: VI.squads[p.squad] || p.squad,
5284
- updated: VI.updatedRel[p.updated] || p.updated
5285
- } : p;
5286
- const counts = useMemo(() => {
5287
- const c = { active: 0, done: 0, hold: 0 };
5288
- D.projects.forEach((p) => {
5289
- if (c[p.status] != null) c[p.status]++;
5290
- });
5291
- const avg = Math.round(D.projects.reduce((a, p) => a + p.pct, 0) / D.projects.length);
5292
- return { total: D.projects.length, ...c, avg };
5293
- }, [D]);
5294
- const shown = useMemo(() => {
5295
- const needle = q.trim().toLowerCase();
5296
- const eff = kpi || (facet === "all" ? null : facet);
5297
- return D.projects.filter((p) => {
5298
- if (eff && p.status !== eff) return false;
5299
- if (!needle) return true;
5300
- return (p.name + " " + p.key + " " + p.blurb).toLowerCase().includes(needle);
5301
- });
5302
- }, [D, q, facet, kpi]);
5303
- const maxV = Math.max(...D.velocity.map((v) => v.n));
5304
- const toggleKpi = (k) => setKpi((cur) => cur === k ? null : k);
5305
- return /* @__PURE__ */ React.createElement("div", { className: "sh", lang, "data-theme": dark ? "dark" : void 0 }, /* @__PURE__ */ React.createElement("header", { className: "hd" }, /* @__PURE__ */ React.createElement("div", { className: "hd-in" }, /* @__PURE__ */ React.createElement("div", { className: "hd-id" }, /* @__PURE__ */ React.createElement("span", { className: "hd-mark" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/logo-mark.svg", alt: "" })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h1", null, "Status Hub"), /* @__PURE__ */ React.createElement("p", { className: "hd-sub" }, t.sub))), /* @__PURE__ */ React.createElement("div", { className: "hd-side" }, /* @__PURE__ */ React.createElement("p", { className: "hd-meta" }, t.updated, /* @__PURE__ */ React.createElement("br", null), VI ? VI.updated : D.updated), /* @__PURE__ */ React.createElement("button", { className: "btn ghost", onClick: () => setLang((l) => l === "en" ? "vi" : "en"), "aria-label": "Toggle language", style: { fontWeight: 700, fontSize: 13 } }, t.langBtn), /* @__PURE__ */ React.createElement("button", { className: "btn ghost", onClick: () => setDark((v) => !v), "aria-label": t.theme }, /* @__PURE__ */ React.createElement(ThemeGlyph, { dark }))))), /* @__PURE__ */ React.createElement("div", { className: "wrap" }, /* @__PURE__ */ React.createElement("div", { className: "deck" }, /* @__PURE__ */ React.createElement("section", { className: "panel" }, /* @__PURE__ */ React.createElement("h2", null, t.portfolio), /* @__PURE__ */ React.createElement("div", { className: "kpis" }, /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === null ? "1" : "0", onClick: () => setKpi(null) }, /* @__PURE__ */ React.createElement("b", null, counts.total), /* @__PURE__ */ React.createElement("span", null, t.projects)), /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === "active" ? "1" : "0", onClick: () => toggleKpi("active") }, /* @__PURE__ */ React.createElement("b", null, counts.active), /* @__PURE__ */ React.createElement("span", null, t.inprog)), /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === "done" ? "1" : "0", onClick: () => toggleKpi("done") }, /* @__PURE__ */ React.createElement("b", null, counts.done), /* @__PURE__ */ React.createElement("span", null, t.shipped)), /* @__PURE__ */ React.createElement("button", { className: "kpi", "data-on": kpi === "hold" ? "1" : "0", onClick: () => toggleKpi("hold") }, /* @__PURE__ */ React.createElement("b", null, counts.hold), /* @__PURE__ */ React.createElement("span", null, t.blocked)), /* @__PURE__ */ React.createElement("button", { className: "kpi", onClick: () => setKpi(null) }, /* @__PURE__ */ React.createElement("b", null, counts.avg, "%"), /* @__PURE__ */ React.createElement("span", null, t.avg))), /* @__PURE__ */ React.createElement(Segments, { seg: { done: 60, active: 22, hold: 6, todo: 12 }, className: "bar-seg" }), /* @__PURE__ */ React.createElement("div", { className: "legend" }, /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-color-semantic-success)" } }), t.shipped), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-color-brand-ochre)" } }), t.inprog), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-color-semantic-danger)" } }), t.blocked), /* @__PURE__ */ React.createElement("span", null, /* @__PURE__ */ React.createElement("i", { className: "dot", style: { background: "var(--cs-status-todo)" } }), t.planned))), /* @__PURE__ */ React.createElement("section", { className: "panel" }, /* @__PURE__ */ React.createElement("h2", null, t.velocity), /* @__PURE__ */ React.createElement("div", { className: "spark" }, D.velocity.map((v) => /* @__PURE__ */ React.createElement("a", { key: v.wk, title: v.n + t.perShipped }, /* @__PURE__ */ React.createElement("b", null, v.n), /* @__PURE__ */ React.createElement("i", { style: { height: v.n / maxV * 72 + "px" } }), /* @__PURE__ */ React.createElement("span", null, v.wk)))))), /* @__PURE__ */ React.createElement("div", { className: "now" }, /* @__PURE__ */ React.createElement("h2", null, t.nowTitle), lang === "vi" ? /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("b", null, "v1.6.0 — Liên kết Glass + gói Ngũ Hành v2."), " Liên kết component ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, ".cs-surface-*"), " (tùy chọn), trình kiểm 0 cảnh báo trên cả 15 gói, và kiểm tra lại APCA cho chế độ tối. Ra mắt tuần này.") : /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement("b", null, "v1.6.0 — Glass bindings + element-pack v2."), " Opt-in ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, ".cs-surface-*"), " component bindings, a zero-warning verifier across all 15 packs, and a dark-mode APCA re-check. Ships this week.")), /* @__PURE__ */ React.createElement("div", { className: "bar" }, /* @__PURE__ */ React.createElement("div", { className: "bar-top" }, /* @__PURE__ */ React.createElement("label", { className: "srch" }, /* @__PURE__ */ React.createElement(SearchGlyph, null), /* @__PURE__ */ React.createElement("input", { placeholder: t.search, value: q, onChange: (e) => setQ(e.target.value) })), /* @__PURE__ */ React.createElement("div", { className: "lenses", role: "tablist", "aria-label": t.view }, ["board", "table", "releases"].map((l) => /* @__PURE__ */ React.createElement("button", { key: l, className: "ln", role: "tab", "aria-selected": lens === l, onClick: () => setLens(l) }, t.lenses[l]))), /* @__PURE__ */ React.createElement("label", { className: "facet" }, t.status, /* @__PURE__ */ React.createElement("select", { value: kpi || facet, onChange: (e) => {
5306
- setFacet(e.target.value);
5307
- setKpi(null);
5308
- } }, /* @__PURE__ */ React.createElement("option", { value: "all" }, t.all), /* @__PURE__ */ React.createElement("option", { value: "active" }, t.inprog), /* @__PURE__ */ React.createElement("option", { value: "done" }, t.shipped), /* @__PURE__ */ React.createElement("option", { value: "hold" }, t.blocked))), /* @__PURE__ */ React.createElement("span", { className: "cnt" }, shown.length, " ", t.of, " ", D.projects.length))), lens === "board" && /* @__PURE__ */ React.createElement("div", { className: "grid" }, shown.map((p0) => {
5309
- const p = loc(p0);
5310
- return /* @__PURE__ */ React.createElement("button", { className: "card" + (p.status === "active" ? " hot" : ""), key: p.key, onClick: () => setSel(p0) }, /* @__PURE__ */ React.createElement("div", { className: "card-h" }, /* @__PURE__ */ React.createElement("h3", null, /* @__PURE__ */ React.createElement("span", { className: "k mono" }, p.key), " · ", p.name), /* @__PURE__ */ React.createElement("span", { className: "pct" }, p.pct, "%")), /* @__PURE__ */ React.createElement("p", null, p.blurb), /* @__PURE__ */ React.createElement(Segments, { seg: p.seg, className: "minibar" }), /* @__PURE__ */ React.createElement("div", { className: "task-chips" }, /* @__PURE__ */ React.createElement("span", { className: "pill " + p.status }, SL[p.status]), p.tasks.slice(0, 3).map((tk) => /* @__PURE__ */ React.createElement("span", { key: tk[0], className: "chip " + tk[2] }, tk[0]))));
5311
- })), lens === "table" && /* @__PURE__ */ React.createElement("div", { className: "tbl-wrap" }, /* @__PURE__ */ React.createElement("table", { className: "tbl" }, /* @__PURE__ */ React.createElement("thead", null, /* @__PURE__ */ React.createElement("tr", null, /* @__PURE__ */ React.createElement("th", null, t.key), /* @__PURE__ */ React.createElement("th", null, t.project), /* @__PURE__ */ React.createElement("th", null, t.squad), /* @__PURE__ */ React.createElement("th", null, t.owner), /* @__PURE__ */ React.createElement("th", null, t.progress), /* @__PURE__ */ React.createElement("th", null, t.status))), /* @__PURE__ */ React.createElement("tbody", null, shown.map((p0) => {
5312
- const p = loc(p0);
5313
- return /* @__PURE__ */ React.createElement("tr", { key: p.key, onClick: () => setSel(p0) }, /* @__PURE__ */ React.createElement("td", { className: "mono", style: { fontWeight: 700 } }, p.key), /* @__PURE__ */ React.createElement("td", null, p.name), /* @__PURE__ */ React.createElement("td", null, p.squad), /* @__PURE__ */ React.createElement("td", null, p.owner), /* @__PURE__ */ React.createElement("td", null, p.pct, "%"), /* @__PURE__ */ React.createElement("td", null, /* @__PURE__ */ React.createElement("span", { className: "pill " + p.status }, SL[p.status])));
5314
- })))), lens === "releases" && /* @__PURE__ */ React.createElement("div", { className: "rels" }, D.releases.map((r) => /* @__PURE__ */ React.createElement("div", { className: "rel" + (r.now ? " now" : ""), key: r.ver }, /* @__PURE__ */ React.createElement("div", { className: "tick" }, r.done ? "✓" : "→"), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "rel-h" }, /* @__PURE__ */ React.createElement("b", null, r.ver), /* @__PURE__ */ React.createElement("span", { className: "pill " + (r.now ? "active" : "done") }, r.now ? t.thisWeek : t.shipped), /* @__PURE__ */ React.createElement("span", { className: "muted" }, VI ? VI.relWhen[r.ver] || r.when : r.when)), /* @__PURE__ */ React.createElement("div", { className: "rel-sec" }, /* @__PURE__ */ React.createElement("h4", null, VI ? VI.relTitles[r.ver] || r.title : r.title), /* @__PURE__ */ React.createElement("ul", null, (VI ? VI.relItems[r.ver] || r.items : r.items).map((it, i) => /* @__PURE__ */ React.createElement("li", { key: i }, it)))))))), /* @__PURE__ */ React.createElement("p", { className: "ft" }, lang === "vi" ? /* @__PURE__ */ React.createElement(React.Fragment, null, "CyberSkill Status Hub — bản tái hiện UI kit của design system. Dựng trên token ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, "styles.css"), ". Turn Your Will Into Real.") : /* @__PURE__ */ React.createElement(React.Fragment, null, "CyberSkill Status Hub — a design-system UI kit recreation. Built on ", /* @__PURE__ */ React.createElement("span", { className: "mono" }, "styles.css"), " tokens. Turn Your Will Into Real."))), /* @__PURE__ */ React.createElement(Drawer, { p: sel && loc(sel), onClose: () => setSel(null), t }));
5315
- }
5316
- window.StatusHub = StatusHub;
5317
- })(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/status-hub/StatusHub.jsx", error: String((e && e.message) || e) }); }
5318
-
5319
- // ui_kits/status-hub/data.js
5320
- try { (() => {
5321
- window.SH_DATA = {
5322
- updated: "18 Jul 2026, 13:20 ICT",
5323
- velocity: [
5324
- { wk: "W23", n: 4 },
5325
- { wk: "W24", n: 7 },
5326
- { wk: "W25", n: 5 },
5327
- { wk: "W26", n: 9 },
5328
- { wk: "W27", n: 6 },
5329
- { wk: "W28", n: 11 },
5330
- { wk: "W29", n: 8 }
5331
- ],
5332
- projects: [
5333
- {
5334
- key: "CDS",
5335
- name: "Design System v1.6",
5336
- status: "active",
5337
- pct: 72,
5338
- blurb: "Liquid Glass component bindings + 15 element packs.",
5339
- seg: { done: 62, active: 20, hold: 4, todo: 14 },
5340
- owner: "Design Lead",
5341
- updated: "2h ago",
5342
- squad: "Platform",
5343
- tasks: [["TASK-DS-041", "Glass surface bindings", "done"], ["TASK-DS-047", "Element-pack verifier v2", "active"], ["TASK-DS-052", "Dark-mode APCA re-check", "active"], ["TASK-DS-055", "Figma variable sync", "todo"]],
5344
- risk: ""
5345
- },
5346
- {
5347
- key: "GENIE",
5348
- name: "Lumi Genie Chat",
5349
- status: "active",
5350
- pct: 58,
5351
- blurb: "Keyless Claude proxy; scripted wish-capture flow.",
5352
- seg: { done: 48, active: 24, hold: 8, todo: 20 },
5353
- owner: "AI Lead",
5354
- updated: "5h ago",
5355
- squad: "AI",
5356
- tasks: [["TASK-CHAR-026", "In-chat wish flow", "done"], ["TASK-CHAR-030", "One-time mascot hint", "active"], ["TASK-CHAR-021", "Golden-genie GLB", "hold"]],
5357
- risk: "GLB art asset is a hard external dependency — commission not yet confirmed (TASK-CHAR-021)."
5358
- },
5359
- {
5360
- key: "SITE",
5361
- name: "cyberskill.world",
5362
- status: "active",
5363
- pct: 84,
5364
- blurb: "Bilingual storytelling landing; lead capture live.",
5365
- seg: { done: 78, active: 12, hold: 0, todo: 10 },
5366
- owner: "Web Lead",
5367
- updated: "1h ago",
5368
- squad: "Growth",
5369
- tasks: [["TASK-BIZ-003", "Lead pipeline e2e", "done"], ["TASK-SEO-012", "llms.txt route", "done"], ["TASK-CTA-020", "Teardown funnel polish", "active"]],
5370
- risk: ""
5371
- },
5372
- {
5373
- key: "OS",
5374
- name: "CyberOS Agent Spine",
5375
- status: "hold",
5376
- pct: 41,
5377
- blurb: "BRAIN memory store + HITL acceptance gates.",
5378
- seg: { done: 36, active: 8, hold: 30, todo: 26 },
5379
- owner: "Eng Lead",
5380
- updated: "1d ago",
5381
- squad: "Platform",
5382
- tasks: [["TASK-OS-018", "Gate runner", "done"], ["TASK-OS-022", "Memory compaction", "hold"], ["TASK-OS-025", "Provenance ledger", "hold"]],
5383
- risk: "Blocked on infra: provenance ledger needs the signing service (target Q1 2027)."
5384
- },
5385
- {
5386
- key: "AUDIT",
5387
- name: "DS Audit Framework",
5388
- status: "done",
5389
- pct: 100,
5390
- blurb: "125-criteria vendor-neutral audit; L3 tier held.",
5391
- seg: { done: 100, active: 0, hold: 0, todo: 0 },
5392
- owner: "DesignOps",
5393
- updated: "3d ago",
5394
- squad: "Platform",
5395
- tasks: [["TASK-AUD-009", "Deterministic engine", "done"], ["TASK-AUD-011", "No-regression CI gate", "done"]],
5396
- risk: ""
5397
- },
5398
- {
5399
- key: "TOKENS",
5400
- name: "Token Pipeline",
5401
- status: "active",
5402
- pct: 66,
5403
- blurb: "DTCG → CSS/TS/Swift/Kotlin/Flutter/Figma.",
5404
- seg: { done: 58, active: 22, hold: 0, todo: 20 },
5405
- owner: "Eng Lead",
5406
- updated: "6h ago",
5407
- squad: "Platform",
5408
- tasks: [["TASK-TOK-014", "Contrast grid export", "done"], ["TASK-TOK-019", "Flutter target", "active"], ["TASK-TOK-021", "Provenance JSON", "todo"]],
5409
- risk: ""
5410
- }
5411
- ],
5412
- releases: [
5413
- {
5414
- ver: "v1.6.0",
5415
- when: "This week",
5416
- now: true,
5417
- done: false,
5418
- title: "Glass bindings + element-pack v2",
5419
- items: ["Opt-in .cs-surface-* component bindings", "Element-pack verifier v2 (0 warnings across 15)", "Dark-mode APCA re-check"]
5420
- },
5421
- {
5422
- ver: "v1.5.1",
5423
- when: "04 Jul 2026",
5424
- now: false,
5425
- done: true,
5426
- title: "15 element packs ship",
5427
- items: ["All 15 packs promoted to shipped", "Per-pack screenshot regression", "Gallery build script"]
5428
- },
5429
- {
5430
- ver: "v1.4.0",
5431
- when: "13 Jun 2026",
5432
- now: false,
5433
- done: true,
5434
- title: "Liquid Glass in code",
5435
- items: ["--cs-glass-* + --cs-depth-* scalars", "glass.css material layer", "Reduced-transparency fallbacks"]
5436
- },
5437
- {
5438
- ver: "v1.3.0",
5439
- when: "14 May 2026",
5440
- now: false,
5441
- done: true,
5442
- title: "Brand assets + Logo",
5443
- items: ["Official master mark ships", "React <Logo> from master artwork", "Light becomes default theme"]
5444
- }
5445
- ]
5446
- };
5447
- window.SH_VI = {
5448
- updated: "18/07/2026, 13:20 ICT",
5449
- blurbs: { CDS: "Liên kết component Liquid Glass + 15 gói Ngũ Hành.", GENIE: "Proxy Claude không cần khóa; luồng tiếp nhận điều ước theo kịch bản.", SITE: "Trang giới thiệu song ngữ kể chuyện; thu lead đang chạy.", OS: "Kho nhớ BRAIN + cổng nghiệm thu có con người.", AUDIT: "Bộ kiểm định trung lập 125 tiêu chí; giữ hạng L3.", TOKENS: "DTCG → CSS/TS/Swift/Kotlin/Flutter/Figma." },
5450
- risks: { GENIE: "Tài sản GLB là phụ thuộc ngoài khó — chưa chốt đặt hàng (TASK-CHAR-021).", OS: "Bị chặn bởi hạ tầng: sổ cái nguồn gốc cần dịch vụ ký số (mục tiêu Q1 2027)." },
5451
- owners: { "Design Lead": "Trưởng thiết kế", "AI Lead": "Trưởng AI", "Web Lead": "Trưởng web", "Eng Lead": "Trưởng kỹ thuật", "DesignOps": "DesignOps" },
5452
- squads: { Platform: "Nền tảng", AI: "AI", Growth: "Tăng trưởng" },
5453
- updatedRel: { "2h ago": "2 giờ trước", "5h ago": "5 giờ trước", "1h ago": "1 giờ trước", "1d ago": "1 ngày trước", "3d ago": "3 ngày trước", "6h ago": "6 giờ trước" },
5454
- relWhen: { "v1.6.0": "Tuần này", "v1.5.1": "04/07/2026", "v1.4.0": "13/06/2026", "v1.3.0": "14/05/2026" },
5455
- relTitles: { "v1.6.0": "Liên kết Glass + gói Ngũ Hành v2", "v1.5.1": "15 gói Ngũ Hành ra mắt", "v1.4.0": "Liquid Glass vào mã", "v1.3.0": "Tài sản thương hiệu + Logo" },
5456
- relItems: { "v1.6.0": ["Liên kết component .cs-surface-* (tùy chọn)", "Trình kiểm gói Ngũ Hành v2 (0 cảnh báo trên 15 gói)", "Kiểm tra lại APCA cho chế độ tối"], "v1.5.1": ["Cả 15 gói lên trạng thái đã ra mắt", "Regression ảnh chụp từng gói", "Script dựng gallery"], "v1.4.0": ["Scalar --cs-glass-* + --cs-depth-*", "Lớp chất liệu glass.css", "Fallback khi giảm độ trong suốt"], "v1.3.0": ["Master mark chính thức ra mắt", "<Logo> React từ artwork gốc", "Light thành theme mặc định"] }
5457
- };
5458
- })(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/status-hub/data.js", error: String((e && e.message) || e) }); }
5459
-
5460
- // ui_kits/website/Website.jsx
5461
- try { (() => {
5462
- const { useState, useRef, useEffect } = React;
5463
- function I({ name, size = 20, stroke = 1.9 }) {
5464
- const p = {
5465
- sparkle: "M12 3l1.8 5.4L19 10l-5.2 1.6L12 17l-1.8-5.4L5 10l5.2-1.6z",
5466
- chat: "M4 5h16v11H8l-4 4z",
5467
- check: "M4 12.5l5 5 11-11",
5468
- "arrow-right": "M5 12h14M13 6l6 6-6 6",
5469
- close: "M6 6l12 12M18 6L6 18",
5470
- sun: "M12 8a4 4 0 1 0 0 8 4 4 0 0 0 0-8zM12 2v2M12 20v2M2 12h2M20 12h2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M19.1 4.9l-1.4 1.4M6.3 17.7l-1.4 1.4",
5471
- moon: "M20 13.5A8 8 0 1 1 10.5 4a6.5 6.5 0 0 0 9.5 9.5z"
5472
- }[name];
5473
- return /* @__PURE__ */ React.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true" }, /* @__PURE__ */ React.createElement("path", { d: p }));
5474
- }
5475
- function Chat({ t, seed, onClose }) {
5476
- const [msgs, setMsgs] = useState([{ who: "bot", text: t.genie.greeting }]);
5477
- const [val, setVal] = useState("");
5478
- const bodyRef = useRef(null);
5479
- useEffect(() => {
5480
- if (seed) send(seed);
5481
- }, []);
5482
- useEffect(() => {
5483
- const b = bodyRef.current;
5484
- if (b) b.scrollTop = b.scrollHeight;
5485
- }, [msgs]);
5486
- function send(text) {
5487
- const m = (text || "").trim();
5488
- if (!m) return;
5489
- setMsgs((cur) => [...cur, { who: "me", text: m }]);
5490
- setVal("");
5491
- setTimeout(() => setMsgs((cur) => [...cur, { who: "bot", text: "A wish worth granting. I’ll hand it to the team — first, what should I call you?" }]), 500);
5492
- }
5493
- return /* @__PURE__ */ React.createElement("div", { className: "chat" }, /* @__PURE__ */ React.createElement("div", { className: "chat-h" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/lumi-poster.webp", alt: "" }), /* @__PURE__ */ React.createElement("b", null, t.genie.title), /* @__PURE__ */ React.createElement("button", { onClick: onClose, "aria-label": "Close chat" }, /* @__PURE__ */ React.createElement(I, { name: "close", size: 18 }))), /* @__PURE__ */ React.createElement("div", { className: "chat-b", ref: bodyRef }, msgs.map((m, i) => /* @__PURE__ */ React.createElement("div", { key: i, className: "msg " + m.who }, m.text)), msgs.length === 1 && /* @__PURE__ */ React.createElement("div", { className: "chips" }, t.genie.chips.map((c) => /* @__PURE__ */ React.createElement("button", { key: c, onClick: () => send(c) }, c)))), /* @__PURE__ */ React.createElement("div", { className: "chat-f" }, /* @__PURE__ */ React.createElement("input", { value: val, onChange: (e) => setVal(e.target.value), onKeyDown: (e) => e.key === "Enter" && send(val), placeholder: t.genie.placeholder }), /* @__PURE__ */ React.createElement("button", { onClick: () => send(val) }, t.genie.send)));
5494
- }
5495
- function Website() {
5496
- const [loc, setLoc] = useState("en");
5497
- const [dark, setDark] = useState(false);
5498
- const [chat, setChat] = useState(false);
5499
- const [seed, setSeed] = useState("");
5500
- const [wish, setWish] = useState("");
5501
- const [sent, setSent] = useState(false);
5502
- const t = window.SITE_COPY[loc];
5503
- function openChat(s) {
5504
- setSeed(s || "");
5505
- setChat(true);
5506
- }
5507
- return /* @__PURE__ */ React.createElement("div", { className: "site", "data-theme": dark ? "dark" : void 0 }, /* @__PURE__ */ React.createElement("header", { className: "hdr cs-surface-light" }, /* @__PURE__ */ React.createElement("div", { className: "container hdr-in" }, /* @__PURE__ */ React.createElement("a", { className: "brand", href: "#top" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/logo-mark.svg", alt: "" }), "CyberSkill"), /* @__PURE__ */ React.createElement("nav", { className: "nav" }, t.nav.map((n, i) => /* @__PURE__ */ React.createElement("a", { key: i, href: "#" + ["services", "work", "process", "careers", "contact"][i] }, n))), /* @__PURE__ */ React.createElement("div", { className: "hdr-side" }, /* @__PURE__ */ React.createElement("button", { className: "mini", onClick: () => setLoc(loc === "en" ? "vi" : "en") }, t.langLabel), /* @__PURE__ */ React.createElement("button", { className: "mini icon", onClick: () => setDark(!dark), "aria-label": "Toggle theme" }, /* @__PURE__ */ React.createElement(I, { name: dark ? "sun" : "moon", size: 18 })), /* @__PURE__ */ React.createElement("button", { className: "mini", style: { background: "var(--cs-color-brand-umber)", color: "#fff", borderColor: "transparent" }, onClick: () => openChat("") }, t.talk)))), /* @__PURE__ */ React.createElement("section", { className: "hero", id: "top" }, /* @__PURE__ */ React.createElement("div", { className: "container hero-in" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.eyebrow), /* @__PURE__ */ React.createElement("h1", null, t.slogan), /* @__PURE__ */ React.createElement("div", { className: "vn" }, t.sub), /* @__PURE__ */ React.createElement("p", { className: "lead" }, t.lead), /* @__PURE__ */ React.createElement("div", { className: "wish" }, /* @__PURE__ */ React.createElement("input", { value: wish, onChange: (e) => setWish(e.target.value), onKeyDown: (e) => e.key === "Enter" && openChat(wish), placeholder: t.wishPlaceholder, "aria-label": t.wishPlaceholder }), /* @__PURE__ */ React.createElement("button", { className: "btn-gold", onClick: () => openChat(wish) }, /* @__PURE__ */ React.createElement(I, { name: "sparkle", size: 18 }), t.wishCta), /* @__PURE__ */ React.createElement("button", { className: "btn-ghost-light", onClick: () => openChat("") }, t.talk))), /* @__PURE__ */ React.createElement("div", { className: "hero-art" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/lumi-poster.webp", alt: "Lumi, the golden genie" })))), /* @__PURE__ */ React.createElement("div", { className: "marq" }, /* @__PURE__ */ React.createElement("div", { className: "container marq-in" }, t.marquee.map((m, i) => /* @__PURE__ */ React.createElement(React.Fragment, { key: i }, i > 0 && /* @__PURE__ */ React.createElement("b", null, "·"), /* @__PURE__ */ React.createElement("span", null, m))))), /* @__PURE__ */ React.createElement("section", { className: "sec", id: "services" }, /* @__PURE__ */ React.createElement("div", { className: "container" }, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.nav[0]), /* @__PURE__ */ React.createElement("h2", null, t.servicesTitle), /* @__PURE__ */ React.createElement("p", { className: "sec-lead" }, t.servicesLead), /* @__PURE__ */ React.createElement("div", { className: "svc-grid" }, t.services.map((s, i) => /* @__PURE__ */ React.createElement("article", { className: "svc cs-surface-standard", key: i }, /* @__PURE__ */ React.createElement("div", { className: "svc-ic" }, /* @__PURE__ */ React.createElement(I, { name: s.icon })), /* @__PURE__ */ React.createElement("h3", null, s.t), /* @__PURE__ */ React.createElement("p", null, s.s), /* @__PURE__ */ React.createElement("ul", null, s.o.map((o) => /* @__PURE__ */ React.createElement("li", { key: o }, o)))))))), /* @__PURE__ */ React.createElement("section", { className: "sec sec-alt", id: "process" }, /* @__PURE__ */ React.createElement("div", { className: "container" }, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.nav[2]), /* @__PURE__ */ React.createElement("h2", null, t.processTitle), /* @__PURE__ */ React.createElement("p", { className: "sec-lead" }, t.processLead), /* @__PURE__ */ React.createElement("div", { className: "steps" }, t.steps.map((s) => /* @__PURE__ */ React.createElement("div", { className: "step", key: s.n }, /* @__PURE__ */ React.createElement("span", { className: "n" }, s.n), /* @__PURE__ */ React.createElement("h3", null, s.t), /* @__PURE__ */ React.createElement("p", null, s.s)))))), /* @__PURE__ */ React.createElement("section", { className: "sec", id: "careers" }, /* @__PURE__ */ React.createElement("div", { className: "container" }, /* @__PURE__ */ React.createElement("div", { className: "band" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("h2", null, t.careersTitle), /* @__PURE__ */ React.createElement("p", null, t.careersLead)), /* @__PURE__ */ React.createElement("button", { className: "btn-gold", onClick: () => openChat("") }, t.careersCta, /* @__PURE__ */ React.createElement(I, { name: "arrow-right", size: 18 }))))), /* @__PURE__ */ React.createElement("section", { className: "sec sec-alt", id: "contact" }, /* @__PURE__ */ React.createElement("div", { className: "container contact-grid" }, /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("div", { className: "eyebrow" }, t.nav[4]), /* @__PURE__ */ React.createElement("h2", null, t.contactTitle), /* @__PURE__ */ React.createElement("p", { className: "sec-lead", style: { marginBottom: 16 } }, t.contactLead), /* @__PURE__ */ React.createElement("p", { className: "trust" }, /* @__PURE__ */ React.createElement(I, { name: "check", size: 16 }), t.form.trust)), sent ? /* @__PURE__ */ React.createElement("div", { className: "sent" }, /* @__PURE__ */ React.createElement(I, { name: "check", size: 18 }), " ", loc === "vi" ? "Đã nhận được lời nhắn. Cảm ơn bạn!" : "Message received. Thank you!") : /* @__PURE__ */ React.createElement("form", { className: "form", onSubmit: (e) => {
5508
- e.preventDefault();
5509
- setSent(true);
5510
- } }, /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.name), /* @__PURE__ */ React.createElement("input", { className: "cs-field__control", required: true })), /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.email), /* @__PURE__ */ React.createElement("input", { className: "cs-field__control", type: "email", required: true })), /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.company, " ", /* @__PURE__ */ React.createElement("span", { className: "muted", style: { fontWeight: 400, color: "var(--cs-color-text-muted)" } }, "· ", t.form.optional)), /* @__PURE__ */ React.createElement("input", { className: "cs-field__control" })), /* @__PURE__ */ React.createElement("label", { className: "cs-field" }, /* @__PURE__ */ React.createElement("span", { className: "cs-field__label" }, t.form.message), /* @__PURE__ */ React.createElement("textarea", { className: "cs-field__control", rows: "3" })), /* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("button", { type: "submit", className: "btn-gold" }, t.form.submit, /* @__PURE__ */ React.createElement(I, { name: "arrow-right", size: 18 })))))), /* @__PURE__ */ React.createElement("footer", { className: "ft" }, /* @__PURE__ */ React.createElement("div", { className: "container ft-in" }, /* @__PURE__ */ React.createElement("a", { className: "brand", href: "#top" }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/logo-mark.svg", alt: "" }), "CyberSkill"), /* @__PURE__ */ React.createElement("div", { className: "ft-links" }, t.footerLinks.map((l) => /* @__PURE__ */ React.createElement("a", { key: l, href: "#top" }, l))), /* @__PURE__ */ React.createElement("small", null, "© 2026 CyberSkill · ", t.footerRights, " · Saigon"))), !chat && /* @__PURE__ */ React.createElement("button", { className: "fab", onClick: () => openChat("") }, /* @__PURE__ */ React.createElement("img", { src: "../../assets/lumi-poster.webp", alt: "" }), t.talk), chat && /* @__PURE__ */ React.createElement(Chat, { t, seed, onClose: () => setChat(false) }));
5511
- }
5512
- window.Website = Website;
5513
- })(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/website/Website.jsx", error: String((e && e.message) || e) }); }
5514
-
5515
- // ui_kits/website/copy.js
5516
- try { (() => {
5517
- window.SITE_COPY = {
5518
- en: {
5519
- nav: ["Services", "Work", "Process", "Careers", "Contact"],
5520
- langLabel: "Tiếng Việt",
5521
- eyebrow: "A Saigon studio that turns will into working software. Since 2020.",
5522
- slogan: "Turn Your Will Into Real",
5523
- sub: "Hiện Thực Hoá Ý Chí",
5524
- lead: "Lumi, our golden genie, takes a clear wish and helps the human team turn it into software your people actually run — built to ship, and built to last.",
5525
- wishPlaceholder: "Make a wish… a faster checkout, an app, a tool",
5526
- wishCta: "Make a wish",
5527
- talk: "Talk to Lumi",
5528
- marquee: ["Web apps", "Mobile apps", "Internal systems", "Automation", "Design systems", "Built to last"],
5529
- servicesTitle: "What we build",
5530
- servicesLead: "Three focused practices. One team that answers for the result.",
5531
- services: [
5532
- { icon: "sparkle", t: "Web & mobile apps", s: "Product apps your customers reach for — fast, accessible, bilingual by default.", o: ["Web apps", "Mobile apps", "Design systems"] },
5533
- { icon: "chat", t: "Internal systems & automation", s: "The tools your team runs on: dashboards, workflows, and the automation behind them.", o: ["Internal systems", "Automation"] },
5534
- { icon: "check", t: "Built to last", s: "Shipped software that keeps running — tested, documented, handed over cleanly.", o: ["Testing", "Docs", "Handover"] }
5535
- ],
5536
- processTitle: "The arc of a wish",
5537
- processLead: "Every project follows one arc: from a clear wish to software running in the real world.",
5538
- steps: [
5539
- { n: "01", t: "Make a wish", s: "Tell us the outcome in a sentence or two. We reply within one business day." },
5540
- { n: "02", t: "Shape it", s: "A small senior team scopes the work and is honest about trade-offs." },
5541
- { n: "03", t: "Build to ship", s: "Short cycles, real software you can run — not slideware." },
5542
- { n: "04", t: "Built to last", s: "Tested, documented, handed over. It keeps running after we leave." }
5543
- ],
5544
- careersTitle: "Build with us",
5545
- careersLead: "We hire for craft and care — senior, small, and honest about trade-offs.",
5546
- careersCta: "See open roles",
5547
- contactTitle: "Tell us your wish",
5548
- contactLead: "Send a short note. We reply within one business day.",
5549
- form: { name: "Your name", email: "Work email", company: "Company", message: "Anything we should know?", submit: "Send", optional: "optional", trust: "Your details go straight to the team. We never share them." },
5550
- genie: { title: "Lumi · CyberSkill’s golden genie", greeting: "Hello, I’m Lumi. Think of me as a friendly guide: tell me a wish, and I’ll share plain advice before asking for anything. When a wish is clear, it goes to a real person on the team.", placeholder: "Type your wish…", send: "Send", chips: ["A web app", "An internal tool", "A design system"] },
5551
- footerRights: "All rights reserved.",
5552
- footerLinks: ["Privacy", "Accessibility", "Terms"]
5553
- },
5554
- vi: {
5555
- nav: ["Dịch vụ", "Dự án", "Quy trình", "Tuyển dụng", "Liên hệ"],
5556
- langLabel: "English",
5557
- eyebrow: "Studio Sài Gòn. Biến ý chí thành phần mềm chạy thật. Từ năm 2020.",
5558
- slogan: "Hiện Thực Hoá Ý Chí",
5559
- sub: "Turn Your Will Into Real",
5560
- lead: "Lumi, thần đèn vàng của CyberSkill, cùng đội ngũ biến điều ước rõ thành phần mềm đội bạn dùng mỗi ngày: bàn giao được, dùng lâu được.",
5561
- wishPlaceholder: "Ước một điều… thanh toán nhanh hơn, một ứng dụng, một công cụ",
5562
- wishCta: "Ước điều này",
5563
- talk: "Trò chuyện với Lumi",
5564
- marquee: ["Ứng dụng web", "Ứng dụng di động", "Hệ thống nội bộ", "Tự động hóa", "Hệ thống thiết kế", "Bền bỉ lâu dài"],
5565
- servicesTitle: "Chúng tôi xây dựng những gì",
5566
- servicesLead: "Ba mảng chuyên sâu. Một đội chịu trách nhiệm đến kết quả cuối cùng.",
5567
- services: [
5568
- { icon: "sparkle", t: "Ứng dụng web & di động", s: "Sản phẩm khách hàng dùng mỗi ngày — nhanh, dễ tiếp cận, song ngữ mặc định.", o: ["Ứng dụng web", "Di động", "Hệ thống thiết kế"] },
5569
- { icon: "chat", t: "Hệ thống nội bộ & tự động hóa", s: "Công cụ đội bạn vận hành: bảng điều khiển, quy trình và tự động hóa phía sau.", o: ["Hệ thống nội bộ", "Tự động hóa"] },
5570
- { icon: "check", t: "Bền bỉ lâu dài", s: "Phần mềm bàn giao vẫn chạy tốt — được kiểm thử, tài liệu đầy đủ, bàn giao gọn.", o: ["Kiểm thử", "Tài liệu", "Bàn giao"] }
5571
- ],
5572
- processTitle: "Hành trình của một điều ước",
5573
- processLead: "Mỗi dự án một hành trình: từ điều ước rõ đến phần mềm chạy giữa đời thật.",
5574
- steps: [
5575
- { n: "01", t: "Ước một điều", s: "Kể kết quả bạn muốn trong một hai câu. Chúng tôi phản hồi trong một ngày làm việc." },
5576
- { n: "02", t: "Định hình", s: "Đội nhỏ, kinh nghiệm bóc tách công việc và nói thẳng về đánh đổi." },
5577
- { n: "03", t: "Xây để bàn giao", s: "Chu kỳ ngắn, phần mềm chạy thật — không phải slide." },
5578
- { n: "04", t: "Dùng lâu được", s: "Kiểm thử, tài liệu, bàn giao. Vẫn chạy tốt sau khi chúng tôi rời đi." }
5579
- ],
5580
- careersTitle: "Cùng chúng tôi kiến tạo",
5581
- careersLead: "Tuyển người có tay nghề và chỉn chu — đội gọn, kinh nghiệm, nói thẳng đánh đổi.",
5582
- careersCta: "Xem vị trí đang mở",
5583
- contactTitle: "Kể chúng tôi nghe điều bạn ước",
5584
- contactLead: "Một lời nhắn ngắn là đủ. Chúng tôi phản hồi trong một ngày làm việc.",
5585
- form: { name: "Tên của bạn", email: "Email công việc", company: "Công ty", message: "Bạn muốn chia sẻ thêm điều gì?", submit: "Gửi", optional: "không bắt buộc", trust: "Thông tin của bạn được gửi thẳng đến đội ngũ. Chúng tôi không chia sẻ cho bên thứ ba." },
5586
- genie: { title: "Lumi · thần đèn vàng CyberSkill", greeting: "Chào bạn, mình là Lumi. Cứ kể mình một điều ước; mình tư vấn trước, chỉ xin thông tin khi bạn muốn gửi. Điều ước rõ sẽ được chuyển cho người thật trong đội.", placeholder: "Viết điều ước của bạn…", send: "Gửi", chips: ["Một ứng dụng web", "Công cụ nội bộ", "Hệ thống thiết kế"] },
5587
- footerRights: "Bảo lưu mọi quyền.",
5588
- footerLinks: ["Quyền riêng tư", "Khả năng tiếp cận", "Điều khoản"]
5589
- }
5590
- };
5591
- })(); } catch (e) { __ds_ns.__errors.push({ path: "ui_kits/website/copy.js", error: String((e && e.message) || e) }); }
5592
-
5593
5353
  __ds_ns.AIDisclosureBadge = __ds_scope.AIDisclosureBadge;
5594
5354
 
5595
5355
  __ds_ns.ChatMessage = __ds_scope.ChatMessage;
@@ -5824,6 +5584,8 @@ __ds_ns.Drawer = __ds_scope.Drawer;
5824
5584
 
5825
5585
  __ds_ns.HoverCard = __ds_scope.HoverCard;
5826
5586
 
5587
+ __ds_ns.OverlayProvider = __ds_scope.OverlayProvider;
5588
+
5827
5589
  __ds_ns.Popconfirm = __ds_scope.Popconfirm;
5828
5590
 
5829
5591
  __ds_ns.Popover = __ds_scope.Popover;