@eifi1/ui-kit 0.25.1 → 0.27.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 (174) hide show
  1. package/README.md +16 -5
  2. package/dist/chart.d.ts +13 -9
  3. package/dist/components/account-chips.d.ts +1 -1
  4. package/dist/components/amount-input.d.ts +13 -9
  5. package/dist/components/button-group.d.ts +13 -9
  6. package/dist/components/calculator.d.ts +13 -9
  7. package/dist/components/confirm-dialog.d.ts +13 -9
  8. package/dist/components/copy-button.d.ts +10 -6
  9. package/dist/components/danger-confirm.d.ts +13 -9
  10. package/dist/components/data-table-cells.d.ts +1 -1
  11. package/dist/components/data-table-filter-popover.d.ts +1 -1
  12. package/dist/components/data-table-filters.d.ts +1 -1
  13. package/dist/components/data-table-labels.d.ts +1 -1
  14. package/dist/components/data-table-pagination.d.ts +1 -1
  15. package/dist/components/data-table.d.ts +1 -1
  16. package/dist/components/data-table.js +3 -2
  17. package/dist/components/data-table.js.map +1 -1
  18. package/dist/components/facing-pair.d.ts +13 -9
  19. package/dist/components/file-button.d.ts +13 -9
  20. package/dist/components/file-dropzone.d.ts +13 -9
  21. package/dist/components/form-actions.d.ts +13 -9
  22. package/dist/components/iban-input.d.ts +13 -9
  23. package/dist/components/language-select.d.ts +13 -9
  24. package/dist/components/money-field.d.ts +13 -9
  25. package/dist/components/number-field.d.ts +13 -9
  26. package/dist/components/number-input.d.ts +13 -9
  27. package/dist/components/numpad-sheet.d.ts +13 -9
  28. package/dist/components/phone-input.d.ts +13 -9
  29. package/dist/components/series-chart.d.ts +13 -9
  30. package/dist/components/settings-fields.d.ts +13 -9
  31. package/dist/components/share-card.d.ts +13 -9
  32. package/dist/components/swipeable-row.d.ts +5 -1
  33. package/dist/components/swipeable-row.js.map +1 -1
  34. package/dist/components/text-link.d.ts +13 -9
  35. package/dist/components/time-input.d.ts +13 -9
  36. package/dist/components/translation-review-labels.d.ts +4 -0
  37. package/dist/components/translation-review-labels.js +2 -1
  38. package/dist/components/translation-review-labels.js.map +1 -1
  39. package/dist/components/translation-review.d.ts +27 -3
  40. package/dist/components/translation-review.js +50 -21
  41. package/dist/components/translation-review.js.map +1 -1
  42. package/dist/components/ui.d.ts +10 -6
  43. package/dist/components/use-table-state.d.ts +1 -1
  44. package/dist/{data-table-labels-Cdn1QS2k.d.ts → data-table-labels-DUELeXpa.d.ts} +5 -0
  45. package/dist/data-table.d.ts +1 -1
  46. package/dist/feedback/feedback-attachment.d.ts +13 -9
  47. package/dist/feedback/feedback-attachment.js +3 -3
  48. package/dist/feedback/feedback-attachment.js.map +1 -1
  49. package/dist/feedback/feedback-capture.d.ts +71 -0
  50. package/dist/feedback/feedback-capture.js +194 -0
  51. package/dist/feedback/feedback-capture.js.map +1 -0
  52. package/dist/feedback/feedback-context.d.ts +71 -0
  53. package/dist/feedback/feedback-context.js +96 -0
  54. package/dist/feedback/feedback-context.js.map +1 -0
  55. package/dist/feedback/feedback-crash.d.ts +276 -0
  56. package/dist/feedback/feedback-crash.js +225 -0
  57. package/dist/feedback/feedback-crash.js.map +1 -0
  58. package/dist/feedback/feedback-dialog.d.ts +13 -9
  59. package/dist/feedback/feedback-dialog.js +5 -3
  60. package/dist/feedback/feedback-dialog.js.map +1 -1
  61. package/dist/feedback/feedback-inbox.d.ts +13 -9
  62. package/dist/feedback/feedback-inbox.js +89 -49
  63. package/dist/feedback/feedback-inbox.js.map +1 -1
  64. package/dist/feedback/feedback-labels.d.ts +71 -0
  65. package/dist/feedback/feedback-labels.js +48 -0
  66. package/dist/feedback/feedback-labels.js.map +1 -0
  67. package/dist/feedback/feedback-menu.d.ts +71 -0
  68. package/dist/feedback/feedback-menu.js +61 -0
  69. package/dist/feedback/feedback-menu.js.map +1 -0
  70. package/dist/feedback/feedback-record.d.ts +71 -0
  71. package/dist/feedback/feedback-record.js +104 -0
  72. package/dist/feedback/feedback-record.js.map +1 -0
  73. package/dist/feedback/feedback-row-detail.d.ts +71 -0
  74. package/dist/feedback/feedback-row-detail.js +437 -0
  75. package/dist/feedback/feedback-row-detail.js.map +1 -0
  76. package/dist/feedback/feedback-status-undo.d.ts +71 -0
  77. package/dist/feedback/feedback-status-undo.js +40 -0
  78. package/dist/feedback/feedback-status-undo.js.map +1 -0
  79. package/dist/feedback/feedback-submit.d.ts +182 -0
  80. package/dist/feedback/feedback-submit.js +101 -0
  81. package/dist/feedback/feedback-submit.js.map +1 -0
  82. package/dist/feedback/feedback-swipe.d.ts +152 -0
  83. package/dist/feedback/feedback-swipe.js +73 -0
  84. package/dist/feedback/feedback-swipe.js.map +1 -0
  85. package/dist/feedback/feedback-table.d.ts +71 -0
  86. package/dist/feedback/feedback-table.js +357 -0
  87. package/dist/feedback/feedback-table.js.map +1 -0
  88. package/dist/feedback/feedback-thread.d.ts +13 -9
  89. package/dist/feedback.d.ts +17 -9
  90. package/dist/feedback.js +11 -0
  91. package/dist/feedback.js.map +1 -1
  92. package/dist/hooks/use-file-drop.d.ts +13 -9
  93. package/dist/i18n/defaults.d.ts +13 -9
  94. package/dist/i18n/defaults.js +16 -0
  95. package/dist/i18n/defaults.js.map +1 -1
  96. package/dist/i18n/german.d.ts +13 -9
  97. package/dist/i18n/german.js +92 -5
  98. package/dist/i18n/german.js.map +1 -1
  99. package/dist/i18n/kit-labels.d.ts +7 -3
  100. package/dist/i18n/kit-labels.js.map +1 -1
  101. package/dist/i18n/languages.d.ts +13 -9
  102. package/dist/i18n/locales/de-CH.d.ts +13 -9
  103. package/dist/i18n/locales/en.d.ts +13 -9
  104. package/dist/i18n/locales/en.js +11 -1
  105. package/dist/i18n/locales/en.js.map +1 -1
  106. package/dist/i18n/locales/es.d.ts +13 -9
  107. package/dist/i18n/locales/es.js +90 -5
  108. package/dist/i18n/locales/es.js.map +1 -1
  109. package/dist/i18n/locales/fr.d.ts +13 -9
  110. package/dist/i18n/locales/fr.js +90 -5
  111. package/dist/i18n/locales/fr.js.map +1 -1
  112. package/dist/i18n/locales/hu.d.ts +13 -9
  113. package/dist/i18n/locales/hu.js +91 -5
  114. package/dist/i18n/locales/hu.js.map +1 -1
  115. package/dist/i18n/locales/it.d.ts +13 -9
  116. package/dist/i18n/locales/it.js +90 -5
  117. package/dist/i18n/locales/it.js.map +1 -1
  118. package/dist/i18n/locales/zh.d.ts +13 -9
  119. package/dist/i18n/locales/zh.js +90 -5
  120. package/dist/i18n/locales/zh.js.map +1 -1
  121. package/dist/i18n/review.d.ts +16 -11
  122. package/dist/i18n/review.js +12 -0
  123. package/dist/i18n/review.js.map +1 -1
  124. package/dist/i18n/swiss.js +3 -1
  125. package/dist/i18n/swiss.js.map +1 -1
  126. package/dist/index.d.ts +7 -3
  127. package/dist/index.js +18 -1
  128. package/dist/index.js.map +1 -1
  129. package/dist/{feedback-BXKH-ToU.d.ts → kit-labels-DQg82eHB.d.ts} +1275 -25
  130. package/dist/lib/translation-review.d.ts +76 -1
  131. package/dist/lib/translation-review.js +30 -0
  132. package/dist/lib/translation-review.js.map +1 -1
  133. package/dist/rhf/fields.d.ts +13 -9
  134. package/dist/rhf/form.d.ts +13 -9
  135. package/dist/rhf.d.ts +13 -9
  136. package/dist/shell/app-shell.d.ts +13 -9
  137. package/dist/shell/top-bar-brand.d.ts +13 -9
  138. package/dist/shell.d.ts +11 -11
  139. package/dist/wizard/stepper-nav.d.ts +13 -9
  140. package/dist/wizard.d.ts +13 -9
  141. package/package.json +6 -1
  142. package/src/components/data-table.tsx +14 -2
  143. package/src/components/swipeable-row.tsx +5 -1
  144. package/src/components/translation-review-labels.ts +5 -0
  145. package/src/components/translation-review.tsx +102 -35
  146. package/src/feedback/feedback-attachment.tsx +19 -3
  147. package/src/feedback/feedback-capture.ts +285 -0
  148. package/src/feedback/feedback-context.tsx +217 -0
  149. package/src/feedback/feedback-crash.ts +547 -0
  150. package/src/feedback/feedback-dialog.tsx +29 -6
  151. package/src/feedback/feedback-inbox.tsx +131 -27
  152. package/src/feedback/feedback-labels.ts +127 -0
  153. package/src/feedback/feedback-menu.tsx +132 -0
  154. package/src/feedback/feedback-record.ts +345 -0
  155. package/src/feedback/feedback-row-detail.tsx +825 -0
  156. package/src/feedback/feedback-status-undo.ts +111 -0
  157. package/src/feedback/feedback-submit.tsx +224 -0
  158. package/src/feedback/feedback-swipe.tsx +170 -0
  159. package/src/feedback/feedback-table.tsx +757 -0
  160. package/src/feedback.ts +13 -0
  161. package/src/i18n/defaults.ts +16 -0
  162. package/src/i18n/german.ts +99 -5
  163. package/src/i18n/kit-labels.tsx +20 -0
  164. package/src/i18n/locales/en.ts +16 -1
  165. package/src/i18n/locales/es.ts +97 -5
  166. package/src/i18n/locales/fr.ts +96 -5
  167. package/src/i18n/locales/hu.ts +95 -5
  168. package/src/i18n/locales/it.ts +98 -5
  169. package/src/i18n/locales/zh.ts +91 -5
  170. package/src/i18n/review.ts +15 -2
  171. package/src/i18n/swiss.ts +5 -1
  172. package/src/index.ts +19 -0
  173. package/src/lib/translation-review.ts +109 -0
  174. package/tokens.css +8 -0
@@ -1,12 +1,12 @@
1
1
  import * as react from 'react';
2
2
  import { InputHTMLAttributes, ReactNode, AnchorHTMLAttributes, Ref, ReactElement, ButtonHTMLAttributes, SelectHTMLAttributes, ComponentPropsWithoutRef, TextareaHTMLAttributes, RefObject } from 'react';
3
- import { TooltipSide } from './components/tooltip.js';
4
- import { f as DataTableLabels } from './data-table-labels-Cdn1QS2k.js';
3
+ import { d as DataTableColumn, f as DataTableLabels } from './data-table-labels-DUELeXpa.js';
5
4
  import { MiniCalendarLabels, WeekDay } from './components/mini-calendar.js';
6
5
  import { CalendarHeatmapLabels } from './components/calendar-heatmap.js';
7
6
  import { PopoverLabels } from './components/popover.js';
8
7
  import { ChipInputLabels } from './components/chip.js';
9
8
  import { FieldSyncLabels } from './components/field-sync.js';
9
+ import { TooltipSide } from './components/tooltip.js';
10
10
  import { WizardLabels } from './wizard/types.js';
11
11
  import { TourLabels } from './tour/tour.js';
12
12
  import { CommandPaletteLabels } from './search/command-palette.js';
@@ -24,8 +24,11 @@ import { IconPickerLabels } from './components/icon-picker.js';
24
24
  import { DialogFrameProps, DialogFrameLabels } from './components/dialog-frame.js';
25
25
  import { MeasuredGridLabels } from './components/measured-grid.js';
26
26
  import { LucideIcon } from 'lucide-react';
27
+ import { UserAvatarBadge } from './components/user-avatar.js';
28
+ import { TopBarMenuEntry } from './shell/topbar-action-menu.js';
29
+ import { FloatingCorner, FloatingPanelLabels } from './components/floating-panel.js';
30
+ import { AuthedFetcher } from './hooks/use-authed-src.js';
27
31
  import { AccountSettingsLabels } from './components/account-settings-labels.js';
28
- import { FloatingPanelLabels } from './components/floating-panel.js';
29
32
  import { BulkActionBarLabels } from './components/bulk-action-bar.js';
30
33
  import { ListLabels } from './components/list.js';
31
34
  import { BreadcrumbsLabels } from './components/breadcrumbs.js';
@@ -1948,7 +1951,8 @@ interface FeedbackDialogLabels extends FeedbackAttachmentLabels {
1948
1951
  subject: string;
1949
1952
  body: string;
1950
1953
  attachment: string;
1951
- submitHint: string;
1954
+ /** See {@link FeedbackDialogTextLabels.submitHint}. */
1955
+ submitHint: FeedbackSubmitHint;
1952
1956
  cancel: string;
1953
1957
  save: string;
1954
1958
  /** @deprecated since 0.16.0 — use `attachmentList`, the one key for the heading and
@@ -1956,6 +1960,8 @@ interface FeedbackDialogLabels extends FeedbackAttachmentLabels {
1956
1960
  * `attachmentList` (but not over an `attachmentList` passed beside it). */
1957
1961
  attachments?: string;
1958
1962
  }
1963
+ /** The submit hint: a string, or the line for the platform (`apple`: Mac, iPhone, iPad). */
1964
+ type FeedbackSubmitHint = string | ((apple: boolean) => string);
1959
1965
  /**
1960
1966
  * The dialog's own strings — the `feedbackDialog` namespace of `<UiKitProvider
1961
1967
  * labels>` (0.12.0). The attachment keys are not repeated here: they are the
@@ -1974,8 +1980,13 @@ interface FeedbackDialogTextLabels {
1974
1980
  bodyOptional?: string;
1975
1981
  /** The heading over the attachment buttons. */
1976
1982
  attachment: string;
1977
- /** The line beside the buttons naming the shortcut. */
1978
- submitHint: string;
1983
+ /**
1984
+ * The line beside the buttons naming the shortcut. Since 0.27.0 a function of the
1985
+ * platform, like `form.submitShortcut`: `apple` is true on a Mac, iPhone or iPad, where
1986
+ * the key is Cmd — "⌘ Enter to send" there, keksdose's "Ctrl+Enter to send" elsewhere
1987
+ * (§7.12). A plain string is still taken and shown everywhere, as before 0.27.0.
1988
+ */
1989
+ submitHint: FeedbackSubmitHint;
1979
1990
  cancel: string;
1980
1991
  save: string;
1981
1992
  /** @deprecated since 0.16.0 — use `feedbackAttachment.attachmentList`. It and this
@@ -1984,6 +1995,16 @@ interface FeedbackDialogTextLabels {
1984
1995
  * honoured when set: it wins over `feedbackAttachment.attachmentList`. */
1985
1996
  attachments?: string;
1986
1997
  }
1998
+ /**
1999
+ * English. The feedback contract's wording (docs/feedback-harmonization.md §4.2, 0.27.0):
2000
+ * "Send", not keksdose's "Save", and the body asks "What happened?" (Marcel's two calls);
2001
+ * the rest is keksdose's. Since 0.27.0 `attachment`, the single-mode heading, is keksdose's
2002
+ * `feedback.attachment` **"Attachment"** (it said "Screenshot" over a button that says "Add
2003
+ * attachment" and a picker that may offer a PDF), and `submitHint` is keksdose's
2004
+ * **"Ctrl+Enter to send"** — "⌘ Enter to send" on Apple platforms, where the old
2005
+ * "Ctrl/⌘ + Enter" made every reader parse both. The `attachments="multiple"` heading is
2006
+ * `feedbackAttachment.attachmentList` ("Attachments") and did not change.
2007
+ */
1987
2008
  declare const DEFAULT_FEEDBACK_DIALOG_LABELS: FeedbackDialogTextLabels;
1988
2009
  interface FeedbackSubmission {
1989
2010
  title: string;
@@ -2142,6 +2163,20 @@ interface FeedbackAttachmentFieldLabels {
2142
2163
  * the keys above. */
2143
2164
  attachmentUploadFailed?: (name: string) => string;
2144
2165
  }
2166
+ /**
2167
+ * English, in the feedback contract's wording since 0.27.0 (docs/feedback-harmonization.md
2168
+ * §4.2: keksdose's `feedback.*` keys are the canon, and the kit owns them now). Three
2169
+ * strings moved with it:
2170
+ *
2171
+ * - `attachmentAdd` "Attach image" → **"Add attachment"**: the field takes a PDF or a text
2172
+ * log as well wherever the app passes `FEEDBACK_ATTACHMENT_ACCEPT` (keksdose feedback
2173
+ * #87), and a button that says "image" over a picker that offers a PDF is wrong in one
2174
+ * of the two places.
2175
+ * - `attachmentPaste` → keksdose's longer line, which says WHY one would paste — a region
2176
+ * snip shows one corner rather than the whole page (Steering Design feedback #39).
2177
+ * - `attachmentLimit` for one: "remove **it**", as keksdose's `attachments_full_one`;
2178
+ * "remove one" of a single file read oddly.
2179
+ */
2145
2180
  declare const DEFAULT_FEEDBACK_ATTACHMENT_LABELS: FeedbackAttachmentFieldLabels;
2146
2181
  /** Why a file was turned away. `"count"` only ever comes from `multiple` (and `refs`)
2147
2182
  * mode: more files arrived at once than `max` left room for, and the surplus was
@@ -2421,13 +2456,20 @@ declare function pastedName(type: string): string;
2421
2456
  * Design's was thinner in every one of those places and read as a different
2422
2457
  * product for no reason anyone chose.
2423
2458
  *
2424
- * **What is deliberately NOT here.** The two apps store a report differently and
2425
- * are meant to: one keeps the reporter's context in a JSON column and the
2426
- * screenshot in an object store, the other keeps a `page_path` and the bytes in
2427
- * the row. So there is no `Feedback` type in this file and nothing here takes
2428
- * one. Every component takes the values it draws, and the detail panel is a
2429
- * *shell* the app fills — which is what lets each keep the shape that suits it
2430
- * without either of them inventing a second look for a status pill.
2459
+ * **The report itself lives next door (0.27.0).** This file used to say there
2460
+ * was deliberately no `Feedback` type here: the apps stored a report differently
2461
+ * (a context JSON column and an object store in one, a `page_path` and the bytes
2462
+ * in the row in another). Marcel's feedback round (2026-10-04,
2463
+ * docs/feedback-harmonization.md §5) gave all three apps one contract, so the
2464
+ * shape is now shared — `FeedbackRecord` / `FeedbackContext` in
2465
+ * feedback-record.ts, with the constants and body helpers. The components HERE
2466
+ * still take the values they draw rather than a row, which keeps them usable
2467
+ * from any surface; the detail panel stays a *shell* the row detail fills.
2468
+ *
2469
+ * **The words are the kit's too (0.27.0).** The status and category names are
2470
+ * the `feedbackStatus` / `feedbackCategory` namespaces (feedback-labels.ts,
2471
+ * keksdose's wording), read by default by the badges and the transitions below;
2472
+ * a `label` the caller passes still wins.
2431
2473
  */
2432
2474
  /** The seven states a report can be in.
2433
2475
  *
@@ -2519,20 +2561,26 @@ declare function feedbackCategoryRank(category: FeedbackCategory): number;
2519
2561
  * Falls back to OTHER's treatment for a category this build has never heard of,
2520
2562
  * so an older client against a newer API degrades to a readable neutral pill
2521
2563
  * instead of throwing on `meta.icon` — a crash report must not be able to cause
2522
- * one. The *label* is the caller's, and for the same reason it needs the same
2523
- * guard: pass what OTHER is called if you cannot name the value you were given.
2564
+ * one. The *label* gets the same guard: by default the `feedbackCategory`
2565
+ * namespace's word (0.27.0), and for a value it has no word for, what OTHER is
2566
+ * called.
2524
2567
  */
2525
2568
  declare function FeedbackCategoryBadge({ category, label, compact, className, }: {
2526
2569
  category: FeedbackCategory;
2527
- label: ReactNode;
2570
+ /** Over the `feedbackCategory` label (0.27.0: optional — before, every app passed
2571
+ * its own translation). */
2572
+ label?: ReactNode;
2528
2573
  compact?: boolean;
2529
2574
  className?: string;
2530
2575
  }): react.JSX.Element;
2531
2576
  /** Where a report stands, as a badge — for the places that only *report* the
2532
- * status rather than offering to change it. */
2577
+ * status rather than offering to change it. Named by the `feedbackStatus`
2578
+ * namespace unless `label` says otherwise (0.27.0); a status this build has never
2579
+ * heard of shows OPEN's look and its own raw value, never OPEN's name. */
2533
2580
  declare function FeedbackStatusBadge({ status, label, className, }: {
2534
2581
  status: FeedbackStatus;
2535
- label: ReactNode;
2582
+ /** Over the `feedbackStatus` label (0.27.0: optional). */
2583
+ label?: ReactNode;
2536
2584
  className?: string;
2537
2585
  }): react.JSX.Element;
2538
2586
  /**
@@ -2550,14 +2598,29 @@ declare function FeedbackStatusBadge({ status, label, className, }: {
2550
2598
  * that policy readable at the two call sites instead of hiding it behind
2551
2599
  * `variant`.
2552
2600
  */
2553
- declare function FeedbackStatusTransitions({ status, statuses, canEdit, onPick, variant, label, className, }: {
2601
+ declare function FeedbackStatusTransitions({ status, statuses, canEdit, onPick, variant, label, commit, disabledReason: ownDisabledReason, className, }: {
2554
2602
  status: FeedbackStatus;
2555
2603
  statuses: FeedbackStatus[];
2556
2604
  canEdit: boolean;
2557
2605
  onPick: (status: FeedbackStatus) => void;
2558
2606
  variant: "icon" | "pill";
2559
- /** What each status is called, translated by the app. */
2560
- label: (status: FeedbackStatus) => string;
2607
+ /** What each status is called. Default (0.27.0): the `feedbackStatus` namespace —
2608
+ * before, every app passed its own translation. */
2609
+ label?: (status: FeedbackStatus) => string;
2610
+ /**
2611
+ * Every button here SAVES the moment it is pressed (0.27.0) — opt in and, under a
2612
+ * locked {@link WriteLockProvider}, the row stays as it is and says why: each status
2613
+ * the row could move to is `aria-disabled` but focusable, the click does nothing, and
2614
+ * the lock's reason is in the kit Tooltip and the button's description — the
2615
+ * `disabledReason` path `Button` takes. Off by default: a feedback inbox is not what
2616
+ * every app's lock is about (keksdose's shell lock is a read-only demo BUDGET, and a
2617
+ * report is not budget data).
2618
+ */
2619
+ commit?: boolean;
2620
+ /** Why the statuses cannot be changed right now, the same way (a lock's own reason
2621
+ * wins over it). Only read while `canEdit`: a read-only row is a picture of the
2622
+ * workflow, not a refused action. */
2623
+ disabledReason?: ReactNode;
2561
2624
  className?: string;
2562
2625
  }): react.JSX.Element;
2563
2626
  /**
@@ -2588,7 +2651,7 @@ declare function FeedbackStatusTransitions({ status, statuses, canEdit, onPick,
2588
2651
  * the editor re-seeds when THAT changes. Either way the decision is the caller's,
2589
2652
  * which is the point: the editor cannot tell a new subject from a new render.
2590
2653
  */
2591
- declare function FeedbackNoteEditor({ initial, pending, onSave, onCancel, saveLabel, cancelLabel, placeholder, rows, attachment, resetKey, }: {
2654
+ declare function FeedbackNoteEditor({ initial, pending, onSave, onCancel, saveLabel, cancelLabel, placeholder, rows, attachment, resetKey, required, commit, }: {
2592
2655
  initial: string;
2593
2656
  pending: boolean;
2594
2657
  onSave: (value: string, attachment?: File | null) => void;
@@ -2610,13 +2673,29 @@ declare function FeedbackNoteEditor({ initial, pending, onSave, onCancel, saveLa
2610
2673
  * that cannot put a `key` on the editor (see the note above). Leave it out and
2611
2674
  * the draft is never thrown away behind the user's back. */
2612
2675
  resetKey?: string | number;
2676
+ /**
2677
+ * A blank note cannot be saved (0.27.0): Save stays disabled — and Ctrl/⌘+Enter does
2678
+ * nothing — until the trimmed text is non-empty, and the box is `aria-required`. For
2679
+ * the rework note, which the feedback contract requires (§3.4: a file alone cannot
2680
+ * be sent; keksdose `feedback-page.tsx:577` dropped the click silently instead, so a
2681
+ * press on Send with an empty box looked like a lost send). Default `false`: an
2682
+ * outcome or a description may be cleared on purpose.
2683
+ */
2684
+ required?: boolean;
2685
+ /** Save COMMITS (0.27.0): under a locked `WriteLockProvider` it is disabled the
2686
+ * `disabledReason` way, with the lock's reason, and Ctrl/⌘+Enter saves nothing. The
2687
+ * text box stays editable — nothing in it reaches the server until the save. */
2688
+ commit?: boolean;
2613
2689
  }): react.JSX.Element;
2614
2690
  /** What {@link FeedbackNoteEditor} needs in order to offer a picture with the
2615
2691
  * note: the same things {@link FeedbackAttachmentField} takes, so the reply path is
2616
2692
  * held to the app's own limits — and drawn in its own look — rather than the
2617
2693
  * defaults. */
2618
2694
  interface FeedbackNoteAttachment {
2619
- labels: FeedbackAttachmentLabels;
2695
+ /** Over the field's own `feedbackAttachment` namespace. Optional since 0.27.0 — the
2696
+ * field has read the provider's words since 0.7.0, so an app with a catalogue has
2697
+ * nothing to restate. */
2698
+ labels?: Partial<FeedbackAttachmentLabels>;
2620
2699
  accept?: string[];
2621
2700
  maxBytes?: number;
2622
2701
  /** `info` (0.23.0) names the refused file and the limit. */
@@ -2654,8 +2733,9 @@ declare function FeedbackDetailSection({ title, action, children, }: {
2654
2733
  }): react.JSX.Element;
2655
2734
  /** The stack a detail panel is. Here so the spacing between sections is decided
2656
2735
  * once rather than by whichever app was written second. */
2657
- declare function FeedbackDetail({ children }: {
2736
+ declare function FeedbackDetail({ children, className }: {
2658
2737
  children: ReactNode;
2738
+ className?: string;
2659
2739
  }): react.JSX.Element;
2660
2740
  /** Prose inside a section — a body, an outcome, a note.
2661
2741
  *
@@ -3025,6 +3105,1165 @@ type ChatComposerLabels = FeedbackComposerLabels;
3025
3105
  /** {@link FeedbackComposerSlotContext}, for {@link ChatComposer}'s `attachmentSlot`. */
3026
3106
  type ChatComposerSlotContext = FeedbackComposerSlotContext;
3027
3107
 
3108
+ /**
3109
+ * The words of a report's status and category, and the toasts a feedback page shows —
3110
+ * three `<UiKitProvider labels>` namespaces (0.27.0).
3111
+ *
3112
+ * Until 0.27 every app kept its own map from the enum to a translation key (keksdose
3113
+ * `STATUS_LABEL` and `categoryLabelKey`, "the package has no translations"), and the
3114
+ * three German maps had drifted apart: Kurvenschmiede said "In Arbeit", "In Prüfung",
3115
+ * "Test auf der Live-Umgebung" where keksdose and kastlan said "In Bearbeitung",
3116
+ * "Zur Prüfung", "Live testen". Marcel's feedback round (2026-10-04,
3117
+ * docs/feedback-harmonization.md §2.4) made keksdose's wording the canon and the kit its
3118
+ * owner, in all seven languages. The English defaults below are keksdose's
3119
+ * `frontend/src/shared/i18n/locales/en.json` (`feedback.*`); the de-CH canon for the
3120
+ * catalogues is noted on each key (keksdose `de-CH.json` — ss, never ß).
3121
+ *
3122
+ * `FeedbackStatusBadge`, `FeedbackCategoryBadge` and `FeedbackStatusTransitions` read
3123
+ * the first two by default, so an app drops its own maps; a `label` passed to them still
3124
+ * wins.
3125
+ */
3126
+ /**
3127
+ * `feedbackStatus` — one word per status, keyed by the enum value so
3128
+ * `labels[row.status]` is the whole lookup.
3129
+ *
3130
+ * de-CH canon: OPEN "Offen" · IN_PROGRESS "In Bearbeitung" · IN_EVALUATION "Zur Prüfung"
3131
+ * · NEEDS_LIVE_TEST "Live testen" · POSTPONED "Zurückgestellt" · DONE "Erledigt" ·
3132
+ * WONT_DO "Wird nicht umgesetzt".
3133
+ */
3134
+ type FeedbackStatusLabels = Record<FeedbackStatus, string>;
3135
+ declare const DEFAULT_FEEDBACK_STATUS_LABELS: FeedbackStatusLabels;
3136
+ /**
3137
+ * `feedbackCategory` — one word per category, keyed by the enum value. `CRASH` is never
3138
+ * pickable, but it is shown: on the badge, in the filter, in a row's detail.
3139
+ *
3140
+ * de-CH canon: CRASH "Absturz" · BUG "Fehler" · IDEA "Idee" · QUESTION "Frage" ·
3141
+ * OTHER "Sonstiges".
3142
+ */
3143
+ type FeedbackCategoryLabels = Record<FeedbackCategory, string>;
3144
+ declare const DEFAULT_FEEDBACK_CATEGORY_LABELS: FeedbackCategoryLabels;
3145
+ /**
3146
+ * `feedbackToast` — what the submit dialog, a failed save and a status change say in a
3147
+ * toast (§4.2, §4.4, §4.5 of the contract). A server's `detail` sentence, when it sent
3148
+ * one, wins over `submitFailed` and `updateFailed` (§3.7); that choice is the caller's.
3149
+ */
3150
+ interface FeedbackToastLabels {
3151
+ /** A report was filed. de-CH: "Danke für Ihr Feedback!" */
3152
+ submitted: string;
3153
+ /** `POST /feedback` failed with no `detail` of its own.
3154
+ * de-CH: "Feedback konnte nicht gesendet werden" */
3155
+ submitFailed: string;
3156
+ /** A picked file is not an image, a PDF or a text file (§3.5).
3157
+ * de-CH: "Nur Bilder, PDF- oder Textdateien sind erlaubt" */
3158
+ attachmentUnsupported: string;
3159
+ /** A picked file is over the 10 MB the contract allows (§3.5).
3160
+ * de-CH: "Datei ist grösser als 10 MB" */
3161
+ attachmentTooLarge: string;
3162
+ /** More files were picked than fit; `count` is how many fit.
3163
+ * de-CH: "Es passen nur {{count}} Anhänge – die übrigen wurden weggelassen." */
3164
+ attachmentTooMany: (count: number) => string;
3165
+ /** The screenshot capture failed (or its optional peer is not installed).
3166
+ * de-CH: "Screenshot konnte nicht aufgenommen werden" */
3167
+ captureFailed: string;
3168
+ /** A `PATCH /feedback/{id}` failed with no `detail` of its own (§4.4).
3169
+ * de-CH: "Änderung konnte nicht gespeichert werden." */
3170
+ updateFailed: string;
3171
+ /** A status change landed — the 8 s Undo toast (§4.5). `status` is the NEW status's
3172
+ * label, `title` the report's title. de-CH: "Auf „{{status}}“ gesetzt: {{title}}" */
3173
+ statusChanged: (status: string, title: string) => string;
3174
+ /** That toast's action. de-CH: "Rückgängig" */
3175
+ statusUndo: string;
3176
+ /** The Undo landed — `status` is the RESTORED status's label.
3177
+ * de-CH: "Zurück auf „{{status}}“: {{title}}" */
3178
+ statusRestored: (status: string, title: string) => string;
3179
+ }
3180
+ declare const DEFAULT_FEEDBACK_TOAST_LABELS: FeedbackToastLabels;
3181
+ /** `feedbackStatus` resolved: English, then `<UiKitProvider labels>`, then `labels`. For
3182
+ * the places that need a status's name outside the kit's own parts — a filter's options,
3183
+ * `feedbackSwipePlan`'s panels, an app's overview tile. */
3184
+ declare function useFeedbackStatusLabels(labels?: Partial<FeedbackStatusLabels>): FeedbackStatusLabels;
3185
+ /** `feedbackCategory` resolved: English, then the provider, then `labels`. */
3186
+ declare function useFeedbackCategoryLabels(labels?: Partial<FeedbackCategoryLabels>): FeedbackCategoryLabels;
3187
+ /** `feedbackToast` resolved: English, then the provider, then `labels`. */
3188
+ declare function useFeedbackToastLabels(labels?: Partial<FeedbackToastLabels>): FeedbackToastLabels;
3189
+
3190
+ /**
3191
+ * `feedbackMenu` — the top-bar feedback menu's own words (0.27.0). The four category rows
3192
+ * are `feedbackCategory`'s, shared with the badges and the dialog's picker. de-CH canon
3193
+ * (keksdose `de-CH.json`): `trigger` "Feedback senden", `myFeedback` "Mein Feedback",
3194
+ * `viewFeedback` "Feedback ansehen".
3195
+ */
3196
+ interface FeedbackMenuLabels {
3197
+ /** The trigger's accessible name, and the menu's. keksdose `feedback.send`. */
3198
+ trigger: string;
3199
+ /** The link to the reporter's own reports — everyone but an admin. keksdose
3200
+ * `feedback.my_title`. */
3201
+ myFeedback: string;
3202
+ /** The link to the inbox — an admin's one list link, in place of "My feedback".
3203
+ * keksdose `feedback.view_all`. */
3204
+ viewFeedback: string;
3205
+ }
3206
+ declare const DEFAULT_FEEDBACK_MENU_LABELS: FeedbackMenuLabels;
3207
+ interface FeedbackMenuProps {
3208
+ /** A category row was chosen — open the submit dialog on it (`useFeedbackSubmit`'s
3209
+ * `open`). Never called with `CRASH`: there is no row for it. */
3210
+ onFile: (category: FeedbackCategory) => void;
3211
+ /** The list link is "View feedback" → {@link inboxHref} INSTEAD of "My feedback" (§4.1,
3212
+ * keksdose `top-bar.tsx:205-214`). The admin check is the app's (kastlan's is
3213
+ * company-scoped). Default `false`. */
3214
+ isAdmin?: boolean;
3215
+ /**
3216
+ * Rows of the app's own, between the divider and the list link — keksdose's
3217
+ * `Sparkles` Help assistant → `/assistant` and `MessagesSquare` Support chat →
3218
+ * `/support` with its unread chip. Any {@link TopBarMenuEntry}; keep their `key`s clear
3219
+ * of the menu's own (`BUG`, `IDEA`, `QUESTION`, `OTHER`, `feedback-divider`,
3220
+ * `my-feedback`, `view-feedback`).
3221
+ */
3222
+ extraEntries?: TopBarMenuEntry[];
3223
+ /** The user's own reports, linked for everyone but an admin. Default `"/my-feedback"`
3224
+ * (§2.3). */
3225
+ myFeedbackHref?: string;
3226
+ /** The admin inbox, linked for an admin. Default `"/feedback"` (§2.3). */
3227
+ inboxHref?: string;
3228
+ /** A dot on the trigger — keksdose's unread support replies (`tone: "danger"`). Its
3229
+ * `label` joins the trigger's name ("Send feedback 2 unread"). The words are the app's. */
3230
+ iconBadge?: UserAvatarBadge | null;
3231
+ /** Prop > `<UiKitProvider labels={{ feedbackMenu }}>` > English. */
3232
+ labels?: Partial<FeedbackMenuLabels>;
3233
+ /** Which edge of the trigger the panel lines up with. Default `end`. */
3234
+ align?: "start" | "end" | "left" | "right";
3235
+ }
3236
+ /**
3237
+ * The top-bar feedback menu, the same in every app (0.27.0, docs/feedback-harmonization.md
3238
+ * §4.1) — keksdose `frontend/src/app/top-bar.tsx:156` on the kit's `TopBarActionMenu`:
3239
+ *
3240
+ * 1. a `MessageSquare` trigger named "Send feedback", with an optional `iconBadge`;
3241
+ * 2. Bug · Idea · Question · Other (`Bug`, `Lightbulb`, `HelpCircle`, `MoreHorizontal` —
3242
+ * the category badges' own glyphs), each calling `onFile(category)`;
3243
+ * 3. a divider, always;
3244
+ * 4. the app's `extraEntries`;
3245
+ * 5. ONE list link, `Inbox`: an admin's is "View feedback" → `/feedback`, everyone
3246
+ * else's "My feedback" → `/my-feedback` — keksdose's (`top-bar.tsx:205-214`, feedback
3247
+ * #331: admins get the management view, everyone else their own submissions).
3248
+ *
3249
+ * WHY A PRESET. Three apps had drawn three menus on the same primitive and they disagreed:
3250
+ * kastlan headed it "New submission", Kurvenschmiede "Send feedback" with a single link
3251
+ * that changed its words by role, keksdose had no heading and one link by role. keksdose
3252
+ * is the source (§2.4, §7.11): no heading, one link — the inbox for an admin, who reaches
3253
+ * their own reports there too — and the kit draws it, so the row order, the icons and the
3254
+ * words cannot drift again. Where the menu sits in the bar stays each app's.
3255
+ *
3256
+ * `CRASH` has no row and never will: crash reports are filed by the error boundary with
3257
+ * the stack and a fingerprint (keksdose feedback #160), and a hand-filed "crash" would sort
3258
+ * to the top like one with none of what makes it actionable. The server refuses it with a
3259
+ * 422 anyway (§3.4).
3260
+ *
3261
+ * The menu forwards nothing to its trigger, as `TopBarActionMenu` does not: keksdose's
3262
+ * tour anchor (`data-tour="feedback-menu"`) stays on a wrapping `<span>`. Needs a router,
3263
+ * as every `TopBarActionMenu` with link rows does.
3264
+ */
3265
+ declare function FeedbackMenu({ onFile, isAdmin, extraEntries, myFeedbackHref, inboxHref, iconBadge, labels: labelsProp, align, }: FeedbackMenuProps): react.JSX.Element;
3266
+
3267
+ /**
3268
+ * One feedback report as the three apps send it, and the facts the kit reads off it.
3269
+ *
3270
+ * Until 0.27 the inbox parts took no report at all ("no `Feedback` type in this file",
3271
+ * feedback-inbox.tsx): keksdose kept its reporter context in a JSON column, kastlan a
3272
+ * thinner copy of it, Kurvenschmiede a `page_path` and the bytes in the row. Marcel's
3273
+ * feedback round (2026-10-04, docs/feedback-harmonization.md) settled one API contract
3274
+ * for all three — keksdose's, read off its code — so the apps now share one shape and the
3275
+ * kit can take a row instead of a dozen loose values. This file is that shape (§3.1,
3276
+ * §3.2), the contract's constants (§3.4, §3.5, §4.3) and the readers and writers of the
3277
+ * one field whose format is itself contract: the body, which carries the rework rounds
3278
+ * (§3.4) — lifted from keksdose `frontend/src/features/feedback/body-attachments.ts`.
3279
+ *
3280
+ * Plain functions and data, no components: everything here is as usable from a loop
3281
+ * script's helper or a test as from a page.
3282
+ */
3283
+ /** Which copy of an app a report came from — keksdose `app/deploy-environment.ts`.
3284
+ * Any other string is read as "not prod" (the admin list shows it upper-cased as a
3285
+ * chip, §4.3), so an app with a `staging` loses nothing. */
3286
+ type FeedbackEnvironment = "prod" | "dev" | "local";
3287
+ /**
3288
+ * The reporter's context, written by the submit dialog (keksdose
3289
+ * `use-feedback-dialog.tsx:105`) — §3.2 of the contract.
3290
+ *
3291
+ * **Every key is optional on read**, and a reader treats a missing one as "unknown":
3292
+ * kastlan's client sends six of them (no email, no version), Kurvenschmiede's old rows are
3293
+ * backfilled from two columns with a path-only `url`, crash rows add their own (and
3294
+ * `origin` / `environment` from the crash payload, §3.6 — older crash rows have neither),
3295
+ * and an erased account's row keeps only an allow-list (§3.2: `environment` stays,
3296
+ * `origin` goes). Extra keys are allowed — a server may add its own — hence the index
3297
+ * signature.
3298
+ */
3299
+ interface FeedbackContext {
3300
+ /** The full `location.href` at open time; `""` when "Attach current page URL" was off.
3301
+ * Readers also accept a path-only value (Kurvenschmiede's backfilled rows). */
3302
+ url?: string;
3303
+ /** Its pathname; `""` when the URL was withheld. */
3304
+ route?: string;
3305
+ /** `location.origin` — which copy of the app, not personal; never withheld. */
3306
+ origin?: string;
3307
+ /** {@link FeedbackEnvironment}, or whatever the app calls its other copies. */
3308
+ environment?: FeedbackEnvironment | (string & {});
3309
+ /** The submitter, as a snapshot. The server should overwrite the three `user_*` keys
3310
+ * from the session (Kurvenschmiede does), so nobody files in someone else's name. */
3311
+ user_id?: number | null;
3312
+ user_email?: string | null;
3313
+ user_display_name?: string | null;
3314
+ /** `"406x816"`. */
3315
+ viewport?: string;
3316
+ /** `navigator.userAgent`. */
3317
+ ua?: string;
3318
+ /** The app build (`__APP_VERSION__`). */
3319
+ version?: string;
3320
+ fingerprint?: string;
3321
+ /** Which error boundary caught it. */
3322
+ boundary?: "app" | "page" | (string & {});
3323
+ online?: boolean;
3324
+ occurrences?: number;
3325
+ first_seen_at?: string;
3326
+ last_seen_at?: string;
3327
+ auto_reported?: boolean;
3328
+ [key: string]: unknown;
3329
+ }
3330
+ /**
3331
+ * One report — `FeedbackResponse`, keksdose `schemas/feedback.py:117`, §3.1 of the
3332
+ * contract. What `GET /feedback`, `GET /feedback/my`, `POST /feedback` and
3333
+ * `PATCH /feedback/{id}` answer in all three apps.
3334
+ *
3335
+ * Extra fields are allowed and ignored (kastlan's `company_id`, `user_name`): an app's
3336
+ * own row type with more fields is assignable to this one as it stands.
3337
+ *
3338
+ * There is **no rework counter field** — {@link reworkCount} reads it off the body, so
3339
+ * nothing can forget to set it.
3340
+ */
3341
+ interface FeedbackRecord {
3342
+ id: number;
3343
+ /** `null`: the author's account was erased (keksdose live #275). */
3344
+ user_id: number | null;
3345
+ /** The admin list only (joined); `null` — or absent — on `/my`, create and update. */
3346
+ user_email?: string | null;
3347
+ /** 1–255 characters, never blank. */
3348
+ title: string;
3349
+ /** `""` when none. Rework rounds are appended here (§3.4) — read it with
3350
+ * {@link splitDescription} / {@link splitBodyAttachments}, write it with
3351
+ * {@link appendRework} / {@link replaceDescription}. */
3352
+ body: string;
3353
+ category: FeedbackCategory;
3354
+ status: FeedbackStatus;
3355
+ /** §3.2; `null` on rows that have none. */
3356
+ context: FeedbackContext | null;
3357
+ /** The ONE captured screenshot's attachment URL, or `null`. */
3358
+ screenshot_url: string | null;
3359
+ /** Up to 5 other files; `null` = none. Optional because a server older than the
3360
+ * column sends no key at all (keksdose dev #578 met exactly that). */
3361
+ attachment_urls?: string[] | null;
3362
+ /** The team's answer; `null` = none written. */
3363
+ outcome: string | null;
3364
+ /** Set on ENTERING `DONE` / `WONT_DO`, cleared on leaving them. ISO 8601. */
3365
+ resolved_at: string | null;
3366
+ /** ISO 8601. */
3367
+ created_at: string;
3368
+ /** ISO 8601. */
3369
+ updated_at: string;
3370
+ }
3371
+ /**
3372
+ * What a report may carry (§3.5, keksdose `upload_guards.py:197` and its
3373
+ * `attachment-options.ts`): the kit's four image types plus a PDF and a plain-text log
3374
+ * (keksdose feedback #87). SVG and HTML stay out — both are script-carrying documents in
3375
+ * an image's clothing. For `FeedbackAttachmentField` / `FeedbackDialog`'s `accept`, with
3376
+ * `DEFAULT_MAX_ATTACHMENTS` (5) and `DEFAULT_MAX_ATTACHMENT_BYTES` (10 MB) from
3377
+ * feedback-attachment.tsx, which already are the contract's limits.
3378
+ *
3379
+ * A plain array (not frozen) because `accept` takes `string[]`, as
3380
+ * `DEFAULT_ATTACHMENT_ACCEPT` does — copy it before changing it.
3381
+ */
3382
+ declare const FEEDBACK_ATTACHMENT_ACCEPT: string[];
3383
+ /**
3384
+ * The categories a person can pick — every one but `CRASH`, in triage order (Bug, Idea,
3385
+ * Question, Other: the feedback menu's rows, §4.1). `CRASH` is filed by an error boundary
3386
+ * and the server refuses it by hand with a 422 (keksdose `_reject_manual_crash`), so no
3387
+ * picker may offer it.
3388
+ */
3389
+ declare const FEEDBACK_PICKABLE_CATEGORIES: readonly FeedbackCategory[];
3390
+ /**
3391
+ * The statuses a report can be sent back for **rework** from (§3.4): everything the team
3392
+ * has answered — `IN_EVALUATION`, `NEEDS_LIVE_TEST`, `POSTPONED`, `DONE`, `WONT_DO`. On
3393
+ * these the server reopens the row when the body grows by a rework block; on `OPEN` /
3394
+ * `IN_PROGRESS` there is nothing to send back yet.
3395
+ */
3396
+ declare const FEEDBACK_REWORKABLE_STATUSES: readonly FeedbackStatus[];
3397
+ /**
3398
+ * The statuses in which the AUTHOR may still edit title, body and category (§3.4,
3399
+ * keksdose `canAuthorEdit`, `feedback-page.tsx:398`): `OPEN` and `IN_PROGRESS`. Once the
3400
+ * team has answered, the description is what they answered and stays as it was; a
3401
+ * correction goes in as a rework.
3402
+ */
3403
+ declare const FEEDBACK_AUTHOR_EDITABLE_STATUSES: readonly FeedbackStatus[];
3404
+ /**
3405
+ * The two statuses that wait on the person triaging: answered and handed back for a
3406
+ * verdict, or answered and only checkable on a deployed build (keksdose
3407
+ * `AWAITING_VERDICT`, live #330). The phone's "Only what is waiting for you" toggle
3408
+ * narrows the inbox to exactly these (§4.3).
3409
+ */
3410
+ declare const FEEDBACK_AWAITING_STATUSES: readonly FeedbackStatus[];
3411
+ /** The body line for one uploaded attachment — what {@link appendRework} writes under
3412
+ * the note. */
3413
+ declare function feedbackAttachmentLine(url: string): string;
3414
+ /**
3415
+ * A rework round appended to a body — the exact text `PATCH /feedback/{id}` takes as the
3416
+ * new `body` (§3.4; keksdose `submitRework`, `feedback-page.tsx:350`):
3417
+ *
3418
+ * ```text
3419
+ * <old body>\n\n--- REWORK 2026-10-04 09:12 ---\n<note>\n[screenshot] /api/v1/feedback/attachments/<key>
3420
+ * ```
3421
+ *
3422
+ * - The stamp is UTC, `YYYY-MM-DD HH:MM` (`toISOString().slice(0, 16)`), from `now`.
3423
+ * - The old body is kept byte for byte, so the result STRICTLY EXTENDS it — which is
3424
+ * how the server recognises an append and reopens an answered row (keksdose
3425
+ * `_is_rework_append`, live #331). `\n\n` is left out when the old body is `""`.
3426
+ * - ONE file per round (`url`), uploaded first by the caller: a body appended without
3427
+ * it would be a rework whose evidence never arrived.
3428
+ *
3429
+ * **Returns `null` when the trimmed note is empty** — a file alone cannot be sent
3430
+ * (keksdose `:577`). Null rather than a throw because an empty note is something a
3431
+ * person does, not a bug, and the type then makes every caller decide what an empty
3432
+ * box means before anything can reach the PATCH.
3433
+ *
3434
+ * **Throws a `TypeError` when `url` is not an attachment URL** of the contract's shape
3435
+ * (`/api/v1/feedback/attachments/<key>`): that one IS a bug — the upload handed back
3436
+ * something foreign — and written anyway it would sit in the body as prose that no
3437
+ * reader recognises as a file.
3438
+ */
3439
+ declare function appendRework(body: string, note: string, url?: string | null, now?: Date): string | null;
3440
+ /**
3441
+ * How many times a report has been sent back — the count of `--- REWORK … ---` rule
3442
+ * lines in its body (keksdose live #331: the "category for items in rework", derived
3443
+ * rather than stored, so nothing can forget to set it or leave it set). Folded kastlan
3444
+ * comments do not count.
3445
+ */
3446
+ declare function reworkCount(body: string): number;
3447
+ /**
3448
+ * A body split into the prose to render and the files to show beside it, in body order.
3449
+ *
3450
+ * The file lines are stripped from the text: they are a location, not something anybody
3451
+ * wrote, and left in they would put a raw URL in the middle of a sentence. Trailing blank
3452
+ * lines go (what a stripped marker leaves at the end of a note); every blank line between
3453
+ * paragraphs stays.
3454
+ */
3455
+ declare function splitBodyAttachments(body: string): {
3456
+ text: string;
3457
+ urls: string[];
3458
+ };
3459
+ /**
3460
+ * The ORIGINAL description, apart from everything appended to it since (§4.4.1).
3461
+ *
3462
+ * - `description` — what the author wrote when filing, without its file lines and
3463
+ * trailing blank lines: the text the "Edit description" editor holds.
3464
+ * - `appended` — every `--- REWORK … ---` round and folded `--- COMMENT … ---` block,
3465
+ * from the first one's rule line to the end, verbatim (`""` when there are none).
3466
+ *
3467
+ * keksdose's editor edits the raw body (`initial={fb.body}`, `feedback-page.tsx:420`), so
3468
+ * an author tidying a typo could delete the rework history and the file lines with it.
3469
+ * The kit's editor holds `description` only and saves through
3470
+ * {@link replaceDescription}, which puts the rest back unchanged.
3471
+ */
3472
+ declare function splitDescription(body: string): {
3473
+ description: string;
3474
+ appended: string;
3475
+ };
3476
+ /**
3477
+ * The body with its original description replaced and everything else kept — the file
3478
+ * lines of the original part (after the new text, in their order) and the appended
3479
+ * blocks (verbatim, after a blank line). The new text is trimmed; clearing it saves the
3480
+ * rest alone (`""` when there is no rest).
3481
+ *
3482
+ * `replaceDescription(body, splitDescription(body).description)` gives back a canonical
3483
+ * body unchanged — a trimmed description, then each block after one blank line, as the
3484
+ * dialog and {@link appendRework} write them.
3485
+ */
3486
+ declare function replaceDescription(body: string, description: string): string;
3487
+ /**
3488
+ * Whether an attachment URL is a picture (keksdose dev #578). The upload route takes
3489
+ * PDFs and text logs too and serves them as downloads, never inline, so an `<img>` on
3490
+ * one shows a broken icon. The key's extension is the server's own statement of the
3491
+ * type (minted from the sniffed content), so it is the thing to read.
3492
+ */
3493
+ declare function isImageAttachment(url: string): boolean;
3494
+ /** The file name an attachment downloads as — its key, the last segment of the URL. */
3495
+ declare function attachmentName(url: string): string;
3496
+
3497
+ /**
3498
+ * The reporter context of a feedback report: the box the submit dialog shows, and the
3499
+ * `context` object `POST /feedback` sends (0.27.0, docs/feedback-harmonization.md §3.2,
3500
+ * §4.2). One file for both, so what the box SAYS will be sent and what IS sent cannot
3501
+ * drift apart.
3502
+ *
3503
+ * Each app had written both halves itself (keksdose `use-feedback-dialog.tsx:105` and
3504
+ * `:222`, kastlan `feedback-button.tsx:80`, Kurvenschmiede `use-feedback-dialog.tsx:124`),
3505
+ * and they had drifted: kastlan sent six keys with `user_email: null` and no version,
3506
+ * Kurvenschmiede a `page_path` column and a sentence where keksdose had a labelled box,
3507
+ * and only keksdose showed the full URL it was about to send.
3508
+ */
3509
+ /**
3510
+ * The person filing, as the app knows them. Snake case on purpose: keksdose's and
3511
+ * Kurvenschmiede's user objects already have this shape (`id`, `email`, `display_name`),
3512
+ * so they pass their `user` as it is; kastlan maps its token's `sub` and name parts.
3513
+ *
3514
+ * A SNAPSHOT for the report and the box. The server should overwrite the three `user_*`
3515
+ * keys from the session (§3.2; Kurvenschmiede does), so nobody can file in someone else's
3516
+ * name — this is what the reporter is SHOWN, not proof of who they are.
3517
+ */
3518
+ interface FeedbackSubmitter {
3519
+ id?: number | null;
3520
+ email?: string | null;
3521
+ display_name?: string | null;
3522
+ }
3523
+ /** What {@link feedbackContext} is built from. */
3524
+ interface FeedbackContextInput {
3525
+ /** The submitter, or `null` when signed out / unknown — the three `user_*` keys are then
3526
+ * `null`, as keksdose sends them. */
3527
+ user?: FeedbackSubmitter | null;
3528
+ /** The box's "Attach current page URL" checkbox. `false` withholds `url` and `route`
3529
+ * (both `""`); nothing else is withheld — `origin` says which copy of the app, which is
3530
+ * not personal, and triage needs it first (keksdose `use-feedback-dialog.tsx:110`). */
3531
+ attachUrl: boolean;
3532
+ /** `location.href` when the dialog OPENED — the page the report is about, not the one
3533
+ * the reporter may have navigated to while typing. */
3534
+ url: string;
3535
+ /** Its pathname. Default: read off `url` (a path-only `url` works too). */
3536
+ route?: string;
3537
+ /** Default `location.origin`. */
3538
+ origin?: string;
3539
+ /** `"prod"` / `"dev"` / `"local"` — the app's own reading (keksdose
3540
+ * `app/deploy-environment.ts`). Left out of the object when not given. */
3541
+ environment?: FeedbackEnvironment | (string & {});
3542
+ /** The app build (`__APP_VERSION__`). Left out of the object when not given. */
3543
+ version?: string;
3544
+ /** Default `${innerWidth}x${innerHeight}` at the time of the call — "406x816". */
3545
+ viewport?: string;
3546
+ /** Default `navigator.userAgent`. */
3547
+ ua?: string;
3548
+ }
3549
+ /**
3550
+ * The `context` object of `POST /feedback` (§3.2), keys in keksdose's order: `url`, `route`,
3551
+ * `origin`, `environment`, `user_id`, `user_email`, `user_display_name`, `viewport`, `ua`,
3552
+ * `version`.
3553
+ *
3554
+ * With `attachUrl: false`, `url` and `route` are `""` — present and empty, as keksdose
3555
+ * sends them, so a reader can tell "withheld" from "an old row without the key".
3556
+ * `environment` and `version` are omitted when the app passes none (kastlan had no
3557
+ * version), rather than sent as `undefined`.
3558
+ *
3559
+ * Reads `window` / `navigator` only for the defaults it was not given, so it is a pure
3560
+ * function in a test that passes everything.
3561
+ */
3562
+ declare function feedbackContext(input: FeedbackContextInput): FeedbackContext;
3563
+ /**
3564
+ * `feedbackContext` — the context box's words. de-CH canon (keksdose `de-CH.json`):
3565
+ * `user` "Nutzer", `attachUrl` "Aktuelle Seiten-URL anhängen".
3566
+ */
3567
+ interface FeedbackContextLabels {
3568
+ /** The submitter line's label — the box draws "**User:** Ada Example (ada@example.com)".
3569
+ * The label alone: the colon between it and the person is the language's
3570
+ * `common.fieldValue` (fr "Utilisateur : …", zh "用户:…"). */
3571
+ user: string;
3572
+ /** The checkbox. The URL it is about is the line beneath it. */
3573
+ attachUrl: string;
3574
+ }
3575
+ declare const DEFAULT_FEEDBACK_CONTEXT_LABELS: FeedbackContextLabels;
3576
+ interface FeedbackContextBoxProps {
3577
+ /** Who is filing — the same value handed to {@link feedbackContext}. */
3578
+ user?: FeedbackSubmitter | null;
3579
+ /** The URL that goes with the report — the same `url` handed to {@link feedbackContext}. */
3580
+ url: string;
3581
+ /** The checkbox, on by default in the dialog (§4.2) — the same `attachUrl`. */
3582
+ attachUrl: boolean;
3583
+ onAttachUrlChange: (next: boolean) => void;
3584
+ /** Prop > `<UiKitProvider labels={{ feedbackContext }}>` > English. */
3585
+ labels?: Partial<FeedbackContextLabels>;
3586
+ className?: string;
3587
+ }
3588
+ /**
3589
+ * The submit dialog's context box — the dialog's `contextSlot` (keksdose
3590
+ * `use-feedback-dialog.tsx:222`, §4.2): who is filing, and the page URL with a checkbox to
3591
+ * leave it out, ON by default.
3592
+ *
3593
+ * The URL stays visible when the box is off, struck through: the reporter sees what they
3594
+ * withheld rather than a line that vanished, and ticking it back on is the same glance.
3595
+ * Kurvenschmiede's argument for the checkbox is why it exists at all (its feedback #101):
3596
+ * the route is right for a bug and wrong for an idea about a screen one is not on — filed
3597
+ * against whatever screen happened to be open, the queue reads as if it were about that
3598
+ * screen.
3599
+ *
3600
+ * Nothing in here is the app's: hand it the values handed to {@link feedbackContext}, and
3601
+ * what the box shows is what is sent.
3602
+ */
3603
+ declare function FeedbackContextBox({ user, url, attachUrl, onAttachUrlChange, labels: labelsProp, className, }: FeedbackContextBoxProps): react.JSX.Element;
3604
+
3605
+ /**
3606
+ * A status change that can be taken back — §4.5 of the feedback contract, lifted from
3607
+ * keksdose's `changeStatusUndoably` (`frontend/src/features/feedback/feedback-page.tsx`,
3608
+ * dev #587: *"Just clicked somewhere and a feedback row disappeared without knowing what
3609
+ * I set as new status."*).
3610
+ *
3611
+ * An inbox is usually filtered by status, so a row whose new status falls outside the
3612
+ * filter leaves the table the moment the change lands, with nothing on screen saying what
3613
+ * it became. The toast says it, names the report, and offers the old status back for 8 s
3614
+ * (the window keksdose's transactions Undo uses, feedback #184). Undo PATCHes the old
3615
+ * status and confirms that too. kastlan and Kurvenschmiede had no Undo at all.
3616
+ */
3617
+ /** The one PATCH a status change is: `PATCH /feedback/{id}` with `{ status }`. */
3618
+ interface FeedbackStatusPatch {
3619
+ id: number;
3620
+ status: FeedbackStatus;
3621
+ }
3622
+ /**
3623
+ * The page's status mutation. **TanStack Query's `mutate` fits as it stands** — pass
3624
+ * `updateMutation.mutate` (stable across renders), not `mutateAsync`. A hand-written
3625
+ * one sends the PATCH and calls `onSuccess` once it has landed, and only then.
3626
+ *
3627
+ * Failure is the mutation's own business: its `onError` toasts the server's `detail`,
3628
+ * else `feedbackToast.updateFailed` (§4.4) — this hook adds no second error toast.
3629
+ */
3630
+ type FeedbackStatusMutate = (patch: FeedbackStatusPatch, callbacks: {
3631
+ onSuccess: () => void;
3632
+ }) => void;
3633
+ /** What the change needs to know about the row: which one, what it is called in the
3634
+ * toast, and where it stood (the status Undo goes back to). */
3635
+ type FeedbackStatusRow = Pick<FeedbackRecord, "id" | "title" | "status">;
3636
+ /** Set `row` to `status`, with the Undo toast. A no-op when it is already there. */
3637
+ type FeedbackStatusChange = (row: FeedbackStatusRow, status: FeedbackStatus) => void;
3638
+ interface FeedbackStatusUndoOptions {
3639
+ /** Over the `feedbackToast` namespace (the toast's words). */
3640
+ labels?: Partial<FeedbackToastLabels>;
3641
+ /** Over the `feedbackStatus` namespace (the status names inside them). */
3642
+ statusLabels?: Partial<FeedbackStatusLabels>;
3643
+ }
3644
+ /** How long the Undo stays reachable: 8 s, keksdose's window (feedback #184). */
3645
+ declare const FEEDBACK_UNDO_DURATION = 8000;
3646
+ /**
3647
+ * The undoable status change — for the table's status cell, the row detail's pills and
3648
+ * the phone swipes alike (§4.5, §7.10: swipes included, Marcel 2026-10-04 — keksdose's
3649
+ * swipe commits go through its `changeStatusUndoably` too). Wire every one of them
3650
+ * through the function this returns:
3651
+ *
3652
+ * ```tsx
3653
+ * const update = useMutation({ mutationFn: api.updateFeedback, onError: … });
3654
+ * const changeStatus = useFeedbackStatusUndo(update.mutate);
3655
+ * <FeedbackStatusTransitions … onPick={(status) => changeStatus(row, status)} />
3656
+ * ```
3657
+ *
3658
+ * Give it the PAGE's mutation, not one a row owns: TanStack only calls per-call
3659
+ * callbacks while the observer that started the mutation is mounted, and a row the new
3660
+ * status filters out unmounts before the PATCH answers — the toast would never come.
3661
+ *
3662
+ * The flow: PATCH `{ id, status }` → on success a success toast
3663
+ * `statusChanged(newName, title)` with the action `statusUndo`, 8 s → Undo PATCHes
3664
+ * `{ id, status: previous }` → on success `statusRestored(previousName, title)`.
3665
+ */
3666
+ declare function useFeedbackStatusUndo(mutate: FeedbackStatusMutate, { labels, statusLabels }?: FeedbackStatusUndoOptions): FeedbackStatusChange;
3667
+
3668
+ /**
3669
+ * The two feedback pages' TABLE — `/feedback` (the admin inbox) and `/my-feedback` (the
3670
+ * user's own) — as the kit's parts: §4.3 of the feedback contract
3671
+ * (docs/feedback-harmonization.md), lifted from keksdose
3672
+ * `frontend/src/features/feedback/feedback-page.tsx` (columns :711–963, the phone card
3673
+ * :1038–1065, the awaiting toggle :689–709 and :1086–1101).
3674
+ *
3675
+ * Until 0.27 each app wrote this table itself, and the three had drifted exactly where a
3676
+ * table drifts: kastlan's showed five statuses and no environment, Kurvenschmiede's one
3677
+ * page served both audiences, and keksdose's — the canon, argued over a year of feedback
3678
+ * items — had a latent bug of its own: its subject cell carries the environment and
3679
+ * rework chips "so a second attempt is marked on every surface at once" (:760), but the
3680
+ * phone card is a `mobileCard`, and `DataTable` never renders the `mobilePrimary` cell
3681
+ * when `mobileCard` is set (data-table.tsx, `cardInner`). Its phone cards showed neither
3682
+ * chip. {@link FeedbackMobileCard} draws both itself, from the same
3683
+ * {@link FeedbackSubject} the column uses, so the two surfaces cannot part again.
3684
+ *
3685
+ * What stays the app's: the rows (its API client), who is an admin, the routes and the
3686
+ * `/feedback` → `/my-feedback` redirect, the `?row=` deep link and the row detail. What
3687
+ * is here: the nine columns ({@link feedbackColumns} / {@link useFeedbackColumns}), the
3688
+ * phone card and its day headers ({@link FeedbackMobileCard},
3689
+ * {@link feedbackMobileGroupBy}), the phone's "Only what is waiting for you" toggle
3690
+ * ({@link FeedbackAwaitingToggle}), the empty state ({@link FeedbackEmptyState}) and the
3691
+ * `feedbackPage` words. The swipes are `feedbackSwipePlan` (feedback-swipe.tsx), passed
3692
+ * by the app because whether the page may write is the app's call.
3693
+ */
3694
+ /**
3695
+ * `feedbackPage` — the words of the two feedback pages (§4.3). The English is keksdose's
3696
+ * `en.json` (`feedback.*`, `common.none`); the de-CH canon is noted on each key (keksdose
3697
+ * `de-CH.json`, ss never ß).
3698
+ */
3699
+ interface FeedbackPageLabels {
3700
+ /** The admin inbox's page title, `/feedback`. de-CH: "Feedback" */
3701
+ title: string;
3702
+ /** The user's own list, `/my-feedback`. de-CH: "Mein Feedback" */
3703
+ myTitle: string;
3704
+ /** Column 1, the id. de-CH: "#" */
3705
+ columnId: string;
3706
+ /** Column 2, when it was filed. de-CH: "Datum" */
3707
+ columnDate: string;
3708
+ /** Column 3. de-CH: "Kategorie" */
3709
+ columnCategory: string;
3710
+ /** Column 4, the title with its chips. de-CH: "Betreff" */
3711
+ columnSubject: string;
3712
+ /** Column 5, who filed it. de-CH: "Nutzer" */
3713
+ columnUser: string;
3714
+ /** Column 6. de-CH: "E-Mail" */
3715
+ columnEmail: string;
3716
+ /** Column 7, the page it was filed from. de-CH: "URL" */
3717
+ columnUrl: string;
3718
+ /** Column 8. de-CH: "Status" */
3719
+ columnStatus: string;
3720
+ /** Column 9, when it was settled. de-CH: "Erledigt am" */
3721
+ columnResolved: string;
3722
+ /** What the URL cell's link does, said before the path to a screen reader.
3723
+ * de-CH: "Seite öffnen" */
3724
+ openPage: string;
3725
+ /** The table with nothing to show, on both pages (§7.13) — keksdose's `common.none`.
3726
+ * de-CH: "Keine" */
3727
+ empty: string;
3728
+ /** The submitter of a report whose account was erased (`user_id: null`, keksdose live
3729
+ * #275). de-CH: "<gelöschter Nutzer>" */
3730
+ deletedUser: string;
3731
+ /** The submitter when neither a name nor an email is known — `id` is `user_id`.
3732
+ * de-CH: "Nutzer #{{id}}" */
3733
+ userFallback: (id: number) => string;
3734
+ /** The chip for a report filed from a copy of the app that is not production —
3735
+ * `environment` is `context.environment` as filed ("dev", "local", or the app's own
3736
+ * word). Upper-cased like the top bar's environment badge, so the marker the tester saw
3737
+ * is the marker triage sees. Guarded: a context is untyped JSON, so a value that is no
3738
+ * string (an app calling it with `context.environment` as stored) is printed, never
3739
+ * thrown on. de-CH: the same. */
3740
+ environment: (environment: string) => string;
3741
+ /** The chip for a report sent back for rework — `count` ≥ 1 is how many times
3742
+ * (`reworkCount`, keksdose live #331). de-CH: "Nacharbeit" / "Nacharbeit ×{{count}}" */
3743
+ reworkChip: (count: number) => string;
3744
+ /** The phone toggle narrowing the inbox to IN_EVALUATION + NEEDS_LIVE_TEST.
3745
+ * de-CH: "Nur was auf Sie wartet" */
3746
+ awaitingFilter: string;
3747
+ /** The name of the phone's floating group that holds it. de-CH: "Feedback-Aktionen" */
3748
+ phoneActions: string;
3749
+ }
3750
+ declare const DEFAULT_FEEDBACK_PAGE_LABELS: FeedbackPageLabels;
3751
+ /** `feedbackPage` resolved: English, then `<UiKitProvider labels>`, then `labels`. The
3752
+ * page's title comes from here too: `mine ? labels.myTitle : labels.title`. */
3753
+ declare function useFeedbackPageLabels(labels?: Partial<FeedbackPageLabels>): FeedbackPageLabels;
3754
+ /** What the table reads off a report — {@link FeedbackRecord}'s fields, so an app's own
3755
+ * row type with more of them passes as it is. */
3756
+ type FeedbackTableRow = Pick<FeedbackRecord, "id" | "user_id" | "user_email" | "title" | "body" | "category" | "status" | "context" | "resolved_at" | "created_at">;
3757
+ /**
3758
+ * The environment a report was filed from when it is NOT production, as filed — or
3759
+ * `null` (keksdose `environmentLabel`, :142). Production is silent, as the top bar's
3760
+ * badge is: the marker earns attention by being rare. A missing value is production too —
3761
+ * every row filed before a test copy existed, and crash rows filed before the crash
3762
+ * payload carried `environment` (§3.2, §3.6). An unknown value is shown rather than swallowed: an environment
3763
+ * nobody recognises is exactly when triage needs to see one.
3764
+ */
3765
+ declare function feedbackEnvironment(context: FeedbackContext | null | undefined): string | null;
3766
+ /**
3767
+ * Who filed a report, as one string (keksdose `submitterLabel`, :163): the display name,
3768
+ * else the email, else `userFallback(user_id)`. An erased account (`user_id: null`) is
3769
+ * `deletedUser` FIRST, ahead of the context: the server scrubs the name with the account,
3770
+ * but a row that somehow kept one must still not go on naming somebody who asked to be
3771
+ * forgotten. The joined `user_email` (admin list) is preferred to the context snapshot.
3772
+ */
3773
+ declare function feedbackSubmitter(row: Pick<FeedbackRecord, "user_id" | "user_email" | "context">, labels: Pick<FeedbackPageLabels, "deletedUser" | "userFallback">): string;
3774
+ /**
3775
+ * The submitter's email (keksdose `submitterEmail`, :175): the joined `user_email` —
3776
+ * authoritative, the display name is self-chosen (feedback #108) — else the context
3777
+ * snapshot, which is what `/my-feedback` has (`GET /feedback/my` sends no join). `""`
3778
+ * for an erased account, for the reason {@link feedbackSubmitter} gives.
3779
+ */
3780
+ declare function feedbackSubmitterEmail(row: Pick<FeedbackRecord, "user_id" | "user_email" | "context">): string;
3781
+ /** Whether a status is one the phone's awaiting toggle keeps — `FEEDBACK_AWAITING_STATUSES`,
3782
+ * the two that wait on the person triaging. `rows.filter((r) => isFeedbackAwaiting(r.status))`. */
3783
+ declare function isFeedbackAwaiting(status: FeedbackStatus): boolean;
3784
+ /**
3785
+ * How the pages write a date: an ISO timestamp in, one string out. keksdose passes its
3786
+ * `useDateCell()` (the app's date-format preference, weekday and all — dev#546), which
3787
+ * fits as it is.
3788
+ *
3789
+ * ONE function for the date column, the resolved column, the phone card and the phone's
3790
+ * day headers, so the four cannot disagree (keksdose dev#546 round 3: the header is a
3791
+ * string and must say what the card says).
3792
+ */
3793
+ type FeedbackRenderDate = (iso: string) => string;
3794
+ /**
3795
+ * `renderDate` when the app gave one, else the kit's: the `<UiKitProvider formatDate>`
3796
+ * when the app set one (called with the LOCAL calendar day and `weekday: true`, as
3797
+ * `DateMark` calls it), else `Intl` in the provider's locale with the weekday. An
3798
+ * unparseable value is passed through rather than hidden.
3799
+ */
3800
+ declare function useFeedbackRenderDate(renderDate?: FeedbackRenderDate, locale?: string): FeedbackRenderDate;
3801
+ interface FeedbackSubjectProps {
3802
+ row: Pick<FeedbackRecord, "title" | "body" | "context">;
3803
+ /**
3804
+ * `false` (default): one line — the title truncates and the chips stay whole, as a
3805
+ * table cell wants. `true`: the title wraps and the chips flow after its last word, as
3806
+ * the phone card wants (a phone shows the whole title or nothing useful).
3807
+ */
3808
+ wrap?: boolean;
3809
+ /** Over the `feedbackPage` namespace. */
3810
+ labels?: Partial<FeedbackPageLabels>;
3811
+ className?: string;
3812
+ }
3813
+ /**
3814
+ * A report's title with its two chips (§4.3, column 4): the environment when it is not
3815
+ * production (keksdose's amber outline, the top bar's environment badge — the marker the
3816
+ * tester saw is the marker triage sees) and **Rework** / **Rework ×n** when the author has
3817
+ * sent it back (keksdose live #331: *"have a new category for items in rework"*, as a fact
3818
+ * derived from the body rather than a seventh status).
3819
+ *
3820
+ * No tooltip on either chip (keksdose dev#523): each already says what it means.
3821
+ */
3822
+ declare function FeedbackSubject({ row, wrap, labels: labelsProp, className }: FeedbackSubjectProps): react.JSX.Element;
3823
+ /** What {@link feedbackColumns} takes — every word already resolved (see
3824
+ * {@link useFeedbackColumns}, which resolves them from the provider). */
3825
+ interface FeedbackColumnsOptions {
3826
+ /** `/my-feedback`. The same nine columns (one page component, §4.3), but the status
3827
+ * cell is read-only there whatever `canEdit` says — "no status or outcome editing
3828
+ * there (also for an admin)". */
3829
+ mine?: boolean;
3830
+ /** The admin on `/feedback` — the status cell offers its steps. Otherwise it shows them
3831
+ * read-only. */
3832
+ canEdit: boolean;
3833
+ /** The undoable status change, `useFeedbackStatusUndo(update.mutate)` (§4.5) — the
3834
+ * same one the row detail and the swipes use. */
3835
+ onStatus: FeedbackStatusChange;
3836
+ /** See {@link FeedbackRenderDate}. */
3837
+ renderDate: FeedbackRenderDate;
3838
+ /** The status buttons SAVE when pressed: pass `true` to put them under the page's
3839
+ * `WriteLockProvider` (FeedbackStatusTransitions' `commit`). Off by default — a
3840
+ * feedback inbox is rarely what an app's lock is about. */
3841
+ commit?: boolean;
3842
+ labels: FeedbackPageLabels;
3843
+ statusLabels: FeedbackStatusLabels;
3844
+ categoryLabels: FeedbackCategoryLabels;
3845
+ }
3846
+ /**
3847
+ * The nine columns of §4.3, in the contract's order, for `DataTable` — keksdose's
3848
+ * (:711–963) with the contract's changes:
3849
+ *
3850
+ * | # | key | cell | filter | phone |
3851
+ * |---|---|---|---|---|
3852
+ * | 1 | `id` | id, mono | number | hidden |
3853
+ * | 2 | `date` | `created_at`, weekday ({@link FeedbackRenderDate}) | date | |
3854
+ * | 3 | `category` | `FeedbackCategoryBadge`, sorted CRASH first | select, all five | |
3855
+ * | 4 | `title` | {@link FeedbackSubject} | text: title + environment | card heading |
3856
+ * | 5 | `user` | {@link feedbackSubmitter} | text: name + email, or "deleted user" | |
3857
+ * | 6 | `email` | {@link feedbackSubmitterEmail} | text | hidden |
3858
+ * | 7 | `url` | the page, a link (`noRowLink`) | text: path | hidden |
3859
+ * | 8 | `status` | `FeedbackStatusTransitions variant="icon"`, `visibleFeedbackStatuses` | select, chain order | |
3860
+ * | 9 | `resolved` | `resolved_at` | date | hidden |
3861
+ *
3862
+ * - Both select filters list the WHOLE vocabulary in its own order, translated —
3863
+ * without `options` the popover lists the bare enum values found in the rows
3864
+ * (keksdose feedback #160), and kastlan's first CRASH row would otherwise appear in a
3865
+ * filter that never offered it.
3866
+ * - The status column owns the status filter, and only it (keksdose live #330's revert:
3867
+ * the phone's filter sheet already exposes every column filter).
3868
+ * - `/my-feedback` shows the same nine: the Email cell's context fallback exists for it
3869
+ * (the admin list's join is absent there, §3.1), and its phone card drops the submitter
3870
+ * instead ({@link FeedbackMobileCardProps.showSubmitter}).
3871
+ *
3872
+ * Pure: hand it resolved words, or use {@link useFeedbackColumns}. Keys are stable, so
3873
+ * an app may drop or add a column (`columns.filter((c) => c.key !== "email")`).
3874
+ */
3875
+ declare function feedbackColumns<T extends FeedbackTableRow>({ mine, canEdit, onStatus, renderDate, commit, labels, statusLabels, categoryLabels, }: FeedbackColumnsOptions): DataTableColumn<T>[];
3876
+ /** What {@link useFeedbackColumns} takes: {@link FeedbackColumnsOptions} with the words
3877
+ * read from the provider, and `renderDate` optional. */
3878
+ interface UseFeedbackColumnsOptions extends Omit<FeedbackColumnsOptions, "renderDate" | "labels" | "statusLabels" | "categoryLabels"> {
3879
+ /** Default: the kit's date with the weekday ({@link useFeedbackRenderDate}). */
3880
+ renderDate?: FeedbackRenderDate;
3881
+ /** Over the `feedbackPage` namespace. Keep it stable (module scope or memoised): it is
3882
+ * a dependency of the memo. */
3883
+ labels?: Partial<FeedbackPageLabels>;
3884
+ }
3885
+ /**
3886
+ * {@link feedbackColumns} with the `feedbackPage`, `feedbackStatus` and
3887
+ * `feedbackCategory` words from the provider, memoised on what it reads — pass
3888
+ * `update.mutate` through `useFeedbackStatusUndo` (stable) rather than a fresh function
3889
+ * per render, or every row's columns are rebuilt on each render (keksdose :667).
3890
+ */
3891
+ declare function useFeedbackColumns<T extends FeedbackTableRow>({ mine, canEdit, onStatus, renderDate: renderDateProp, commit, labels: labelsProp, }: UseFeedbackColumnsOptions): DataTableColumn<T>[];
3892
+ /**
3893
+ * The phone list's day headers (keksdose feedback #325: the list "grouped by date, like
3894
+ * the transactions list"): `DataTable mobileGroupBy={feedbackMobileGroupBy(renderDate)}`.
3895
+ *
3896
+ * The key IS the header — the same string the card's date says. DataTable groups runs of
3897
+ * equal keys, so the rows must come newest first, as `GET /feedback` and
3898
+ * `GET /feedback/my` send them (§3.3).
3899
+ */
3900
+ declare function feedbackMobileGroupBy(renderDate: FeedbackRenderDate): (row: Pick<FeedbackRecord, "created_at">) => string;
3901
+ interface FeedbackMobileCardProps {
3902
+ row: FeedbackTableRow;
3903
+ /** Add the submitter after the date — the admin inbox (`canEdit`). On `/my-feedback`
3904
+ * every row is the viewer's own and the name would repeat down the list. */
3905
+ showSubmitter?: boolean;
3906
+ /** See {@link FeedbackRenderDate}; the same function as the columns' and the day
3907
+ * headers'. Default: the kit's ({@link useFeedbackRenderDate}). */
3908
+ renderDate?: FeedbackRenderDate;
3909
+ /** Over the `feedbackPage` namespace. */
3910
+ labels?: Partial<FeedbackPageLabels>;
3911
+ }
3912
+ /**
3913
+ * One report as a phone card (§4.3; keksdose :1042, feedback #325 rework: *"compact card
3914
+ * instead of a wasteful label/value row per field"*): the title with its environment and
3915
+ * rework chips and the status badge on one line, then the compact category badge, the
3916
+ * date and (admin) the submitter beneath.
3917
+ *
3918
+ * `DataTable mobileCard={(row) => <FeedbackMobileCard row={row} … />}`. It draws the
3919
+ * chips itself: with a `mobileCard`, DataTable never renders the `mobilePrimary` cell,
3920
+ * which is how keksdose's cards lost them.
3921
+ */
3922
+ declare function FeedbackMobileCard({ row, showSubmitter, renderDate: renderDateProp, labels: labelsProp }: FeedbackMobileCardProps): react.JSX.Element;
3923
+ interface FeedbackAwaitingToggleProps {
3924
+ /** Narrowed to what waits on the triager. Page state, not the URL (keksdose :699): a
3925
+ * thumb gesture on one screen, not a view worth linking to. */
3926
+ pressed: boolean;
3927
+ onPressedChange: (pressed: boolean) => void;
3928
+ /** Further `FloatingAction`s, after the toggle — the group is what makes them free. */
3929
+ children?: ReactNode;
3930
+ /** Default `bottom-end`, the register's corner. */
3931
+ corner?: FloatingCorner;
3932
+ /** Over the group's default distance from the bottom. */
3933
+ offset?: string;
3934
+ /** On the group. It is `md:hidden` already: the desktop has the status column's
3935
+ * filter, the same question asked where there is room for it. */
3936
+ className?: string;
3937
+ /** Over the `feedbackPage` namespace. */
3938
+ labels?: Partial<FeedbackPageLabels>;
3939
+ }
3940
+ /**
3941
+ * The phone inbox's one filter button — **"Only what is waiting for you"** (§4.3;
3942
+ * keksdose live #330 rework: *"a button that toggles the evaluation items (in evaluation,
3943
+ * test when live) as toggle button similar to the show/hide upcoming/scheduled for the tx
3944
+ * page"*).
3945
+ *
3946
+ * It narrows TO `FEEDBACK_AWAITING_STATUSES` rather than hiding them: after a loop run
3947
+ * those ARE the queue — the rows handed back for a verdict — and everything else is
3948
+ * history. A `FloatingActionGroup` with one member anyway, in the register's corner and
3949
+ * shape (keksdose feedback #58), so a second action costs nothing; the glyph is
3950
+ * IN_EVALUATION's own, so the button reads in the vocabulary of the badges it filters on.
3951
+ *
3952
+ * Admin inbox only: on `/my-feedback` the author is not waiting on their own verdict.
3953
+ * The filtering is the page's — `rows.filter((r) => isFeedbackAwaiting(r.status))` —
3954
+ * so the table's own filters, sort and URL stay untouched.
3955
+ */
3956
+ declare function FeedbackAwaitingToggle({ pressed, onPressedChange, children, corner, offset, className, labels: labelsProp, }: FeedbackAwaitingToggleProps): react.JSX.Element;
3957
+ interface FeedbackEmptyStateProps {
3958
+ /** Over the `feedbackPage` namespace. */
3959
+ labels?: Partial<FeedbackPageLabels>;
3960
+ }
3961
+ /**
3962
+ * The table with nothing to show (§7.13): the one word **None** / **Keine**, on both
3963
+ * pages — keksdose's `empty={t("common.none")}` (`feedback-page.tsx:982`). No hint
3964
+ * beneath it: where reports are sent from is the feedback menu's business, and the inbox
3965
+ * needs no sentence to say it is empty.
3966
+ *
3967
+ * For `DataTable empty`, which sits in a centred, muted cell — so no box of its own.
3968
+ * keksdose passes it unconditionally, so a filter (or the awaiting toggle) that leaves
3969
+ * nothing says "None" too: `empty={<FeedbackEmptyState />}`.
3970
+ */
3971
+ declare function FeedbackEmptyState({ labels: labelsProp }: FeedbackEmptyStateProps): react.JSX.Element;
3972
+
3973
+ /**
3974
+ * One report, opened — the panel a feedback table unfolds under its row (§4.4 of
3975
+ * docs/feedback-harmonization.md), lifted from keksdose's `FeedbackRow`
3976
+ * (`frontend/src/features/feedback/feedback-page.tsx:329-612`) with the contract's
3977
+ * changes applied.
3978
+ *
3979
+ * Until 0.27 each app wrote this panel itself out of the kit's shell
3980
+ * (`FeedbackDetail` / `FeedbackDetailSection` / `FeedbackNoteEditor`), and the three
3981
+ * copies had drifted in exactly the places a user notices:
3982
+ *
3983
+ * - **the description editor held the raw body** — keksdose `initial={fb.body}`
3984
+ * (`:420`), kastlan the same (`pages/feedback-page.tsx:226`) — so an author fixing a
3985
+ * typo could delete the `--- REWORK … ---` rounds and the `[screenshot]` file lines
3986
+ * with it, and the history was gone. Here the editor holds the ORIGINAL description
3987
+ * only ({@link splitDescription}) and saves through {@link replaceDescription}, which
3988
+ * puts every appended block and file line back unchanged;
3989
+ * - **kastlan's rework sent `status: "OPEN"`** from the client (`:189-195`) and offered it
3990
+ * on three statuses; keksdose sends the body alone and lets the server reopen the row
3991
+ * on five (live #331). The contract is keksdose's (§3.4) — {@link appendRework}, one
3992
+ * file, the server reopens;
3993
+ * - **kastlan showed only `screenshot_url`** (`:265-276`) and no downloads; keksdose the
3994
+ * screenshot, the other files and the rework pictures, a PDF as a download (dev #578);
3995
+ * - **the URL fell away on Kurvenschmiede's rows**, whose backfilled `context.url` is a
3996
+ * path (`/accounts?p=2`): keksdose's `new URL(…)` throws on one and the section
3997
+ * vanished. kastlan already fell back to `context.route` (`feedbackRaw`, `:97`).
3998
+ *
3999
+ * The sections, in the contract's order: **Description** (the body; the author's
4000
+ * Edit while OPEN / IN_PROGRESS), **URL**, **Attachment**, **Outcome** (the admin's
4001
+ * outcome editor, Rework, Open page), **Send for rework** (only while reworking) and
4002
+ * **Status**. No comment thread (§2.2: kastlan's comments fold into the body, §7.7).
4003
+ *
4004
+ * The app keeps its data and its API: the row it renders, the PATCH (`onUpdate`), the
4005
+ * upload (`onUpload`), the authenticated GET (`fetcher`) and who is looking (`canEdit`,
4006
+ * `viewerId`). Every word is the kit's `feedbackDetail` namespace.
4007
+ */
4008
+ /**
4009
+ * `feedbackDetail` — the row detail's words (§4.4). English: keksdose's `en.json`
4010
+ * (`feedback.*`, `common.*`, `more.copy_url`) with the contract's changes; the de-CH
4011
+ * canon (keksdose `de-CH.json`, ss never ß, "Nacharbeit" throughout — §7.14) on each key.
4012
+ */
4013
+ interface FeedbackDetailLabels {
4014
+ /** Section 1's heading — keksdose's `feedback.body` (§7.3). The dialog's field asks
4015
+ * "What happened? (optional)"; the description, once filed, is headed as what it is
4016
+ * — and as the author's "Edit description" calls it. de-CH: "Beschreibung" */
4017
+ body: string;
4018
+ /** The author's button on that heading. de-CH: "Bearbeiten" */
4019
+ edit: string;
4020
+ /** Its accessible name — says WHAT is edited; contains the visible word.
4021
+ * de-CH: "Beschreibung bearbeiten" */
4022
+ editDescription: string;
4023
+ /** The editors' save button (description, outcome). de-CH: "Speichern" */
4024
+ save: string;
4025
+ /** Every editor's cancel button. de-CH: "Abbrechen" */
4026
+ cancel: string;
4027
+ /** Section 2's heading. de-CH: "URL" */
4028
+ url: string;
4029
+ /** The copy button beside the link — copies the FULL `context.url`.
4030
+ * de-CH: "URL kopieren" */
4031
+ copyUrl: string;
4032
+ /** Section 3's heading, and each picture's alt text. de-CH: "Anhang" */
4033
+ attachment: string;
4034
+ /** A PDF's / text file's download button, given the file's name.
4035
+ * de-CH: "{{name}} herunterladen" */
4036
+ download: (name: string) => string;
4037
+ /** The toast when that download failed. de-CH: "Der Anhang konnte nicht
4038
+ * heruntergeladen werden." */
4039
+ downloadFailed: string;
4040
+ /** Section 4's heading. de-CH: "Ergebnis" */
4041
+ outcome: string;
4042
+ /** On section 4's heading line once the row is settled, given the formatted
4043
+ * `resolved_at` date. de-CH: "Erledigt am: {{date}}" */
4044
+ resolvedAt: (date: string) => string;
4045
+ /** The admin's button while there is no outcome. de-CH: "Ergebnis hinzufügen" */
4046
+ outcomeAdd: string;
4047
+ /** The same button once there is one. de-CH: "Aktualisieren" */
4048
+ outcomeUpdate: string;
4049
+ /** The line above the outcome editor. de-CH: "Was wurde umgesetzt, entschieden oder
4050
+ * warum nicht." */
4051
+ outcomePlaceholder: string;
4052
+ /** The button that opens section 5 on an answered row. de-CH: "Nacharbeit" */
4053
+ rework: string;
4054
+ /** The link to the page the report was filed on. de-CH: "Seite öffnen" */
4055
+ openPage: string;
4056
+ /** Section 5's heading. de-CH: "Zur Nacharbeit senden" */
4057
+ reworkTitle: string;
4058
+ /** Section 5's send button. de-CH: "Nacharbeit senden" */
4059
+ reworkSend: string;
4060
+ /** The line above the rework note. de-CH: "Was muss noch angepasst werden? Neue
4061
+ * Anforderungen oder Richtungswechsel." */
4062
+ reworkPlaceholder: string;
4063
+ /** The toast when the rework's file did not upload — and so nothing was sent. Names
4064
+ * no screenshot: it fires for a PDF or a log too (§7.14; keksdose's said
4065
+ * "screenshot"). de-CH: "Die Datei konnte nicht hochgeladen werden. Die Nacharbeit
4066
+ * wurde nicht gesendet." */
4067
+ reworkUploadFailed: string;
4068
+ /** Section 6's heading. de-CH: "Status" */
4069
+ status: string;
4070
+ }
4071
+ declare const DEFAULT_FEEDBACK_DETAIL_LABELS: FeedbackDetailLabels;
4072
+ /** `feedbackDetail` resolved: English, then `<UiKitProvider labels>`, then `labels`. */
4073
+ declare function useFeedbackDetailLabels(labels?: Partial<FeedbackDetailLabels>): FeedbackDetailLabels;
4074
+ /**
4075
+ * The in-app link to the page a report was filed on — path + query + hash (keksdose
4076
+ * `localHref`: the query and hash carry the state worth restoring, `?tab=import`, a
4077
+ * drill-down). Read from `context.url`, else `context.route`; a path-only `url` works.
4078
+ * Always a path on THIS app, never another origin's address, so it is safe as an
4079
+ * `href` whatever the row says. `null` when there is nothing to link.
4080
+ */
4081
+ declare function feedbackPageHref(context: FeedbackContext | null | undefined): string | null;
4082
+ /** The page's path alone — the compact text a URL column shows (§4.3). */
4083
+ declare function feedbackPagePath(context: FeedbackContext | null | undefined): string | null;
4084
+ /**
4085
+ * What "Copy URL" copies: the FULL `context.url` as the reporter's browser had it —
4086
+ * origin included, which is the one part that says which copy of the app it was
4087
+ * (feedback #278) — else `context.route`. `null` when there is no page.
4088
+ */
4089
+ declare function feedbackPageUrl(context: FeedbackContext | null | undefined): string | null;
4090
+ /**
4091
+ * Every file a report carries, in the order the detail shows them (§4.4.3): the
4092
+ * screenshot it was filed with, then its other files, then whatever the rework rounds
4093
+ * brought, in body order (keksdose dev #578, live #291). One list rather than three —
4094
+ * a reader looking for "the picture" should not have to know at which point in the
4095
+ * conversation it was taken.
4096
+ *
4097
+ * Each URL once: keksdose and Kurvenschmiede content-address their keys, so the same
4098
+ * screenshot pasted twice is one URL — shown twice it is the same picture twice (and,
4099
+ * keyed by URL, a React key clash keksdose had).
4100
+ */
4101
+ declare function feedbackAttachmentUrls(row: Pick<FeedbackRecord, "screenshot_url" | "attachment_urls" | "body">): string[];
4102
+ /** What the person looking at a row may do with it — {@link feedbackRowAccess}. */
4103
+ interface FeedbackRowAccess {
4104
+ /** The viewer filed it (`viewerId === row.user_id`; never for an erased author). */
4105
+ isAuthor: boolean;
4106
+ /** "Edit description": the author, while the row is OPEN or IN_PROGRESS — on either
4107
+ * page (keksdose `canAuthorEdit`, `feedback-page.tsx:398`, is not gated on `mine`). */
4108
+ canEditDescription: boolean;
4109
+ /** The outcome editor: the admin on the inbox (`canEdit`). */
4110
+ canEditOutcome: boolean;
4111
+ /** "Rework": the admin or the author, on an answered row
4112
+ * ({@link FEEDBACK_REWORKABLE_STATUSES}) — §3.4, anyone else gets the server's 403. */
4113
+ canRework: boolean;
4114
+ /** The status pills: the admin on the inbox; everyone else sees them read-only. */
4115
+ canChangeStatus: boolean;
4116
+ }
4117
+ /**
4118
+ * The detail's permission rules in one place — the same answers the server gives
4119
+ * (§3.4), so the panel never offers what the PATCH would refuse.
4120
+ *
4121
+ * `canEdit` is the app's "admin on `/feedback`": an admin on `/my-feedback` edits no
4122
+ * status and no outcome there (§4.3), but as the author still edits the description
4123
+ * and sends for rework. `viewerId` is the signed-in user's id.
4124
+ */
4125
+ declare function feedbackRowAccess(row: Pick<FeedbackRecord, "user_id" | "status">, { canEdit, viewerId }: {
4126
+ canEdit: boolean;
4127
+ viewerId?: number | null;
4128
+ }): FeedbackRowAccess;
4129
+ /** What the detail sends as `PATCH /feedback/{id}` (§3.4): the body (a description
4130
+ * edit, a rework append) or the outcome — and, through {@link useFeedbackStatusUndo}
4131
+ * when no `statusChange` is given, the status. Never two at once: a rework PATCH must
4132
+ * carry the body ALONE for the server to read it as an append (keksdose live #331). */
4133
+ interface FeedbackDetailPatch {
4134
+ id: number;
4135
+ body?: string;
4136
+ /** `null` clears it — a real edit. */
4137
+ outcome?: string | null;
4138
+ status?: FeedbackStatus;
4139
+ }
4140
+ /**
4141
+ * The page's update mutation. **TanStack Query's `mutate` fits as it stands** — pass
4142
+ * `update.mutate` (and `pending={update.isPending}`), the same function
4143
+ * `useFeedbackStatusUndo` takes. A hand-written one sends the PATCH and calls
4144
+ * `onSuccess` once it has landed, and only then: that is when an editor closes. A
4145
+ * failure is the mutation's own to toast — the server's `detail`, else
4146
+ * `feedbackToast.updateFailed` (§4.4) — and leaves the editor open with its draft.
4147
+ */
4148
+ type FeedbackDetailUpdate = (patch: FeedbackDetailPatch, callbacks: {
4149
+ onSuccess: () => void;
4150
+ }) => void;
4151
+ /** `POST /feedback/attachments` (§3.3): one file up, its URL
4152
+ * (`/api/v1/feedback/attachments/<key>`) back. Reject on failure. */
4153
+ type FeedbackUpload = (file: File) => Promise<string>;
4154
+ interface FeedbackReworkSectionProps {
4155
+ /** The row being sent back: its id, and the body the round is appended to. */
4156
+ row: Pick<FeedbackRecord, "id" | "body">;
4157
+ /** The PATCH — see {@link FeedbackDetailUpdate}. Called with `{ id, body }` only. */
4158
+ onUpdate: FeedbackDetailUpdate;
4159
+ /** The PATCH is in flight (`update.isPending`). The upload adds its own. */
4160
+ pending?: boolean;
4161
+ /** The upload. Left out, the note goes without a file field. */
4162
+ onUpload?: FeedbackUpload;
4163
+ /** Snapshot the app view as a File — the kit's `captureAppScreenshot`, or the app's
4164
+ * own. A "Capture screenshot" button appears only with it; a rejection (no
4165
+ * `modern-screenshot`, a canvas that would not draw) toasts
4166
+ * `feedbackToast.captureFailed` and adds nothing. */
4167
+ onCaptureScreenshot?: () => Promise<File | null>;
4168
+ /** The round landed — the server has the note and, on an answered row, reopened it. */
4169
+ onSent?: () => void;
4170
+ onCancel: () => void;
4171
+ /** Over the field's defaults: the contract's types and 10 MB (§3.5), the
4172
+ * `feedbackToast` refusals, the `feedbackAttachment` words. */
4173
+ attachment?: Partial<FeedbackNoteAttachment>;
4174
+ /** The send COMMITS — see `FeedbackNoteEditor`'s `commit`. */
4175
+ commit?: boolean;
4176
+ labels?: Partial<FeedbackDetailLabels>;
4177
+ }
4178
+ /**
4179
+ * The rework editor (§4.4.5): the submitter — or the admin — sends an answered report
4180
+ * back with a note and at most one file, and the server reopens it (§3.4, keksdose
4181
+ * `submitRework`, `feedback-page.tsx:350`).
4182
+ *
4183
+ * - **The note is required** and trimmed: Send stays disabled on a blank box (a file
4184
+ * alone cannot be sent; keksdose `:577` dropped that click silently).
4185
+ * - **One file**, added, captured or pasted — the dialog's types and 10 MB. It is
4186
+ * uploaded FIRST and awaited: a body naming a file that never arrived would be a
4187
+ * rework whose evidence is missing, and the upload is the half that fails on its
4188
+ * own. If it fails nothing is sent and the toast says so; the note stays in the box.
4189
+ * - **The body alone goes in the PATCH** — {@link appendRework}'s strict extension of
4190
+ * the current body, which is how the server recognises the append and reopens the
4191
+ * row. Never a `status` (kastlan's client sent `OPEN`; an author may not write one,
4192
+ * so the server refused the whole PATCH, note and all — keksdose live #331).
4193
+ *
4194
+ * Exported on its own for an app that offers rework somewhere other than the row
4195
+ * detail. Kurvenschmiede's author verdict ("That is it, done") is NOT here: an answered
4196
+ * item stays answered unless its author sends it back (§7.2).
4197
+ */
4198
+ declare function FeedbackReworkSection({ row, onUpdate, pending, onUpload, onCaptureScreenshot, onSent, onCancel, attachment, commit, labels: labelsProp, }: FeedbackReworkSectionProps): react.JSX.Element;
4199
+ interface FeedbackRowDetailProps {
4200
+ /** The report — the list's own row, as `GET /feedback` / `GET /feedback/my` sent it. */
4201
+ row: FeedbackRecord;
4202
+ /** The admin on the inbox (`/feedback`): status pills and the outcome editor. `false`
4203
+ * on `/my-feedback`, for an admin too (§4.3). */
4204
+ canEdit: boolean;
4205
+ /** The signed-in user's id — who the AUTHOR is: the description editor and rework. */
4206
+ viewerId?: number | null;
4207
+ /** The page's PATCH — `update.mutate` (see {@link FeedbackDetailUpdate}). */
4208
+ onUpdate: FeedbackDetailUpdate;
4209
+ /** `update.isPending`: the editors' save buttons wait for it. */
4210
+ pending?: boolean;
4211
+ /** `POST /feedback/attachments` for the rework's one file. Left out, a rework is a
4212
+ * note alone. */
4213
+ onUpload?: FeedbackUpload;
4214
+ /** The app's authenticated GET (`AuthedImage`'s) — the pictures and the downloads go
4215
+ * through it, since the attachment route wants the bearer token (§3.5). Left out,
4216
+ * the browser's own fetch with same-origin cookies. */
4217
+ fetcher?: AuthedFetcher | null;
4218
+ /**
4219
+ * The undoable status change — what `useFeedbackStatusUndo(update.mutate)` returns,
4220
+ * the SAME one the table's status cell and the swipes use (§4.5). Left out, the
4221
+ * detail builds one from `onUpdate`, which is equivalent as long as `onUpdate` is the
4222
+ * page's mutation (its observer outlives the row a status filter removes).
4223
+ */
4224
+ statusChange?: FeedbackStatusChange;
4225
+ /** For the rework's "Capture screenshot" — see {@link FeedbackReworkSectionProps}. */
4226
+ onCaptureScreenshot?: () => Promise<File | null>;
4227
+ /** The rework's file field, over the contract's defaults. */
4228
+ reworkAttachment?: Partial<FeedbackNoteAttachment>;
4229
+ /** How the `resolved_at` date reads — the app's date cell, as a string. Default: the
4230
+ * kit's `formatDate` in the provider's locale, `medium` style. */
4231
+ renderDate?: (iso: string) => string;
4232
+ /**
4233
+ * The detail's saves COMMIT (description, outcome, rework, status): under a locked
4234
+ * `WriteLockProvider` each is `aria-disabled` with the lock's reason, and nothing
4235
+ * reaches `onUpdate`. Off by default — keksdose's shell lock is a read-only demo
4236
+ * BUDGET, and a person viewing one still owns their own reports.
4237
+ */
4238
+ commit?: boolean;
4239
+ labels?: Partial<FeedbackDetailLabels>;
4240
+ className?: string;
4241
+ }
4242
+ /**
4243
+ * The expanded row of a feedback table (§4.4): what was reported, where, with which
4244
+ * files, what the answer was, and where it stands — with the edits each viewer may
4245
+ * make. Hand it to `DataTable`'s `expandedRow`:
4246
+ *
4247
+ * ```tsx
4248
+ * const update = useMutation({ mutationFn: ({ id, ...patch }) => api.updateFeedback(id, patch),
4249
+ * onSuccess: invalidate, onError: (e) => toast.error(detailOf(e) ?? toastLabels.updateFailed) });
4250
+ * const changeStatus = useFeedbackStatusUndo(update.mutate);
4251
+ * <DataTable
4252
+ * isExpanded={(row) => expandedId === row.id}
4253
+ * expandedRow={(row) => (
4254
+ * <FeedbackRowDetail row={row} canEdit={isAdmin && !mine} viewerId={user?.id}
4255
+ * onUpdate={update.mutate} pending={update.isPending} statusChange={changeStatus}
4256
+ * onUpload={api.uploadFeedbackAttachment} fetcher={authedImageFetcher}
4257
+ * onCaptureScreenshot={captureAppScreenshot} />
4258
+ * )} … />
4259
+ * ```
4260
+ *
4261
+ * Mounted only while its row is expanded, so which editor is open lives here, and
4262
+ * collapsing the row closes it. Each editor is keyed by the row's id, so a draft is
4263
+ * never thrown away by a list refresh under it.
4264
+ */
4265
+ declare function FeedbackRowDetail({ row, canEdit, viewerId, onUpdate, pending, onUpload, fetcher, statusChange, onCaptureScreenshot, reworkAttachment, renderDate, commit, labels: labelsProp, className, }: FeedbackRowDetailProps): react.JSX.Element;
4266
+
3028
4267
  /**
3029
4268
  * The `confirmDialog` namespace of `<UiKitProvider labels>`: the two buttons' fallback
3030
4269
  * text. A call that knows what it is confirming says so per call (`confirmLabel:
@@ -4008,6 +5247,17 @@ interface UiKitLabels {
4008
5247
  feedbackDialog: FeedbackDialogTextLabels;
4009
5248
  feedbackThread: FeedbackThreadLabels;
4010
5249
  feedbackComposer: FeedbackComposerLabels;
5250
+ /** 0.27.0: the feedback harmonisation (docs/feedback-harmonization.md §4) — a report's
5251
+ * status and category words, keyed by the enum value; the toasts a feedback page
5252
+ * shows; `FeedbackMenu`; `FeedbackContextBox`; the two pages' table; the row detail.
5253
+ * keksdose's wording is the canon, in every language. */
5254
+ feedbackStatus: FeedbackStatusLabels;
5255
+ feedbackCategory: FeedbackCategoryLabels;
5256
+ feedbackToast: FeedbackToastLabels;
5257
+ feedbackMenu: FeedbackMenuLabels;
5258
+ feedbackContext: FeedbackContextLabels;
5259
+ feedbackPage: FeedbackPageLabels;
5260
+ feedbackDetail: FeedbackDetailLabels;
4011
5261
  /** 0.12.0: `ProfileSetting`, `PasswordSetting`, `TwoFactorSetting`, `PasskeysSetting`
4012
5262
  * — one record per section. The provider takes a whole section record; the
4013
5263
  * components merge it key by key, as a prop. */
@@ -4250,4 +5500,4 @@ declare function useKitFileLabels(prop?: Partial<FileLabels>): FileLabels;
4250
5500
  */
4251
5501
  declare function missingKitLabels(labels: UiKitLabelOverrides | undefined, reference: UiKitLabels): string[];
4252
5502
 
4253
- export { DEFAULT_COMMON_LABELS as $, type AppShellLabels as A, Button as B, type CalculatorLabels as C, type ChatComposerHandle as D, type ChatComposerLabels as E, type ChatComposerProps as F, type ChatComposerSlotContext as G, type ComboboxLabels as H, type CommonLabels as I, type ConfirmDialogLabels as J, type ConfirmFn as K, type ConfirmOptions as L, ConfirmProvider as M, type ConfirmProviderProps as N, type ConfirmTone as O, CopyButton as P, type CopyButtonLabels as Q, type CopyButtonProps as R, type CurrencyLabels as S, CurrentPasswordInput as T, type CurrentPasswordInputLabels as U, type CurrentPasswordInputProps as V, DEFAULT_APP_SHELL_LABELS as W, DEFAULT_ATTACHMENT_ACCEPT as X, DEFAULT_CALCULATOR_LABELS as Y, DEFAULT_CHARACTER_COUNT_LABELS as Z, DEFAULT_COMBOBOX_LABELS as _, type ButtonClassesOptions as a, FeedbackDetail as a$, DEFAULT_CONFIRM_DIALOG_LABELS as a0, DEFAULT_COPY_BUTTON_LABELS as a1, DEFAULT_CURRENCY_LABELS as a2, DEFAULT_DANGER_CONFIRM_LABELS as a3, DEFAULT_DATE_PICKER_LABELS as a4, DEFAULT_FEEDBACK_ATTACHMENT_LABELS as a5, DEFAULT_FEEDBACK_COMPOSER_LABELS as a6, DEFAULT_FEEDBACK_DIALOG_LABELS as a7, DEFAULT_FEEDBACK_THREAD_LABELS as a8, DEFAULT_FILE_LABELS as a9, FIELD_DISPLAY as aA, FIELD_FLOATING_PAD as aB, FIELD_INVALID as aC, FIELD_TRIGGER as aD, FIELD_WRITABLE_LOOK as aE, FLOATING_INPUT_CLASS as aF, FLOATING_LABEL_CLASS as aG, type FeedbackAttachmentError as aH, type FeedbackAttachmentErrorInfo as aI, FeedbackAttachmentField as aJ, type FeedbackAttachmentFieldLabels as aK, type FeedbackAttachmentFieldMultipleProps as aL, type FeedbackAttachmentFieldProps as aM, type FeedbackAttachmentFieldRefsProps as aN, type FeedbackAttachmentFieldSingleProps as aO, type FeedbackAttachmentLabels as aP, type FeedbackAttachmentRef as aQ, type FeedbackAttachmentRefsError as aR, type FeedbackCategory as aS, FeedbackCategoryBadge as aT, type FeedbackCategoryOption as aU, FeedbackComposer as aV, type FeedbackComposerAttachment as aW, type FeedbackComposerHandle as aX, type FeedbackComposerLabels as aY, type FeedbackComposerProps as aZ, type FeedbackComposerSlotContext as a_, DEFAULT_FILE_PICKER_LABELS as aa, DEFAULT_FORM_ACTIONS_LABELS as ab, DEFAULT_IBAN_INPUT_LABELS as ac, DEFAULT_MAX_ATTACHMENTS as ad, DEFAULT_MAX_ATTACHMENT_BYTES as ae, DEFAULT_MULTI_SELECT_LABELS as af, DEFAULT_PASSWORD_REVEAL_LABELS as ag, DEFAULT_PHONE_INPUT_LABELS as ah, DEFAULT_PICKER_SHEET_LABELS as ai, DEFAULT_SHARE_CARD_LABELS as aj, DEFAULT_SWIPEABLE_ROW_LABELS as ak, DEFAULT_TABS_LABELS as al, DEFAULT_TOP_BAR_LABELS as am, DangerConfirm as an, type DangerConfirmLabels as ao, type DangerConfirmProps as ap, type DangerConfirmValues as aq, type DangerConsequence as ar, type DatePickerLabels as as, EmptyState as at, type EmptyStateProps as au, FEEDBACK_CATEGORY_META as av, FEEDBACK_CATEGORY_ORDER as aw, FEEDBACK_STATUS_META as ax, FEEDBACK_STATUS_ORDER as ay, FIELD_BASE as az, type ButtonLinkProps as b, type LabelProps as b$, FeedbackDetailSection as b0, FeedbackDialog as b1, type FeedbackDialogLabels as b2, type FeedbackDialogMultipleProps as b3, type FeedbackDialogProps as b4, type FeedbackDialogSingleProps as b5, type FeedbackDialogTextLabels as b6, type FeedbackMultipleSubmission as b7, type FeedbackNoteAttachment as b8, FeedbackNoteEditor as b9, type FormActionsAlign as bA, type FormActionsDestructive as bB, type FormActionsLabels as bC, type FormActionsPlacement as bD, type FormActionsProps as bE, type FormActionsStickyWithin as bF, type FormActionsSubmitProps as bG, type FormActionsSubmitShortcut as bH, IbanInput as bI, type IbanInputLabels as bJ, type IbanInputProps as bK, IconButton as bL, type IconButtonGlyphSize as bM, type IconButtonLinkProps as bN, type IconButtonProps as bO, type IconButtonSize as bP, type IconButtonTone as bQ, type IconButtonToneColor as bR, type IconButtonVariant as bS, Input as bT, type InputProps as bU, type KitDateFormatContext as bV, type KitDateFormatter as bW, type KitLinkComponent as bX, type KitLinkProps as bY, Label as bZ, type LabelOverride as b_, FeedbackProse as ba, type FeedbackStatus as bb, FeedbackStatusBadge as bc, FeedbackStatusTransitions as bd, type FeedbackSubmission as be, FeedbackThread as bf, type FeedbackThreadAttachment as bg, type FeedbackThreadEvent as bh, type FeedbackThreadItem as bi, type FeedbackThreadLabels as bj, type FeedbackThreadMessage as bk, type FeedbackThreadProps as bl, FieldChevron as bm, type FieldChevronProps as bn, FieldHint as bo, type FieldHintProps as bp, FileButton as bq, type FileButtonProps as br, type FileLabels as bs, type FilePickerLabels as bt, type FileRejection as bu, type FileRejectionReason as bv, type FileScreenOptions as bw, FloatingField as bx, type FloatingFieldProps as by, FormActions as bz, type ButtonProps as c, type FilePickHandler as c$, type MultiSelectLabels as c0, PHONE_QUERY as c1, type PasswordRevealLabels as c2, PhoneInput as c3, type PhoneInputLabels as c4, type PhoneInputProps as c5, type PickerSheetLabels as c6, type ResponsiveFormActionsPlacement as c7, Select as c8, type SelectProps as c9, type UiKitLabelOverrides as cA, type UiKitLabels as cB, UiKitProvider as cC, type UiKitProviderProps as cD, type UseFilePickerOptions as cE, type UseFilePickerReturn as cF, buttonClasses as cG, feedbackCategoryRank as cH, formatFileSize as cI, matchesAccept as cJ, missingKitLabels as cK, nextFeedbackStatus as cL, pastedName as cM, resolvePasswordRevealLabels as cN, selectableFeedbackStatuses as cO, typedMatches as cP, useConfirm as cQ, useFilePicker as cR, useKitChartTooltipPlacement as cS, useKitDateFormatter as cT, useKitFileLabels as cU, useKitLabelOverrides as cV, useKitLabels as cW, useKitLink as cX, useKitLocale as cY, useKitWeekStart as cZ, visibleFeedbackStatuses as c_, type ShareAddRequest as ca, type ShareCandidate as cb, ShareCard as cc, type ShareCardLabels as cd, type ShareCardProps as ce, ShareDialog as cf, type ShareDialogProps as cg, type ShareGrantee as ch, SharePanel as ci, type SharePanelProps as cj, type SharePendingGrant as ck, type ShareRole as cl, Spinner as cm, type SpinnerProps as cn, type SwipeableRowLabels as co, type TabItem as cp, Tabs as cq, type TabsLabels as cr, type TabsProps as cs, Textarea as ct, type TextareaProps as cu, type TopBarLabels as cv, TypedConfirmField as cw, type TypedConfirmFieldLabels as cx, type TypedConfirmFieldProps as cy, type TypedMatch as cz, type ButtonSize as d, usePromisePending as d0, formatAccept as d1, judgePick as d2, screenFiles as d3, summariseRejections as d4, type ButtonTone as e, type ButtonVariant as f, Card as g, CardAction as h, type CardActionProps as i, CardContent as j, type CardContentProps as k, type CardDensity as l, CardDescription as m, type CardDescriptionProps as n, CardFooter as o, type CardFooterProps as p, CardHeader as q, type CardHeaderProps as r, type CardProps as s, CardTitle as t, type CardTitleLevel as u, type CardTitleProps as v, type CardTone as w, type CharacterCountLabels as x, type ChartTooltipPlacement as y, ChatComposer as z };
5503
+ export { type FeedbackContext as $, type FeedbackAttachmentError as A, type FeedbackAttachmentErrorInfo as B, ChatComposer as C, DEFAULT_ATTACHMENT_ACCEPT as D, FeedbackAttachmentField as E, FEEDBACK_ATTACHMENT_ACCEPT as F, type FeedbackAttachmentFieldLabels as G, type FeedbackAttachmentFieldMultipleProps as H, type FeedbackAttachmentFieldProps as I, type FeedbackAttachmentFieldRefsProps as J, type FeedbackAttachmentFieldSingleProps as K, type FeedbackAttachmentLabels as L, type FeedbackAttachmentRef as M, type FeedbackAttachmentRefsError as N, FeedbackAwaitingToggle as O, type FeedbackAwaitingToggleProps as P, type FeedbackCategory as Q, FeedbackCategoryBadge as R, type FeedbackCategoryLabels as S, type FeedbackCategoryOption as T, type FeedbackColumnsOptions as U, FeedbackComposer as V, type FeedbackComposerAttachment as W, type FeedbackComposerHandle as X, type FeedbackComposerLabels as Y, type FeedbackComposerProps as Z, type FeedbackComposerSlotContext as _, type ChatComposerHandle as a, feedbackAttachmentUrls as a$, FeedbackContextBox as a0, type FeedbackContextBoxProps as a1, type FeedbackContextInput as a2, type FeedbackContextLabels as a3, FeedbackDetail as a4, type FeedbackDetailLabels as a5, type FeedbackDetailPatch as a6, FeedbackDetailSection as a7, type FeedbackDetailUpdate as a8, FeedbackDialog as a9, FeedbackStatusBadge as aA, type FeedbackStatusChange as aB, type FeedbackStatusLabels as aC, type FeedbackStatusMutate as aD, type FeedbackStatusPatch as aE, type FeedbackStatusRow as aF, FeedbackStatusTransitions as aG, type FeedbackStatusUndoOptions as aH, FeedbackSubject as aI, type FeedbackSubjectProps as aJ, type FeedbackSubmission as aK, type FeedbackSubmitHint as aL, type FeedbackSubmitter as aM, type FeedbackTableRow as aN, FeedbackThread as aO, type FeedbackThreadAttachment as aP, type FeedbackThreadEvent as aQ, type FeedbackThreadItem as aR, type FeedbackThreadLabels as aS, type FeedbackThreadMessage as aT, type FeedbackThreadProps as aU, type FeedbackToastLabels as aV, type FeedbackUpload as aW, type UseFeedbackColumnsOptions as aX, appendRework as aY, attachmentName as aZ, feedbackAttachmentLine as a_, type FeedbackDialogLabels as aa, type FeedbackDialogMultipleProps as ab, type FeedbackDialogProps as ac, type FeedbackDialogSingleProps as ad, type FeedbackDialogTextLabels as ae, FeedbackEmptyState as af, type FeedbackEmptyStateProps as ag, type FeedbackEnvironment as ah, FeedbackMenu as ai, type FeedbackMenuLabels as aj, type FeedbackMenuProps as ak, FeedbackMobileCard as al, type FeedbackMobileCardProps as am, type FeedbackMultipleSubmission as an, type FeedbackNoteAttachment as ao, FeedbackNoteEditor as ap, type FeedbackPageLabels as aq, FeedbackProse as ar, type FeedbackRecord as as, type FeedbackRenderDate as at, FeedbackReworkSection as au, type FeedbackReworkSectionProps as av, type FeedbackRowAccess as aw, FeedbackRowDetail as ax, type FeedbackRowDetailProps as ay, type FeedbackStatus as az, type ChatComposerLabels as b, type ConfirmProviderProps as b$, feedbackCategoryRank as b0, feedbackColumns as b1, feedbackContext as b2, feedbackEnvironment as b3, feedbackMobileGroupBy as b4, feedbackPageHref as b5, feedbackPagePath as b6, feedbackPageUrl as b7, feedbackRowAccess as b8, feedbackSubmitter as b9, type ButtonVariant as bA, type CalculatorLabels as bB, Card as bC, CardAction as bD, type CardActionProps as bE, CardContent as bF, type CardContentProps as bG, type CardDensity as bH, CardDescription as bI, type CardDescriptionProps as bJ, CardFooter as bK, type CardFooterProps as bL, CardHeader as bM, type CardHeaderProps as bN, type CardProps as bO, CardTitle as bP, type CardTitleLevel as bQ, type CardTitleProps as bR, type CardTone as bS, type CharacterCountLabels as bT, type ChartTooltipPlacement as bU, type ComboboxLabels as bV, type CommonLabels as bW, type ConfirmDialogLabels as bX, type ConfirmFn as bY, type ConfirmOptions as bZ, ConfirmProvider as b_, feedbackSubmitterEmail as ba, isFeedbackAwaiting as bb, isImageAttachment as bc, nextFeedbackStatus as bd, pastedName as be, replaceDescription as bf, reworkCount as bg, selectableFeedbackStatuses as bh, splitBodyAttachments as bi, splitDescription as bj, useFeedbackCategoryLabels as bk, useFeedbackColumns as bl, useFeedbackDetailLabels as bm, useFeedbackPageLabels as bn, useFeedbackRenderDate as bo, useFeedbackStatusLabels as bp, useFeedbackStatusUndo as bq, useFeedbackToastLabels as br, visibleFeedbackStatuses as bs, type AppShellLabels as bt, Button as bu, type ButtonClassesOptions as bv, type ButtonLinkProps as bw, type ButtonProps as bx, type ButtonSize as by, type ButtonTone as bz, type ChatComposerProps as c, type FormActionsPlacement as c$, type ConfirmTone as c0, CopyButton as c1, type CopyButtonLabels as c2, type CopyButtonProps as c3, type CurrencyLabels as c4, CurrentPasswordInput as c5, type CurrentPasswordInputLabels as c6, type CurrentPasswordInputProps as c7, DEFAULT_APP_SHELL_LABELS as c8, DEFAULT_CALCULATOR_LABELS as c9, EmptyState as cA, type EmptyStateProps as cB, FIELD_BASE as cC, FIELD_DISPLAY as cD, FIELD_FLOATING_PAD as cE, FIELD_INVALID as cF, FIELD_TRIGGER as cG, FIELD_WRITABLE_LOOK as cH, FLOATING_INPUT_CLASS as cI, FLOATING_LABEL_CLASS as cJ, FieldChevron as cK, type FieldChevronProps as cL, FieldHint as cM, type FieldHintProps as cN, FileButton as cO, type FileButtonProps as cP, type FileLabels as cQ, type FilePickerLabels as cR, type FileRejection as cS, type FileRejectionReason as cT, type FileScreenOptions as cU, FloatingField as cV, type FloatingFieldProps as cW, FormActions as cX, type FormActionsAlign as cY, type FormActionsDestructive as cZ, type FormActionsLabels as c_, DEFAULT_CHARACTER_COUNT_LABELS as ca, DEFAULT_COMBOBOX_LABELS as cb, DEFAULT_COMMON_LABELS as cc, DEFAULT_CONFIRM_DIALOG_LABELS as cd, DEFAULT_COPY_BUTTON_LABELS as ce, DEFAULT_CURRENCY_LABELS as cf, DEFAULT_DANGER_CONFIRM_LABELS as cg, DEFAULT_DATE_PICKER_LABELS as ch, DEFAULT_FILE_LABELS as ci, DEFAULT_FILE_PICKER_LABELS as cj, DEFAULT_FORM_ACTIONS_LABELS as ck, DEFAULT_IBAN_INPUT_LABELS as cl, DEFAULT_MULTI_SELECT_LABELS as cm, DEFAULT_PASSWORD_REVEAL_LABELS as cn, DEFAULT_PHONE_INPUT_LABELS as co, DEFAULT_PICKER_SHEET_LABELS as cp, DEFAULT_SHARE_CARD_LABELS as cq, DEFAULT_SWIPEABLE_ROW_LABELS as cr, DEFAULT_TABS_LABELS as cs, DEFAULT_TOP_BAR_LABELS as ct, DangerConfirm as cu, type DangerConfirmLabels as cv, type DangerConfirmProps as cw, type DangerConfirmValues as cx, type DangerConsequence as cy, type DatePickerLabels as cz, type ChatComposerSlotContext as d, UiKitProvider as d$, type FormActionsProps as d0, type FormActionsStickyWithin as d1, type FormActionsSubmitProps as d2, type FormActionsSubmitShortcut as d3, IbanInput as d4, type IbanInputLabels as d5, type IbanInputProps as d6, IconButton as d7, type IconButtonGlyphSize as d8, type IconButtonLinkProps as d9, type ShareCandidate as dA, ShareCard as dB, type ShareCardLabels as dC, type ShareCardProps as dD, ShareDialog as dE, type ShareDialogProps as dF, type ShareGrantee as dG, SharePanel as dH, type SharePanelProps as dI, type SharePendingGrant as dJ, type ShareRole as dK, Spinner as dL, type SpinnerProps as dM, type SwipeableRowLabels as dN, type TabItem as dO, Tabs as dP, type TabsLabels as dQ, type TabsProps as dR, Textarea as dS, type TextareaProps as dT, type TopBarLabels as dU, TypedConfirmField as dV, type TypedConfirmFieldLabels as dW, type TypedConfirmFieldProps as dX, type TypedMatch as dY, type UiKitLabelOverrides as dZ, type UiKitLabels as d_, type IconButtonProps as da, type IconButtonSize as db, type IconButtonTone as dc, type IconButtonToneColor as dd, type IconButtonVariant as de, Input as df, type InputProps as dg, type KitDateFormatContext as dh, type KitDateFormatter as di, type KitLinkComponent as dj, type KitLinkProps as dk, Label as dl, type LabelOverride as dm, type LabelProps as dn, type MultiSelectLabels as dp, PHONE_QUERY as dq, type PasswordRevealLabels as dr, PhoneInput as ds, type PhoneInputLabels as dt, type PhoneInputProps as du, type PickerSheetLabels as dv, type ResponsiveFormActionsPlacement as dw, Select as dx, type SelectProps as dy, type ShareAddRequest as dz, DEFAULT_FEEDBACK_ATTACHMENT_LABELS as e, type UiKitProviderProps as e0, type UseFilePickerOptions as e1, type UseFilePickerReturn as e2, buttonClasses as e3, formatFileSize as e4, matchesAccept as e5, missingKitLabels as e6, resolvePasswordRevealLabels as e7, typedMatches as e8, useConfirm as e9, useFilePicker as ea, useKitChartTooltipPlacement as eb, useKitDateFormatter as ec, useKitFileLabels as ed, useKitLabelOverrides as ee, useKitLabels as ef, useKitLink as eg, useKitLocale as eh, useKitWeekStart as ei, type FilePickHandler as ej, usePromisePending as ek, formatAccept as el, judgePick as em, screenFiles as en, summariseRejections as eo, DEFAULT_FEEDBACK_CATEGORY_LABELS as f, DEFAULT_FEEDBACK_COMPOSER_LABELS as g, DEFAULT_FEEDBACK_CONTEXT_LABELS as h, DEFAULT_FEEDBACK_DETAIL_LABELS as i, DEFAULT_FEEDBACK_DIALOG_LABELS as j, DEFAULT_FEEDBACK_MENU_LABELS as k, DEFAULT_FEEDBACK_PAGE_LABELS as l, DEFAULT_FEEDBACK_STATUS_LABELS as m, DEFAULT_FEEDBACK_THREAD_LABELS as n, DEFAULT_FEEDBACK_TOAST_LABELS as o, DEFAULT_MAX_ATTACHMENTS as p, DEFAULT_MAX_ATTACHMENT_BYTES as q, FEEDBACK_AUTHOR_EDITABLE_STATUSES as r, FEEDBACK_AWAITING_STATUSES as s, FEEDBACK_CATEGORY_META as t, FEEDBACK_CATEGORY_ORDER as u, FEEDBACK_PICKABLE_CATEGORIES as v, FEEDBACK_REWORKABLE_STATUSES as w, FEEDBACK_STATUS_META as x, FEEDBACK_STATUS_ORDER as y, FEEDBACK_UNDO_DURATION as z };