@azurioh/discord-kernel 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (529) hide show
  1. package/README.md +318 -1
  2. package/dist/authz/require-level-guard.d.ts +1 -1
  3. package/dist/authz/require-level-guard.d.ts.map +1 -1
  4. package/dist/color.d.ts +42 -0
  5. package/dist/color.d.ts.map +1 -0
  6. package/dist/color.js +130 -0
  7. package/dist/color.js.map +1 -0
  8. package/dist/discord/command/all-required.d.ts +8 -0
  9. package/dist/discord/command/all-required.d.ts.map +1 -0
  10. package/dist/discord/command/all-required.js +12 -0
  11. package/dist/discord/command/all-required.js.map +1 -0
  12. package/dist/discord/command/autocomplete-dispatcher.d.ts.map +1 -1
  13. package/dist/discord/command/autocomplete-dispatcher.js +2 -1
  14. package/dist/discord/command/autocomplete-dispatcher.js.map +1 -1
  15. package/dist/discord/command/autocomplete-limits.d.ts +3 -0
  16. package/dist/discord/command/autocomplete-limits.d.ts.map +1 -0
  17. package/dist/discord/command/autocomplete-limits.js +6 -0
  18. package/dist/discord/command/autocomplete-limits.js.map +1 -0
  19. package/dist/discord/command/autocomplete.d.ts +6 -1
  20. package/dist/discord/command/autocomplete.d.ts.map +1 -1
  21. package/dist/discord/command/autocomplete.js +6 -6
  22. package/dist/discord/command/autocomplete.js.map +1 -1
  23. package/dist/discord/command/command-responders.d.ts +26 -0
  24. package/dist/discord/command/command-responders.d.ts.map +1 -0
  25. package/dist/discord/command/command-responders.js +16 -0
  26. package/dist/discord/command/command-responders.js.map +1 -0
  27. package/dist/discord/command/context.d.ts +8 -17
  28. package/dist/discord/command/context.d.ts.map +1 -1
  29. package/dist/discord/command/context.js +8 -37
  30. package/dist/discord/command/context.js.map +1 -1
  31. package/dist/discord/command/cooldown-guard.d.ts +1 -1
  32. package/dist/discord/command/cooldown-guard.d.ts.map +1 -1
  33. package/dist/discord/command/create-command.d.ts +1 -8
  34. package/dist/discord/command/create-command.d.ts.map +1 -1
  35. package/dist/discord/command/create-command.js +17 -65
  36. package/dist/discord/command/create-command.js.map +1 -1
  37. package/dist/discord/command/create-context-menu-command.d.ts +3 -9
  38. package/dist/discord/command/create-context-menu-command.d.ts.map +1 -1
  39. package/dist/discord/command/create-context-menu-command.js +14 -65
  40. package/dist/discord/command/create-context-menu-command.js.map +1 -1
  41. package/dist/discord/command/errors.d.ts +1 -1
  42. package/dist/discord/command/errors.d.ts.map +1 -1
  43. package/dist/discord/command/errors.js +2 -2
  44. package/dist/discord/command/errors.js.map +1 -1
  45. package/dist/discord/command/guard.d.ts +15 -8
  46. package/dist/discord/command/guard.d.ts.map +1 -1
  47. package/dist/discord/command/guard.js +4 -4
  48. package/dist/discord/command/guard.js.map +1 -1
  49. package/dist/discord/command/options.d.ts +1 -10
  50. package/dist/discord/command/options.d.ts.map +1 -1
  51. package/dist/discord/command/options.js +127 -320
  52. package/dist/discord/command/options.js.map +1 -1
  53. package/dist/discord/command/passes-guard.d.ts +14 -0
  54. package/dist/discord/command/passes-guard.d.ts.map +1 -0
  55. package/dist/discord/command/passes-guard.js +27 -0
  56. package/dist/discord/command/passes-guard.js.map +1 -0
  57. package/dist/discord/command/permission-guard.d.ts +1 -1
  58. package/dist/discord/command/permission-guard.d.ts.map +1 -1
  59. package/dist/discord/command/render-command-failure.d.ts +23 -0
  60. package/dist/discord/command/render-command-failure.d.ts.map +1 -0
  61. package/dist/discord/command/render-command-failure.js +40 -0
  62. package/dist/discord/command/render-command-failure.js.map +1 -0
  63. package/dist/discord/command/route-key.d.ts +8 -0
  64. package/dist/discord/command/route-key.d.ts.map +1 -0
  65. package/dist/discord/command/route-key.js +13 -0
  66. package/dist/discord/command/route-key.js.map +1 -0
  67. package/dist/discord/command/router.d.ts +32 -7
  68. package/dist/discord/command/router.d.ts.map +1 -1
  69. package/dist/discord/command/router.js +48 -10
  70. package/dist/discord/command/router.js.map +1 -1
  71. package/dist/discord/command/send-embed.d.ts +14 -0
  72. package/dist/discord/command/send-embed.d.ts.map +1 -0
  73. package/dist/discord/command/send-embed.js +37 -0
  74. package/dist/discord/command/send-embed.js.map +1 -0
  75. package/dist/discord/command/types.d.ts +7 -1
  76. package/dist/discord/command/types.d.ts.map +1 -1
  77. package/dist/discord/components/component-access.d.ts +12 -0
  78. package/dist/discord/components/component-access.d.ts.map +1 -0
  79. package/dist/discord/components/component-access.js +21 -0
  80. package/dist/discord/components/component-access.js.map +1 -0
  81. package/dist/discord/components/component-router.d.ts +37 -15
  82. package/dist/discord/components/component-router.d.ts.map +1 -1
  83. package/dist/discord/components/component-router.js +44 -32
  84. package/dist/discord/components/component-router.js.map +1 -1
  85. package/dist/discord/components/interactive-message/index.d.ts +3 -1
  86. package/dist/discord/components/interactive-message/index.d.ts.map +1 -1
  87. package/dist/discord/components/interactive-message/index.js +5 -3
  88. package/dist/discord/components/interactive-message/index.js.map +1 -1
  89. package/dist/discord/components/interactive-message/interactive-message-collector.d.ts +3 -28
  90. package/dist/discord/components/interactive-message/interactive-message-collector.d.ts.map +1 -1
  91. package/dist/discord/components/interactive-message/interactive-message-collector.js +6 -53
  92. package/dist/discord/components/interactive-message/interactive-message-collector.js.map +1 -1
  93. package/dist/discord/components/interactive-message/message-edit-options.d.ts +18 -0
  94. package/dist/discord/components/interactive-message/message-edit-options.d.ts.map +1 -0
  95. package/dist/discord/components/interactive-message/message-edit-options.js +40 -0
  96. package/dist/discord/components/interactive-message/message-edit-options.js.map +1 -0
  97. package/dist/discord/components/interactive-message/mount-interactive-message.d.ts +2 -1
  98. package/dist/discord/components/interactive-message/mount-interactive-message.d.ts.map +1 -1
  99. package/dist/discord/components/interactive-message/mount-interactive-message.js +4 -2
  100. package/dist/discord/components/interactive-message/mount-interactive-message.js.map +1 -1
  101. package/dist/discord/components/interactive-message/state-store.d.ts +12 -0
  102. package/dist/discord/components/interactive-message/state-store.d.ts.map +1 -0
  103. package/dist/discord/components/interactive-message/state-store.js +14 -0
  104. package/dist/discord/components/interactive-message/state-store.js.map +1 -0
  105. package/dist/discord/components/paginator/mount-paginator.js +7 -5
  106. package/dist/discord/components/paginator/mount-paginator.js.map +1 -1
  107. package/dist/discord/components/paginator/paginator-view.d.ts +1 -1
  108. package/dist/discord/components/paginator/paginator-view.d.ts.map +1 -1
  109. package/dist/discord/components/persistent-paginator.d.ts +1 -1
  110. package/dist/discord/components/persistent-paginator.d.ts.map +1 -1
  111. package/dist/discord/components/persistent-paginator.js +6 -6
  112. package/dist/discord/components/persistent-paginator.js.map +1 -1
  113. package/dist/discord/components/settings-editor/from-declaration-controls.d.ts +67 -0
  114. package/dist/discord/components/settings-editor/from-declaration-controls.d.ts.map +1 -0
  115. package/dist/discord/components/settings-editor/from-declaration-controls.js +245 -0
  116. package/dist/discord/components/settings-editor/from-declaration-controls.js.map +1 -0
  117. package/dist/discord/components/settings-editor/from-declaration-display.d.ts +23 -0
  118. package/dist/discord/components/settings-editor/from-declaration-display.d.ts.map +1 -0
  119. package/dist/discord/components/settings-editor/from-declaration-display.js +114 -0
  120. package/dist/discord/components/settings-editor/from-declaration-display.js.map +1 -0
  121. package/dist/discord/components/settings-editor/from-declaration-layout.d.ts +33 -0
  122. package/dist/discord/components/settings-editor/from-declaration-layout.d.ts.map +1 -0
  123. package/dist/discord/components/settings-editor/from-declaration-layout.js +178 -0
  124. package/dist/discord/components/settings-editor/from-declaration-layout.js.map +1 -0
  125. package/dist/discord/components/settings-editor/from-declaration-search.d.ts +27 -0
  126. package/dist/discord/components/settings-editor/from-declaration-search.d.ts.map +1 -0
  127. package/dist/discord/components/settings-editor/from-declaration-search.js +58 -0
  128. package/dist/discord/components/settings-editor/from-declaration-search.js.map +1 -0
  129. package/dist/discord/components/settings-editor/from-declaration-stored.d.ts +21 -0
  130. package/dist/discord/components/settings-editor/from-declaration-stored.d.ts.map +1 -0
  131. package/dist/discord/components/settings-editor/from-declaration-stored.js +37 -0
  132. package/dist/discord/components/settings-editor/from-declaration-stored.js.map +1 -0
  133. package/dist/discord/components/settings-editor/from-declaration-subject.d.ts +32 -0
  134. package/dist/discord/components/settings-editor/from-declaration-subject.d.ts.map +1 -0
  135. package/dist/discord/components/settings-editor/from-declaration-subject.js +65 -0
  136. package/dist/discord/components/settings-editor/from-declaration-subject.js.map +1 -0
  137. package/dist/discord/components/settings-editor/from-declaration-values.d.ts +57 -0
  138. package/dist/discord/components/settings-editor/from-declaration-values.d.ts.map +1 -0
  139. package/dist/discord/components/settings-editor/from-declaration-values.js +187 -0
  140. package/dist/discord/components/settings-editor/from-declaration-values.js.map +1 -0
  141. package/dist/discord/components/settings-editor/from-declaration.d.ts +39 -0
  142. package/dist/discord/components/settings-editor/from-declaration.d.ts.map +1 -0
  143. package/dist/discord/components/settings-editor/from-declaration.js +132 -0
  144. package/dist/discord/components/settings-editor/from-declaration.js.map +1 -0
  145. package/dist/discord/components/settings-editor/index.d.ts +3 -1
  146. package/dist/discord/components/settings-editor/index.d.ts.map +1 -1
  147. package/dist/discord/components/settings-editor/index.js +6 -3
  148. package/dist/discord/components/settings-editor/index.js.map +1 -1
  149. package/dist/discord/components/settings-editor/mount-settings-editor.d.ts +16 -2
  150. package/dist/discord/components/settings-editor/mount-settings-editor.d.ts.map +1 -1
  151. package/dist/discord/components/settings-editor/mount-settings-editor.js +14 -10
  152. package/dist/discord/components/settings-editor/mount-settings-editor.js.map +1 -1
  153. package/dist/discord/components/settings-editor/navigation.d.ts +20 -0
  154. package/dist/discord/components/settings-editor/navigation.d.ts.map +1 -1
  155. package/dist/discord/components/settings-editor/navigation.js +22 -0
  156. package/dist/discord/components/settings-editor/navigation.js.map +1 -1
  157. package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts +4 -3
  158. package/dist/discord/components/settings-editor/settings-editor-card.view.d.ts.map +1 -1
  159. package/dist/discord/components/settings-editor/settings-editor-card.view.js +60 -34
  160. package/dist/discord/components/settings-editor/settings-editor-card.view.js.map +1 -1
  161. package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts +2 -1
  162. package/dist/discord/components/settings-editor/settings-editor-field-modal.d.ts.map +1 -1
  163. package/dist/discord/components/settings-editor/settings-editor-field-modal.js +48 -72
  164. package/dist/discord/components/settings-editor/settings-editor-field-modal.js.map +1 -1
  165. package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts +6 -0
  166. package/dist/discord/components/settings-editor/settings-editor-field-values.d.ts.map +1 -0
  167. package/dist/discord/components/settings-editor/settings-editor-field-values.js +13 -0
  168. package/dist/discord/components/settings-editor/settings-editor-field-values.js.map +1 -0
  169. package/dist/discord/components/settings-editor/settings-editor-fields.d.ts +6 -5
  170. package/dist/discord/components/settings-editor/settings-editor-fields.d.ts.map +1 -1
  171. package/dist/discord/components/settings-editor/settings-editor-fields.js +0 -10
  172. package/dist/discord/components/settings-editor/settings-editor-fields.js.map +1 -1
  173. package/dist/discord/components/settings-editor/settings-editor.view.d.ts +3 -2
  174. package/dist/discord/components/settings-editor/settings-editor.view.d.ts.map +1 -1
  175. package/dist/discord/components/settings-editor/settings-editor.view.js +4 -21
  176. package/dist/discord/components/settings-editor/settings-editor.view.js.map +1 -1
  177. package/dist/discord/events/event-guild-id.d.ts +10 -0
  178. package/dist/discord/events/event-guild-id.d.ts.map +1 -0
  179. package/dist/discord/events/event-guild-id.js +37 -0
  180. package/dist/discord/events/event-guild-id.js.map +1 -0
  181. package/dist/discord/events/event-router.d.ts +17 -3
  182. package/dist/discord/events/event-router.d.ts.map +1 -1
  183. package/dist/discord/events/event-router.js +33 -9
  184. package/dist/discord/events/event-router.js.map +1 -1
  185. package/dist/discord/i18n.d.ts +17 -2
  186. package/dist/discord/i18n.d.ts.map +1 -1
  187. package/dist/discord/i18n.js +43 -6
  188. package/dist/discord/i18n.js.map +1 -1
  189. package/dist/discord/interaction/interaction-locale.d.ts +14 -0
  190. package/dist/discord/interaction/interaction-locale.d.ts.map +1 -0
  191. package/dist/discord/interaction/interaction-locale.js +12 -0
  192. package/dist/discord/interaction/interaction-locale.js.map +1 -0
  193. package/dist/discord/interaction/locale-resolver.d.ts +27 -0
  194. package/dist/discord/interaction/locale-resolver.d.ts.map +1 -0
  195. package/dist/discord/interaction/locale-resolver.js +3 -0
  196. package/dist/discord/interaction/locale-resolver.js.map +1 -0
  197. package/dist/discord/interaction/modal.d.ts.map +1 -1
  198. package/dist/discord/interaction/modal.js +5 -40
  199. package/dist/discord/interaction/modal.js.map +1 -1
  200. package/dist/discord/interaction/reply-locale.d.ts +18 -0
  201. package/dist/discord/interaction/reply-locale.d.ts.map +1 -0
  202. package/dist/discord/interaction/reply-locale.js +25 -0
  203. package/dist/discord/interaction/reply-locale.js.map +1 -0
  204. package/dist/discord/interaction/select-bounds.d.ts +19 -0
  205. package/dist/discord/interaction/select-bounds.d.ts.map +1 -0
  206. package/dist/discord/interaction/select-bounds.js +17 -0
  207. package/dist/discord/interaction/select-bounds.js.map +1 -0
  208. package/dist/discord/interaction/select-menu.d.ts.map +1 -1
  209. package/dist/discord/interaction/select-menu.js +2 -9
  210. package/dist/discord/interaction/select-menu.js.map +1 -1
  211. package/dist/discord/placeholder-registry.d.ts +1 -1
  212. package/dist/discord/placeholder-registry.d.ts.map +1 -1
  213. package/dist/discord/presenter.d.ts +1 -1
  214. package/dist/discord/presenter.d.ts.map +1 -1
  215. package/dist/discord/settings/discord-guild-directory.d.ts +28 -0
  216. package/dist/discord/settings/discord-guild-directory.d.ts.map +1 -0
  217. package/dist/discord/settings/discord-guild-directory.js +171 -0
  218. package/dist/discord/settings/discord-guild-directory.js.map +1 -0
  219. package/dist/discord/settings/guild-locale-resolver.d.ts +25 -0
  220. package/dist/discord/settings/guild-locale-resolver.d.ts.map +1 -0
  221. package/dist/discord/settings/guild-locale-resolver.js +33 -0
  222. package/dist/discord/settings/guild-locale-resolver.js.map +1 -0
  223. package/dist/discord/settings/is-module-disabled.d.ts +16 -0
  224. package/dist/discord/settings/is-module-disabled.d.ts.map +1 -0
  225. package/dist/discord/settings/is-module-disabled.js +20 -0
  226. package/dist/discord/settings/is-module-disabled.js.map +1 -0
  227. package/dist/discord/settings/module-disabled-embed.d.ts +13 -0
  228. package/dist/discord/settings/module-disabled-embed.d.ts.map +1 -0
  229. package/dist/discord/settings/module-disabled-embed.js +16 -0
  230. package/dist/discord/settings/module-disabled-embed.js.map +1 -0
  231. package/dist/discord/settings/require-configured.d.ts +26 -0
  232. package/dist/discord/settings/require-configured.d.ts.map +1 -0
  233. package/dist/discord/settings/require-configured.js +62 -0
  234. package/dist/discord/settings/require-configured.js.map +1 -0
  235. package/dist/discord/ui/color-aliases.d.ts +31 -0
  236. package/dist/discord/ui/color-aliases.d.ts.map +1 -0
  237. package/dist/discord/ui/color-aliases.js +66 -0
  238. package/dist/discord/ui/color-aliases.js.map +1 -0
  239. package/dist/discord/ui/color-input.d.ts +5 -32
  240. package/dist/discord/ui/color-input.d.ts.map +1 -1
  241. package/dist/discord/ui/color-input.js +8 -157
  242. package/dist/discord/ui/color-input.js.map +1 -1
  243. package/dist/discord/ui/colors.d.ts +0 -2
  244. package/dist/discord/ui/colors.d.ts.map +1 -1
  245. package/dist/discord/ui/colors.js +4 -5
  246. package/dist/discord/ui/colors.js.map +1 -1
  247. package/dist/discord/ui/embed.d.ts.map +1 -1
  248. package/dist/discord/ui/embed.js +3 -8
  249. package/dist/discord/ui/embed.js.map +1 -1
  250. package/dist/discord/ui/truncate-text.d.ts +11 -0
  251. package/dist/discord/ui/truncate-text.d.ts.map +1 -0
  252. package/dist/discord/ui/truncate-text.js +19 -0
  253. package/dist/discord/ui/truncate-text.js.map +1 -0
  254. package/dist/errors/error-message.d.ts +9 -0
  255. package/dist/errors/error-message.d.ts.map +1 -0
  256. package/dist/errors/error-message.js +14 -0
  257. package/dist/errors/error-message.js.map +1 -0
  258. package/dist/i18n/business-message.d.ts +1 -1
  259. package/dist/i18n/business-message.d.ts.map +1 -1
  260. package/dist/i18n/catalog.d.ts +9 -0
  261. package/dist/i18n/catalog.d.ts.map +1 -1
  262. package/dist/i18n/catalog.js +4 -0
  263. package/dist/i18n/catalog.js.map +1 -1
  264. package/dist/i18n/locale.d.ts +2 -0
  265. package/dist/i18n/locale.d.ts.map +1 -1
  266. package/dist/i18n/locale.js +3 -1
  267. package/dist/i18n/locale.js.map +1 -1
  268. package/dist/i18n/translator.d.ts +2 -0
  269. package/dist/i18n/translator.d.ts.map +1 -1
  270. package/dist/i18n/translator.js.map +1 -1
  271. package/dist/module/module.d.ts +15 -1
  272. package/dist/module/module.d.ts.map +1 -1
  273. package/dist/resilience/retry.js +2 -2
  274. package/dist/resilience/retry.js.map +1 -1
  275. package/dist/scheduler/scheduler.d.ts.map +1 -1
  276. package/dist/scheduler/scheduler.js +2 -4
  277. package/dist/scheduler/scheduler.js.map +1 -1
  278. package/dist/settings/cache.d.ts +29 -0
  279. package/dist/settings/cache.d.ts.map +1 -0
  280. package/dist/settings/cache.js +47 -0
  281. package/dist/settings/cache.js.map +1 -0
  282. package/dist/settings/choice.d.ts +10 -0
  283. package/dist/settings/choice.d.ts.map +1 -0
  284. package/dist/settings/choice.js +3 -0
  285. package/dist/settings/choice.js.map +1 -0
  286. package/dist/settings/decode-stored.d.ts +20 -0
  287. package/dist/settings/decode-stored.d.ts.map +1 -0
  288. package/dist/settings/decode-stored.js +35 -0
  289. package/dist/settings/decode-stored.js.map +1 -0
  290. package/dist/settings/define-settings.d.ts +52 -0
  291. package/dist/settings/define-settings.d.ts.map +1 -0
  292. package/dist/settings/define-settings.js +141 -0
  293. package/dist/settings/define-settings.js.map +1 -0
  294. package/dist/settings/describe-field.d.ts +19 -0
  295. package/dist/settings/describe-field.d.ts.map +1 -0
  296. package/dist/settings/describe-field.js +131 -0
  297. package/dist/settings/describe-field.js.map +1 -0
  298. package/dist/settings/describe.d.ts +27 -0
  299. package/dist/settings/describe.d.ts.map +1 -0
  300. package/dist/settings/describe.js +62 -0
  301. package/dist/settings/describe.js.map +1 -0
  302. package/dist/settings/duration.d.ts +16 -0
  303. package/dist/settings/duration.d.ts.map +1 -0
  304. package/dist/settings/duration.js +56 -0
  305. package/dist/settings/duration.js.map +1 -0
  306. package/dist/settings/field-order.d.ts +37 -0
  307. package/dist/settings/field-order.d.ts.map +1 -0
  308. package/dist/settings/field-order.js +100 -0
  309. package/dist/settings/field-order.js.map +1 -0
  310. package/dist/settings/field-search.d.ts +10 -0
  311. package/dist/settings/field-search.d.ts.map +1 -0
  312. package/dist/settings/field-search.js +18 -0
  313. package/dist/settings/field-search.js.map +1 -0
  314. package/dist/settings/fields/builders.d.ts +41 -0
  315. package/dist/settings/fields/builders.d.ts.map +1 -0
  316. package/dist/settings/fields/builders.js +83 -0
  317. package/dist/settings/fields/builders.js.map +1 -0
  318. package/dist/settings/fields/field.d.ts +199 -0
  319. package/dist/settings/fields/field.d.ts.map +1 -0
  320. package/dist/settings/fields/field.js +33 -0
  321. package/dist/settings/fields/field.js.map +1 -0
  322. package/dist/settings/fields/zod-schema.d.ts +67 -0
  323. package/dist/settings/fields/zod-schema.d.ts.map +1 -0
  324. package/dist/settings/fields/zod-schema.js +281 -0
  325. package/dist/settings/fields/zod-schema.js.map +1 -0
  326. package/dist/settings/in-memory/in-memory-guild-directory.d.ts +24 -0
  327. package/dist/settings/in-memory/in-memory-guild-directory.d.ts.map +1 -0
  328. package/dist/settings/in-memory/in-memory-guild-directory.js +42 -0
  329. package/dist/settings/in-memory/in-memory-guild-directory.js.map +1 -0
  330. package/dist/settings/in-memory/in-memory-settings-store.d.ts +8 -0
  331. package/dist/settings/in-memory/in-memory-settings-store.d.ts.map +1 -0
  332. package/dist/settings/in-memory/in-memory-settings-store.js +43 -0
  333. package/dist/settings/in-memory/in-memory-settings-store.js.map +1 -0
  334. package/dist/settings/in-memory/in-process-notifier.d.ts +9 -0
  335. package/dist/settings/in-memory/in-process-notifier.d.ts.map +1 -0
  336. package/dist/settings/in-memory/in-process-notifier.js +32 -0
  337. package/dist/settings/in-memory/in-process-notifier.js.map +1 -0
  338. package/dist/settings/index.d.ts +22 -0
  339. package/dist/settings/index.d.ts.map +1 -0
  340. package/dist/settings/index.js +31 -0
  341. package/dist/settings/index.js.map +1 -0
  342. package/dist/settings/is-plain-object.d.ts +6 -0
  343. package/dist/settings/is-plain-object.d.ts.map +1 -0
  344. package/dist/settings/is-plain-object.js +15 -0
  345. package/dist/settings/is-plain-object.js.map +1 -0
  346. package/dist/settings/messages.d.ts +61 -0
  347. package/dist/settings/messages.d.ts.map +1 -0
  348. package/dist/settings/messages.js +155 -0
  349. package/dist/settings/messages.js.map +1 -0
  350. package/dist/settings/migrate.d.ts +35 -0
  351. package/dist/settings/migrate.d.ts.map +1 -0
  352. package/dist/settings/migrate.js +42 -0
  353. package/dist/settings/migrate.js.map +1 -0
  354. package/dist/settings/ports/guild-directory.d.ts +61 -0
  355. package/dist/settings/ports/guild-directory.d.ts.map +1 -0
  356. package/dist/settings/ports/guild-directory.js +3 -0
  357. package/dist/settings/ports/guild-directory.js.map +1 -0
  358. package/dist/settings/ports/settings-changed-notifier.d.ts +20 -0
  359. package/dist/settings/ports/settings-changed-notifier.d.ts.map +1 -0
  360. package/dist/settings/ports/settings-changed-notifier.js +3 -0
  361. package/dist/settings/ports/settings-changed-notifier.js.map +1 -0
  362. package/dist/settings/ports/settings-store.d.ts +76 -0
  363. package/dist/settings/ports/settings-store.d.ts.map +1 -0
  364. package/dist/settings/ports/settings-store.js +3 -0
  365. package/dist/settings/ports/settings-store.js.map +1 -0
  366. package/dist/settings/registry.d.ts +31 -0
  367. package/dist/settings/registry.d.ts.map +1 -0
  368. package/dist/settings/registry.js +102 -0
  369. package/dist/settings/registry.js.map +1 -0
  370. package/dist/settings/settings-declaration-error.d.ts +13 -0
  371. package/dist/settings/settings-declaration-error.d.ts.map +1 -0
  372. package/dist/settings/settings-declaration-error.js +22 -0
  373. package/dist/settings/settings-declaration-error.js.map +1 -0
  374. package/dist/settings/settings-service.d.ts +148 -0
  375. package/dist/settings/settings-service.d.ts.map +1 -0
  376. package/dist/settings/settings-service.js +279 -0
  377. package/dist/settings/settings-service.js.map +1 -0
  378. package/dist/settings/settings-validation-error.d.ts +23 -0
  379. package/dist/settings/settings-validation-error.d.ts.map +1 -0
  380. package/dist/settings/settings-validation-error.js +17 -0
  381. package/dist/settings/settings-validation-error.js.map +1 -0
  382. package/dist/settings/status.d.ts +16 -0
  383. package/dist/settings/status.d.ts.map +1 -0
  384. package/dist/settings/status.js +18 -0
  385. package/dist/settings/status.js.map +1 -0
  386. package/dist/settings/suggest.d.ts +77 -0
  387. package/dist/settings/suggest.d.ts.map +1 -0
  388. package/dist/settings/suggest.js +192 -0
  389. package/dist/settings/suggest.js.map +1 -0
  390. package/dist/settings/system/kernel-settings.d.ts +43 -0
  391. package/dist/settings/system/kernel-settings.d.ts.map +1 -0
  392. package/dist/settings/system/kernel-settings.js +53 -0
  393. package/dist/settings/system/kernel-settings.js.map +1 -0
  394. package/dist/settings/system/module-gate.d.ts +31 -0
  395. package/dist/settings/system/module-gate.d.ts.map +1 -0
  396. package/dist/settings/system/module-gate.js +34 -0
  397. package/dist/settings/system/module-gate.js.map +1 -0
  398. package/dist/settings/testing/index.d.ts +2 -0
  399. package/dist/settings/testing/index.d.ts.map +1 -0
  400. package/dist/settings/testing/index.js +6 -0
  401. package/dist/settings/testing/index.js.map +1 -0
  402. package/dist/settings/testing/settings-store-contract.d.ts +29 -0
  403. package/dist/settings/testing/settings-store-contract.d.ts.map +1 -0
  404. package/dist/settings/testing/settings-store-contract.js +192 -0
  405. package/dist/settings/testing/settings-store-contract.js.map +1 -0
  406. package/dist/settings/timed-search.d.ts +26 -0
  407. package/dist/settings/timed-search.d.ts.map +1 -0
  408. package/dist/settings/timed-search.js +48 -0
  409. package/dist/settings/timed-search.js.map +1 -0
  410. package/dist/settings/types.d.ts +37 -0
  411. package/dist/settings/types.d.ts.map +1 -0
  412. package/dist/settings/types.js +3 -0
  413. package/dist/settings/types.js.map +1 -0
  414. package/dist/settings/validate.d.ts +55 -0
  415. package/dist/settings/validate.d.ts.map +1 -0
  416. package/dist/settings/validate.js +196 -0
  417. package/dist/settings/validate.js.map +1 -0
  418. package/package.json +463 -88
  419. package/src/authz/require-level-guard.ts +1 -1
  420. package/src/color.ts +134 -0
  421. package/src/discord/command/all-required.ts +12 -0
  422. package/src/discord/command/autocomplete-dispatcher.ts +2 -1
  423. package/src/discord/command/autocomplete-limits.ts +2 -0
  424. package/src/discord/command/autocomplete.ts +8 -7
  425. package/src/discord/command/command-responders.ts +39 -0
  426. package/src/discord/command/context.ts +14 -54
  427. package/src/discord/command/cooldown-guard.ts +1 -1
  428. package/src/discord/command/create-command.ts +19 -94
  429. package/src/discord/command/create-context-menu-command.ts +19 -97
  430. package/src/discord/command/errors.ts +1 -1
  431. package/src/discord/command/guard.ts +24 -12
  432. package/src/discord/command/options.ts +202 -412
  433. package/src/discord/command/passes-guard.ts +37 -0
  434. package/src/discord/command/permission-guard.ts +1 -1
  435. package/src/discord/command/render-command-failure.ts +71 -0
  436. package/src/discord/command/route-key.ts +9 -0
  437. package/src/discord/command/router.ts +73 -12
  438. package/src/discord/command/send-embed.ts +42 -0
  439. package/src/discord/command/types.ts +7 -1
  440. package/src/discord/components/component-access.ts +20 -0
  441. package/src/discord/components/component-router.ts +73 -36
  442. package/src/discord/components/interactive-message/index.ts +5 -3
  443. package/src/discord/components/interactive-message/interactive-message-collector.ts +4 -68
  444. package/src/discord/components/interactive-message/message-edit-options.ts +47 -0
  445. package/src/discord/components/interactive-message/mount-interactive-message.ts +5 -3
  446. package/src/discord/components/interactive-message/state-store.ts +20 -0
  447. package/src/discord/components/paginator/mount-paginator.ts +3 -3
  448. package/src/discord/components/paginator/paginator-view.ts +1 -1
  449. package/src/discord/components/persistent-paginator.ts +8 -9
  450. package/src/discord/components/settings-editor/from-declaration-controls.ts +371 -0
  451. package/src/discord/components/settings-editor/from-declaration-display.ts +155 -0
  452. package/src/discord/components/settings-editor/from-declaration-layout.ts +247 -0
  453. package/src/discord/components/settings-editor/from-declaration-search.ts +77 -0
  454. package/src/discord/components/settings-editor/from-declaration-stored.ts +39 -0
  455. package/src/discord/components/settings-editor/from-declaration-subject.ts +110 -0
  456. package/src/discord/components/settings-editor/from-declaration-values.ts +240 -0
  457. package/src/discord/components/settings-editor/from-declaration.ts +218 -0
  458. package/src/discord/components/settings-editor/index.ts +5 -2
  459. package/src/discord/components/settings-editor/mount-settings-editor.ts +28 -10
  460. package/src/discord/components/settings-editor/navigation.ts +41 -0
  461. package/src/discord/components/settings-editor/settings-editor-card.view.ts +79 -36
  462. package/src/discord/components/settings-editor/settings-editor-field-modal.ts +76 -68
  463. package/src/discord/components/settings-editor/settings-editor-field-values.ts +11 -0
  464. package/src/discord/components/settings-editor/settings-editor-fields.ts +6 -11
  465. package/src/discord/components/settings-editor/settings-editor.view.ts +11 -24
  466. package/src/discord/events/event-guild-id.ts +35 -0
  467. package/src/discord/events/event-router.ts +46 -13
  468. package/src/discord/i18n.ts +47 -8
  469. package/src/discord/interaction/interaction-locale.ts +19 -0
  470. package/src/discord/interaction/locale-resolver.ts +28 -0
  471. package/src/discord/interaction/modal.ts +5 -40
  472. package/src/discord/interaction/reply-locale.ts +35 -0
  473. package/src/discord/interaction/select-bounds.ts +33 -0
  474. package/src/discord/interaction/select-menu.ts +2 -9
  475. package/src/discord/placeholder-registry.ts +1 -1
  476. package/src/discord/presenter.ts +1 -1
  477. package/src/discord/settings/discord-guild-directory.ts +214 -0
  478. package/src/discord/settings/guild-locale-resolver.ts +38 -0
  479. package/src/discord/settings/is-module-disabled.ts +22 -0
  480. package/src/discord/settings/module-disabled-embed.ts +19 -0
  481. package/src/discord/settings/require-configured.ts +68 -0
  482. package/src/discord/ui/color-aliases.ts +70 -0
  483. package/src/discord/ui/color-input.ts +8 -173
  484. package/src/discord/ui/colors.ts +2 -3
  485. package/src/discord/ui/embed.ts +3 -9
  486. package/src/discord/ui/truncate-text.ts +15 -0
  487. package/src/errors/error-message.ts +10 -0
  488. package/src/i18n/business-message.ts +1 -1
  489. package/src/i18n/catalog.ts +14 -0
  490. package/src/i18n/locale.ts +3 -0
  491. package/src/i18n/translator.ts +3 -0
  492. package/src/module/module.ts +15 -1
  493. package/src/resilience/retry.ts +1 -1
  494. package/src/scheduler/scheduler.ts +2 -5
  495. package/src/settings/cache.ts +70 -0
  496. package/src/settings/choice.ts +9 -0
  497. package/src/settings/decode-stored.ts +40 -0
  498. package/src/settings/define-settings.ts +214 -0
  499. package/src/settings/describe-field.ts +155 -0
  500. package/src/settings/describe.ts +90 -0
  501. package/src/settings/duration.ts +54 -0
  502. package/src/settings/field-order.ts +128 -0
  503. package/src/settings/field-search.ts +16 -0
  504. package/src/settings/fields/builders.ts +161 -0
  505. package/src/settings/fields/field.ts +205 -0
  506. package/src/settings/fields/zod-schema.ts +300 -0
  507. package/src/settings/in-memory/in-memory-guild-directory.ts +69 -0
  508. package/src/settings/in-memory/in-memory-settings-store.ts +46 -0
  509. package/src/settings/in-memory/in-process-notifier.ts +40 -0
  510. package/src/settings/index.ts +72 -0
  511. package/src/settings/is-plain-object.ts +11 -0
  512. package/src/settings/messages.ts +161 -0
  513. package/src/settings/migrate.ts +58 -0
  514. package/src/settings/ports/guild-directory.ts +48 -0
  515. package/src/settings/ports/settings-changed-notifier.ts +20 -0
  516. package/src/settings/ports/settings-store.ts +74 -0
  517. package/src/settings/registry.ts +137 -0
  518. package/src/settings/settings-declaration-error.ts +16 -0
  519. package/src/settings/settings-service.ts +535 -0
  520. package/src/settings/settings-validation-error.ts +41 -0
  521. package/src/settings/status.ts +25 -0
  522. package/src/settings/suggest.ts +267 -0
  523. package/src/settings/system/kernel-settings.ts +74 -0
  524. package/src/settings/system/module-gate.ts +49 -0
  525. package/src/settings/testing/index.ts +7 -0
  526. package/src/settings/testing/settings-store-contract.ts +278 -0
  527. package/src/settings/timed-search.ts +56 -0
  528. package/src/settings/types.ts +42 -0
  529. package/src/settings/validate.ts +298 -0
@@ -1,7 +1,10 @@
1
- import type { InteractiveView } from "@/discord/components/interactive-message";
1
+ import { subtext, TimestampStyles, time } from "discord.js";
2
+ import type { InteractiveView } from "@/discord/components/interactive-message/interactive-message-collector";
2
3
  import {
3
4
  canReturn,
4
5
  currentLevelKey,
6
+ indexLevels,
7
+ levelTrail,
5
8
  ROOT_LEVEL_KEY,
6
9
  type SettingsEditorLevel,
7
10
  } from "@/discord/components/settings-editor/navigation";
@@ -20,7 +23,9 @@ import { SETTINGS_EDITOR_MESSAGES } from "@/discord/i18n";
20
23
  import { type Button, createActionRow } from "@/discord/interaction/button";
21
24
  import { type CardBlock, type CardDef, createCard } from "@/discord/ui/card";
22
25
  import { EMBED_COLORS } from "@/discord/ui/colors";
23
- import type { Locale, Translator } from "@/i18n";
26
+ import { truncateText } from "@/discord/ui/truncate-text";
27
+ import type { Locale } from "@/i18n/locale";
28
+ import type { Translator } from "@/i18n/translator";
24
29
 
25
30
  /**
26
31
  * Discord refuses a card carrying more than {@link import("@/discord/ui/card").MAX_CARD_COMPONENTS}
@@ -68,6 +73,23 @@ export const MAX_CARD_BUTTON_LEVEL_ENTRIES = 20;
68
73
  */
69
74
  const ENTRY_ACTIONS_RESERVE = 2;
70
75
 
76
+ /**
77
+ * The most characters an entry's "Current: …" line may take. Well under the
78
+ * 4000 a card's text may hold in total: ten entries each showing a long value
79
+ * must still leave room for the preview and the chrome around them.
80
+ */
81
+ const MAX_CURRENT_VALUE_LENGTH = 200;
82
+
83
+ /** Separates a group's members on its one "Current: …" line. */
84
+ const GROUP_MEMBER_SEPARATOR = " · ";
85
+
86
+ /** A value's own line breaks, folded so it stays on the one line it is given. */
87
+ const LINE_BREAKS = /\s*\r?\n\s*/g;
88
+
89
+ function oneLine(value: string): string {
90
+ return value.replace(LINE_BREAKS, " ");
91
+ }
92
+
71
93
  /**
72
94
  * The button a menu entry's own `Section` carries, derived from the field's
73
95
  * key rather than declared alongside it — the same relationship `ids.field`
@@ -102,30 +124,24 @@ export function createCardEditorView<S, A, F extends string>(
102
124
  translator: Translator,
103
125
  locale: Locale,
104
126
  dressing: SettingsEditorDressing,
127
+ displayValue?: (subject: S, field: F) => string | null,
105
128
  ): InteractiveView<SettingsEditorState<S, A>> {
106
129
  const screenTitle = translator.translate(locale, chrome.titleKey);
107
- // Separates the levels in the breadcrumb trail.
108
- const BREADCRUMB_SEPARATOR = " › ";
109
130
 
110
- // The root is a level like any other, named after the screen itself, so
111
- // rendering never special-cases "no depth declared" — the same bargain
112
- // `createEditorView` strikes for the embeds layout. Its own `entryLayout`
113
- // is the chrome's, mirroring `SettingsEditorLevel.entryLayout` for the one
114
- // level that is never itself a `SettingsEditorLevel`.
115
- const root: SettingsEditorLevel<F> = {
116
- key: ROOT_LEVEL_KEY,
117
- titleKey: chrome.titleKey,
118
- fields,
119
- entryLayout: chrome.entryLayout,
120
- };
121
- const allLevels: readonly SettingsEditorLevel<F>[] = [root, ...(levels ?? [])];
131
+ // The root's own `entryLayout` is the chrome's, mirroring
132
+ // `SettingsEditorLevel.entryLayout` for the one level that is never itself a
133
+ // `SettingsEditorLevel`.
134
+ const allLevels = indexLevels(
135
+ { key: ROOT_LEVEL_KEY, titleKey: chrome.titleKey, fields, entryLayout: chrome.entryLayout },
136
+ levels,
137
+ );
122
138
 
123
139
  // A level's fields are authored, not accumulated: passing more entries than a
124
140
  // card can carry is a mistake in the declaration, and Discord would reject
125
141
  // the whole message rather than show the first ones that fit. Failing here
126
142
  // names the level; failing at render time would only say the message was
127
143
  // invalid.
128
- for (const level of allLevels) {
144
+ for (const level of allLevels.all) {
129
145
  // A `"buttons"` level spends its budget on rows of `ActionRow`s rather
130
146
  // than `Section`s, so it is checked against its own, higher cap instead
131
147
  // of the `"sections"` one — failing here names the level rather than the
@@ -149,18 +165,10 @@ export function createCardEditorView<S, A, F extends string>(
149
165
  }
150
166
  }
151
167
 
152
- function levelAt(key: string): SettingsEditorLevel<F> {
153
- return allLevels.find((level) => level.key === key) ?? root;
154
- }
155
-
156
- /**
157
- * Where the reader is, as a trail rather than only the current name: a level
158
- * called "Roles" says nothing on its own about which type it belongs to.
159
- */
160
168
  function heading(state: SettingsEditorState<S, A>): string {
161
- return state.path
162
- .map((key) => translator.translate(locale, levelAt(key).titleKey))
163
- .join(BREADCRUMB_SEPARATOR);
169
+ return levelTrail(state.path, allLevels, (level) =>
170
+ translator.translate(locale, level.titleKey),
171
+ );
164
172
  }
165
173
 
166
174
  /**
@@ -170,19 +178,54 @@ export function createCardEditorView<S, A, F extends string>(
170
178
  * an embed's timestamp already did.
171
179
  */
172
180
  function signature(): string {
173
- const unixSeconds = Math.floor(dressing.clock.now().getTime() / 1000);
174
- return `-# ${screenTitle} • <t:${unixSeconds}:t>`;
181
+ return subtext(`${screenTitle} • ${time(dressing.clock.now(), TimestampStyles.ShortTime)}`);
182
+ }
183
+
184
+ /**
185
+ * What an entry currently holds, as the text after the "Current:" prefix:
186
+ * the hook's own answer for a single field, one `label: value` per member
187
+ * holding one for a group, nothing for a level. `null` when there is
188
+ * nothing to show — no hook, or a hook answering `null` throughout.
189
+ */
190
+ function currentText(field: SettingsEditorField<F>, subject: S): string | null {
191
+ if (displayValue === undefined || field.kind === "level") {
192
+ return null;
193
+ }
194
+ if (field.kind !== "group") {
195
+ return displayValue(subject, field.key);
196
+ }
197
+ const members = field.fields.flatMap((member) => {
198
+ const value = displayValue(subject, member.key);
199
+ return value === null ? [] : [`${translator.translate(locale, member.labelKey)}: ${value}`];
200
+ });
201
+ return members.length === 0 ? null : members.join(GROUP_MEMBER_SEPARATOR);
202
+ }
203
+
204
+ /** {@link currentText}, prefixed and cut to {@link MAX_CURRENT_VALUE_LENGTH}; none when it is `null`. */
205
+ function currentLines(field: SettingsEditorField<F>, subject: S): string[] {
206
+ const value = currentText(field, subject);
207
+ if (value === null) {
208
+ return [];
209
+ }
210
+ const line = translator.translate(locale, SETTINGS_EDITOR_MESSAGES.currentValue, {
211
+ value: oneLine(value),
212
+ });
213
+ return [truncateText(line, MAX_CURRENT_VALUE_LENGTH)];
175
214
  }
176
215
 
177
216
  /**
178
217
  * One menu entry, as the `Section` that replaces it: its label as the title,
179
- * its hint as the line under it, and the button `mountSettingsEditor`
218
+ * its hint as the line under it, what it currently holds under that when the
219
+ * screen declares `displayValue`, and the button `mountSettingsEditor`
180
220
  * registered for it as the accessory — the very button that opens its modal
181
221
  * or descends into it, reached by a click instead of a pick.
182
222
  */
183
- function entrySection(field: SettingsEditorField<F>): CardBlock<SettingsEditorState<S, A>> {
223
+ function entrySection(
224
+ field: SettingsEditorField<F>,
225
+ subject: S,
226
+ ): CardBlock<SettingsEditorState<S, A>> {
184
227
  const title = translator.translate(locale, field.labelKey);
185
- const lines = [translator.translate(locale, field.hintKey)];
228
+ const lines = [translator.translate(locale, field.hintKey), ...currentLines(field, subject)];
186
229
  const action = fieldButtons.get(field.key);
187
230
  return action === undefined
188
231
  ? { kind: "entry", title, lines }
@@ -287,7 +330,7 @@ export function createCardEditorView<S, A, F extends string>(
287
330
  state: SettingsEditorState<S, A>,
288
331
  ): readonly (CardBlock<SettingsEditorState<S, A>> | null)[] {
289
332
  const confirmingReset = state.confirming === SETTINGS_EDITOR_RESET_CONFIRMING;
290
- const level = levelAt(currentLevelKey(state.path));
333
+ const level = allLevels.at(currentLevelKey(state.path));
291
334
 
292
335
  const notice: CardBlock<SettingsEditorState<S, A>> | null = confirmingReset
293
336
  ? {
@@ -302,10 +345,10 @@ export function createCardEditorView<S, A, F extends string>(
302
345
  const entries: CardBlock<SettingsEditorState<S, A>>[] =
303
346
  level.entryLayout === "buttons"
304
347
  ? buttonRows(levelButtons(level, state))
305
- : level.fields.map((field) => entrySection(field));
348
+ : level.fields.map((field) => entrySection(field, state.subject));
306
349
 
307
350
  return [
308
- { kind: "text", content: `-# ${heading(state)}` },
351
+ { kind: "text", content: subtext(heading(state)) },
309
352
  notice,
310
353
  ...(chrome.extraBlocks?.(state, confirmingReset) ?? []),
311
354
  { kind: "separator" },
@@ -17,12 +17,15 @@ import {
17
17
  type ChannelFieldDef,
18
18
  createModal,
19
19
  type ModalFields,
20
+ type ModalValues,
20
21
  type RoleFieldDef,
21
22
  type SelectFieldDef,
22
23
  type TextFieldDef,
23
24
  type UserFieldDef,
24
25
  } from "@/discord/interaction/modal";
25
- import type { Locale, Translator } from "@/i18n";
26
+ import type { SelectBounds } from "@/discord/interaction/select-bounds";
27
+ import type { Locale } from "@/i18n/locale";
28
+ import type { Translator } from "@/i18n/translator";
26
29
 
27
30
  const EDITOR_FIELD_MODAL_ID = "settings-editor:field-value";
28
31
  const EDITOR_GROUP_MODAL_ID = "settings-editor:group-value";
@@ -125,8 +128,23 @@ export async function promptEditorFieldValue<F extends string>(
125
128
  ? createImageModal(field, current, translator, locale)
126
129
  : createTextModal(field, current, translator, locale);
127
130
 
131
+ const submitted = await collectModalSubmission(
132
+ interaction,
133
+ modal.build(nextModalOpenState(field.key)),
134
+ );
135
+ return submitted === null ? null : { interaction: submitted, ...modal.read(submitted) };
136
+ }
137
+
138
+ /**
139
+ * Show `built` and wait for this very opening's submission. `null` on timeout,
140
+ * on any failure to show or collect it, and on a submission that did not come
141
+ * from a message.
142
+ */
143
+ async function collectModalSubmission(
144
+ interaction: MessageComponentInteraction,
145
+ built: ModalBuilder,
146
+ ): Promise<ModalMessageModalSubmitInteraction | null> {
128
147
  try {
129
- const built = modal.build(nextModalOpenState(field.key));
130
148
  const expectedCustomId = customIdOf(built);
131
149
  await interaction.showModal(built);
132
150
  const submitted = await interaction.awaitModalSubmit({
@@ -141,7 +159,7 @@ export async function promptEditorFieldValue<F extends string>(
141
159
  if (!submitted.isFromMessage()) {
142
160
  return null;
143
161
  }
144
- return { interaction: submitted, ...modal.read(submitted) };
162
+ return submitted;
145
163
  } catch {
146
164
  return null;
147
165
  }
@@ -192,22 +210,11 @@ export async function promptEditorGroupValue<F extends string>(
192
210
  locale: Locale,
193
211
  ): Promise<SettingsEditorGroupPrompt<F> | null> {
194
212
  const modal = createGroupModal(group, currentValue, legend, translator, locale);
195
- try {
196
- const built = modal.build(nextModalOpenState(group.key));
197
- const expectedCustomId = customIdOf(built);
198
- await interaction.showModal(built);
199
- const submitted = await interaction.awaitModalSubmit({
200
- time: MODAL_TIMEOUT_MS,
201
- filter: (submission) =>
202
- submission.user.id === interaction.user.id && submission.customId === expectedCustomId,
203
- });
204
- if (!submitted.isFromMessage()) {
205
- return null;
206
- }
207
- return { interaction: submitted, values: modal.read(submitted) };
208
- } catch {
209
- return null;
210
- }
213
+ const submitted = await collectModalSubmission(
214
+ interaction,
215
+ modal.build(nextModalOpenState(group.key)),
216
+ );
217
+ return submitted === null ? null : { interaction: submitted, values: modal.read(submitted) };
211
218
  }
212
219
 
213
220
  /** Reading a group's submission back — the group's own counterpart of {@link EditorFieldModal}. */
@@ -340,25 +347,44 @@ function valueFieldDefOf<F extends string>(
340
347
  };
341
348
  }
342
349
 
343
- /** Every field but the images: one optional text input, emptied to clear. */
344
- function createTextModal<F extends string>(
345
- field: SettingsEditorValueField<F>,
346
- current: SettingsEditorFieldValue,
350
+ /**
351
+ * The one modal a single field opens: titled after the field, carrying
352
+ * `fields`, and read back into the shape every single-field caller shares.
353
+ */
354
+ function createFieldModal<const M extends ModalFields>(
355
+ labelKey: string,
356
+ fields: M,
347
357
  translator: Translator,
348
358
  locale: Locale,
359
+ toSubmission: (values: ModalValues<M>) => ReturnType<EditorFieldModal["read"]>,
349
360
  ): EditorFieldModal {
350
- const label = translator.translate(locale, field.labelKey);
351
361
  const modal = createModal({
352
362
  id: EDITOR_FIELD_MODAL_ID,
353
- title: modalTitle(label, translator, locale),
354
- fields: { value: valueFieldDefOf(field, current, translator, locale) },
363
+ title: modalTitle(translator.translate(locale, labelKey), translator, locale),
364
+ fields,
355
365
  });
356
366
  return {
357
367
  build: (state) => modal.build(state),
358
- read: (interaction) => ({ value: modal.read(interaction).value, upload: null, ids: null }),
368
+ read: (interaction) => toSubmission(modal.read(interaction)),
359
369
  };
360
370
  }
361
371
 
372
+ /** Every field but the images: one optional text input, emptied to clear. */
373
+ function createTextModal<F extends string>(
374
+ field: SettingsEditorValueField<F>,
375
+ current: SettingsEditorFieldValue,
376
+ translator: Translator,
377
+ locale: Locale,
378
+ ): EditorFieldModal {
379
+ return createFieldModal(
380
+ field.labelKey,
381
+ { value: valueFieldDefOf(field, current, translator, locale) },
382
+ translator,
383
+ locale,
384
+ (values) => ({ value: values.value, upload: null, ids: null }),
385
+ );
386
+ }
387
+
362
388
  /**
363
389
  * An image field: a file upload and the address input side by side, both
364
390
  * optional, two of the five components a modal may carry.
@@ -373,11 +399,9 @@ function createImageModal<F extends string>(
373
399
  translator: Translator,
374
400
  locale: Locale,
375
401
  ): EditorFieldModal {
376
- const label = translator.translate(locale, field.labelKey);
377
- const modal = createModal({
378
- id: EDITOR_FIELD_MODAL_ID,
379
- title: modalTitle(label, translator, locale),
380
- fields: {
402
+ return createFieldModal(
403
+ field.labelKey,
404
+ {
381
405
  upload: {
382
406
  kind: "file",
383
407
  label: translator.translate(locale, SETTINGS_EDITOR_MESSAGES.modalUploadLabel),
@@ -402,14 +426,10 @@ function createImageModal<F extends string>(
402
426
  ...(current.text === null ? {} : { value: current.text }),
403
427
  },
404
428
  },
405
- });
406
- return {
407
- build: (state) => modal.build(state),
408
- read: (interaction) => {
409
- const values = modal.read(interaction);
410
- return { value: values.value, upload: values.upload?.[0] ?? null, ids: null };
411
- },
412
- };
429
+ translator,
430
+ locale,
431
+ (values) => ({ value: values.value, upload: values.upload?.[0] ?? null, ids: null }),
432
+ );
413
433
  }
414
434
 
415
435
  /**
@@ -426,11 +446,9 @@ function createPickerModal<F extends string>(
426
446
  translator: Translator,
427
447
  locale: Locale,
428
448
  ): EditorFieldModal {
429
- const label = translator.translate(locale, field.labelKey);
430
- const modal = createModal({
431
- id: EDITOR_FIELD_MODAL_ID,
432
- title: modalTitle(label, translator, locale),
433
- fields: {
449
+ return createFieldModal(
450
+ field.labelKey,
451
+ {
434
452
  value: pickerFieldDef(
435
453
  field,
436
454
  pickerShared(field, translator, locale),
@@ -439,14 +457,16 @@ function createPickerModal<F extends string>(
439
457
  locale,
440
458
  ),
441
459
  },
442
- });
443
- return {
444
- build: (state) => modal.build(state),
445
- read: (interaction) => {
446
- const read = modal.read(interaction) as { value: readonly string[] | null };
447
- return { value: null, upload: null, ids: read.value };
448
- },
449
- };
460
+ translator,
461
+ locale,
462
+ (values) => ({ value: null, upload: null, ids: values.value as readonly string[] | null }),
463
+ );
464
+ }
465
+
466
+ /** The part of a picker component every kind of picker declares alike. */
467
+ interface PickerShared extends SelectBounds {
468
+ label: string;
469
+ required: boolean;
450
470
  }
451
471
 
452
472
  /**
@@ -457,13 +477,7 @@ function pickerShared<F extends string>(
457
477
  field: SettingsEditorPickerField<F>,
458
478
  translator: Translator,
459
479
  locale: Locale,
460
- ): {
461
- label: string;
462
- required: boolean;
463
- minValues?: number;
464
- maxValues?: number;
465
- placeholder?: string;
466
- } {
480
+ ): PickerShared {
467
481
  const placeholder =
468
482
  field.pickerPlaceholderKey === undefined
469
483
  ? undefined
@@ -483,13 +497,7 @@ function pickerShared<F extends string>(
483
497
  /** The one component a picker modal carries, in the shape its field's kind names. */
484
498
  function pickerFieldDef<F extends string>(
485
499
  field: SettingsEditorPickerField<F>,
486
- shared: {
487
- label: string;
488
- required: boolean;
489
- minValues?: number;
490
- maxValues?: number;
491
- placeholder?: string;
492
- },
500
+ shared: PickerShared,
493
501
  held: readonly string[],
494
502
  translator: Translator,
495
503
  locale: Locale,
@@ -522,7 +530,7 @@ function pickerFieldDef<F extends string>(
522
530
  kind: "select",
523
531
  ...shared,
524
532
  options: field.choices.map((choice) => ({
525
- label: translator.translate(locale, choice.labelKey),
533
+ label: translator.translate(locale, choice.labelKey, choice.labelParams),
526
534
  value: choice.value,
527
535
  ...(choice.descriptionKey === undefined
528
536
  ? {}
@@ -0,0 +1,11 @@
1
+ import type { SettingsEditorFieldValue } from "@/discord/components/settings-editor/settings-editor-fields";
2
+
3
+ /** A text override, as the modal editing it opens on. */
4
+ export function textFieldValue(value: string | null): SettingsEditorFieldValue {
5
+ return { text: value, uploaded: false, picked: [] };
6
+ }
7
+
8
+ /** A picker override, as the modal editing it opens on — prefilled from what it already holds. */
9
+ export function pickedFieldValue(ids: readonly string[]): SettingsEditorFieldValue {
10
+ return { text: null, uploaded: false, picked: ids };
11
+ }
@@ -1,6 +1,6 @@
1
1
  import type { ChannelType } from "discord.js";
2
2
  import type { TextFieldStyleName } from "@/discord/interaction/modal";
3
- import type { TranslationParams } from "@/i18n";
3
+ import type { TranslationParams } from "@/i18n/translator";
4
4
 
5
5
  /**
6
6
  * How a field is entered.
@@ -129,6 +129,11 @@ export interface SettingsEditorChoice {
129
129
  /** What is stored — never shown, so it is not translated. */
130
130
  readonly value: string;
131
131
  readonly labelKey: string;
132
+ /**
133
+ * Placeholders of `labelKey`, for a label computed at run time (a search
134
+ * result) rather than declared.
135
+ */
136
+ readonly labelParams?: TranslationParams;
132
137
  /** Shown under the label, as a menu entry's hint is. Optional: a two-way toggle rarely needs one. */
133
138
  readonly descriptionKey?: string;
134
139
  }
@@ -244,16 +249,6 @@ export interface SettingsEditorFieldValue {
244
249
  readonly picked: readonly string[];
245
250
  }
246
251
 
247
- /** A text override, as the modal editing it opens on. */
248
- export function textFieldValue(value: string | null): SettingsEditorFieldValue {
249
- return { text: value, uploaded: false, picked: [] };
250
- }
251
-
252
- /** A picker override, as the modal editing it opens on — prefilled from what it already holds. */
253
- export function pickedFieldValue(ids: readonly string[]): SettingsEditorFieldValue {
254
- return { text: null, uploaded: false, picked: ids };
255
- }
256
-
257
252
  /** Discord's own cap on a modal text input, whatever the embed itself would take. */
258
253
  export const MAX_MODAL_INPUT_LENGTH = 4000;
259
254
 
@@ -8,10 +8,12 @@ import type { Clock } from "@/clock";
8
8
  import type {
9
9
  InteractiveMessagePayload,
10
10
  InteractiveView,
11
- } from "@/discord/components/interactive-message";
11
+ } from "@/discord/components/interactive-message/interactive-message-collector";
12
12
  import {
13
13
  canReturn,
14
14
  currentLevelKey,
15
+ indexLevels,
16
+ levelTrail,
15
17
  ROOT_LEVEL_KEY,
16
18
  type SettingsEditorLevel,
17
19
  type SettingsEditorPath,
@@ -33,7 +35,8 @@ import { createSelectRow, createStringSelect } from "@/discord/interaction/selec
33
35
  import type { CardBlock } from "@/discord/ui/card";
34
36
  import { EMBED_COLORS } from "@/discord/ui/colors";
35
37
  import type { MessageFile } from "@/discord/ui/message-attachments";
36
- import type { Locale, Translator } from "@/i18n";
38
+ import type { Locale } from "@/i18n/locale";
39
+ import type { Translator } from "@/i18n/translator";
37
40
 
38
41
  /**
39
42
  * The row type this file's own rendering builds — and, since the card layout
@@ -456,16 +459,11 @@ export function createEditorView<S, A, F extends string>(
456
459
  const screenTitle = translator.translate(locale, chrome.titleKey);
457
460
  const intro =
458
461
  chrome.introKey === undefined ? null : translator.translate(locale, chrome.introKey);
459
- // Separates the levels in the heading trail.
460
- const BREADCRUMB_SEPARATOR = " › ";
461
462
  const body = [intro, chrome.legend].filter(
462
463
  (part): part is string => part !== undefined && part !== null,
463
464
  );
464
465
 
465
- // The root is a level like any other, named after the screen itself, so
466
- // rendering never special-cases "no depth declared".
467
- const root: SettingsEditorLevel<F> = { key: ROOT_LEVEL_KEY, titleKey: chrome.titleKey, fields };
468
- const allLevels: readonly SettingsEditorLevel<F>[] = [root, ...(levels ?? [])];
466
+ const allLevels = indexLevels({ key: ROOT_LEVEL_KEY, titleKey: chrome.titleKey, fields }, levels);
469
467
 
470
468
  // A level's fields are authored, not accumulated: passing more than a menu can
471
469
  // hold is a mistake in the declaration, and Discord would reject the whole
@@ -474,7 +472,7 @@ export function createEditorView<S, A, F extends string>(
474
472
  //
475
473
  // Lists that *grow* — a guild's ticket types, its permission grants — are the
476
474
  // screen's own to page, through `extraRows`. See research D7.
477
- for (const level of allLevels) {
475
+ for (const level of allLevels.all) {
478
476
  if (level.fields.length > MAX_SELECT_OPTIONS) {
479
477
  throw new Error(
480
478
  `Settings editor level "${level.key}" declares ${level.fields.length} entries; a select menu holds ${MAX_SELECT_OPTIONS}. Split it into a deeper level.`,
@@ -497,17 +495,10 @@ export function createEditorView<S, A, F extends string>(
497
495
  }
498
496
  }
499
497
 
500
- // A path entry naming no declared level cannot happen — only a level field
501
- // puts one there — but falling back to the root keeps a corrupted state
502
- // showing a usable screen rather than an empty one.
503
- function levelAt(key: string): SettingsEditorLevel<F> {
504
- return allLevels.find((level) => level.key === key) ?? root;
505
- }
506
-
507
498
  // One menu per level, built once: the options of a level never vary with the
508
499
  // state, only which level is showing does.
509
500
  const menus = new Map(
510
- allLevels.map((level) => [
501
+ allLevels.all.map((level) => [
511
502
  level.key,
512
503
  createStringSelect({
513
504
  id: ids.field,
@@ -525,14 +516,10 @@ export function createEditorView<S, A, F extends string>(
525
516
  return menus.get(currentLevelKey(state.path)) ?? menus.get(ROOT_LEVEL_KEY);
526
517
  }
527
518
 
528
- /**
529
- * Where the reader is, as a trail rather than only the current name: a level
530
- * called "Roles" says nothing on its own about which type it belongs to.
531
- */
532
519
  function heading(state: SettingsEditorState<S, A>): string {
533
- return state.path
534
- .map((key) => translator.translate(locale, levelAt(key).titleKey))
535
- .join(BREADCRUMB_SEPARATOR);
520
+ return levelTrail(state.path, allLevels, (level) =>
521
+ translator.translate(locale, level.titleKey),
522
+ );
536
523
  }
537
524
 
538
525
  /**
@@ -0,0 +1,35 @@
1
+ import { Guild } from "discord.js";
2
+
3
+ /**
4
+ * The guild a gateway event concerns, read from its arguments: a `Guild`
5
+ * itself (`guildCreate`), or the first argument carrying a `guildId` (a
6
+ * message, a channel, an interaction) or a `guild` (a member, a ban, a role).
7
+ *
8
+ * @param args - the arguments discord.js passed to the listener.
9
+ * @returns the guild id, or `undefined` for an event bound to no guild.
10
+ */
11
+ export function eventGuildId(args: readonly unknown[]): string | undefined {
12
+ for (const arg of args) {
13
+ const guildId = guildIdOf(arg);
14
+ if (guildId !== undefined) {
15
+ return guildId;
16
+ }
17
+ }
18
+ return undefined;
19
+ }
20
+
21
+ function guildIdOf(arg: unknown): string | undefined {
22
+ if (arg instanceof Guild) {
23
+ return arg.id;
24
+ }
25
+ if (typeof arg !== "object" || arg === null) {
26
+ return undefined;
27
+ }
28
+ if ("guildId" in arg && typeof arg.guildId === "string") {
29
+ return arg.guildId;
30
+ }
31
+ if ("guild" in arg && typeof arg.guild === "object" && arg.guild !== null && "id" in arg.guild) {
32
+ return typeof arg.guild.id === "string" ? arg.guild.id : undefined;
33
+ }
34
+ return undefined;
35
+ }