@drawbridge/components 0.1.5 → 0.1.6

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.
package/README.md CHANGED
@@ -4,12 +4,16 @@ The components for Drawbridge. Three folders: `shared/` renders on both the dash
4
4
 
5
5
  ## Work on a component
6
6
 
7
- npm run storybook # every component, every state, at http://localhost:6006
8
- npm run test-storybook # renders every story in Chromium; fails on an error
7
+ npm run app # every component, every state, at http://localhost:3003 (the family's ports: dashboard 3000, share 3001, website 3002)
8
+ npm run test:app # renders every story in Chromium; fails on an error
9
9
  npm test # exports resolve, every component has a story, unit tests
10
10
 
11
11
  A component without a `.stories.js` beside it fails `npm test`. One story per visible state.
12
12
 
13
+ ## The app on the web
14
+
15
+ `https://components.trydrawbridge.com` is this app, built from `main` on every push (`.do/app.yaml`, `Dockerfile`): `npm run app:build` makes the static site and `server.js` serves it behind the family's staging gate, the `BASIC_AUTH_USERNAME` and `BASIC_AUTH_PASSWORD` pair set on the DigitalOcean app, the same values that open the dev dashboard and website. Unset both and the site is public; `/health` is always open.
16
+
13
17
  ## See it in an app without publishing
14
18
 
15
19
  npm run dev # here: rebuilds dist on change
@@ -460,7 +460,9 @@ const TemplateSubdomainGallery = ({
460
460
  "div",
461
461
  {
462
462
  ...{
463
- className: "items-center absolute top-0 left-0 z-50 flex w-full px-3 py-5 lg:p-5 " + (length > 1 ? "flex" : "hidden")
463
+ // @story public-entry/view-page
464
+ // Above the skip buttons and holds no control: taps pass through to them.
465
+ className: "items-center absolute top-0 left-0 z-50 flex w-full px-3 py-5 lg:p-5 pointer-events-none " + (length > 1 ? "flex" : "hidden")
464
466
  },
465
467
  children: gallery.map(({ id }, key) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
466
468
  "div",
@@ -428,7 +428,9 @@ const TemplateSubdomainGallery = ({
428
428
  "div",
429
429
  {
430
430
  ...{
431
- className: "items-center absolute top-0 left-0 z-50 flex w-full px-3 py-5 lg:p-5 " + (length > 1 ? "flex" : "hidden")
431
+ // @story public-entry/view-page
432
+ // Above the skip buttons and holds no control: taps pass through to them.
433
+ className: "items-center absolute top-0 left-0 z-50 flex w-full px-3 py-5 lg:p-5 pointer-events-none " + (length > 1 ? "flex" : "hidden")
432
434
  },
433
435
  children: gallery.map(({ id }, key) => /* @__PURE__ */ jsx(
434
436
  "div",
@@ -122,7 +122,10 @@ const TemplateSubdomain = ({
122
122
  "div",
123
123
  {
124
124
  ...{
125
- className: "z-30 fixed top-0 left-0 right-1 lg:left-auto lg:right-1.5 lg:w-auto",
125
+ // @story public-entry/view-page
126
+ // Fixed over the top of the gallery: taps on its empty space (and the
127
+ // logo, a picture) pass through to the gallery; its buttons keep theirs.
128
+ className: "z-30 fixed top-0 left-0 right-1 lg:left-auto lg:right-1.5 lg:w-auto pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto",
126
129
  style: {
127
130
  ...gallery.length > 1 && {
128
131
  "--controls-padding-top": controlPaddingTop + "px"
@@ -216,7 +219,10 @@ const TemplateSubdomain = ({
216
219
  "div",
217
220
  {
218
221
  ...{
219
- className: "lg:hidden absolute bottom-2 left-0 w-full px-6 pb-4 pt-12 lg:pt-20 text-center z-20 space-y-4",
222
+ // @story public-entry/view-page
223
+ // Sits over the lower gallery, above its skip buttons: taps pass through
224
+ // to the gallery except on this block's own buttons and links.
225
+ className: "lg:hidden absolute bottom-2 left-0 w-full px-6 pb-4 pt-12 lg:pt-20 text-center z-20 space-y-4 pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto",
220
226
  style: {
221
227
  ...(style == null ? void 0 : style.text) && {
222
228
  color: (_a2 = style.text.color) == null ? void 0 : _a2.rgb
@@ -90,7 +90,10 @@ const TemplateSubdomain = ({
90
90
  "div",
91
91
  {
92
92
  ...{
93
- className: "z-30 fixed top-0 left-0 right-1 lg:left-auto lg:right-1.5 lg:w-auto",
93
+ // @story public-entry/view-page
94
+ // Fixed over the top of the gallery: taps on its empty space (and the
95
+ // logo, a picture) pass through to the gallery; its buttons keep theirs.
96
+ className: "z-30 fixed top-0 left-0 right-1 lg:left-auto lg:right-1.5 lg:w-auto pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto",
94
97
  style: {
95
98
  ...gallery.length > 1 && {
96
99
  "--controls-padding-top": controlPaddingTop + "px"
@@ -184,7 +187,10 @@ const TemplateSubdomain = ({
184
187
  "div",
185
188
  {
186
189
  ...{
187
- className: "lg:hidden absolute bottom-2 left-0 w-full px-6 pb-4 pt-12 lg:pt-20 text-center z-20 space-y-4",
190
+ // @story public-entry/view-page
191
+ // Sits over the lower gallery, above its skip buttons: taps pass through
192
+ // to the gallery except on this block's own buttons and links.
193
+ className: "lg:hidden absolute bottom-2 left-0 w-full px-6 pb-4 pt-12 lg:pt-20 text-center z-20 space-y-4 pointer-events-none [&_a]:pointer-events-auto [&_button]:pointer-events-auto",
188
194
  style: {
189
195
  ...(style == null ? void 0 : style.text) && {
190
196
  color: (_a2 = style.text.color) == null ? void 0 : _a2.rgb
@@ -42,78 +42,88 @@ const FormAction = ({
42
42
  children,
43
43
  callback,
44
44
  className = "space-y-4",
45
+ defaultValues,
45
46
  disabled,
46
47
  fineprint,
47
48
  onError,
48
49
  onSuccess
49
50
  }) => {
50
- const { handleSubmit } = (0, import_form.useForm)();
51
+ const methods = (0, import_form.useForm)({
52
+ defaultValues,
53
+ guard: false
54
+ });
51
55
  const mutation = (0, import_react_query.useMutation)({
52
56
  mutationFn: callback,
53
57
  onSuccess,
54
58
  onError,
55
59
  retry: false
56
60
  });
57
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
58
- import_form2.default,
61
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
62
+ import_form.FormProvider,
59
63
  {
60
- ...{
61
- className: "w-full space-y-6",
62
- onSubmit: handleSubmit(() => mutation.mutate())
63
- },
64
- children: [
65
- children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
66
- "div",
67
- {
68
- ...{
69
- className
70
- },
71
- children
72
- }
73
- ),
74
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
75
- import_fieldset.default,
76
- {
77
- ...{
78
- // Side by side once there is something to cancel, so the pair reads as a
79
- // prompt; equal grid columns, as a Button never shrinks in a flex row.
80
- className: cancel ? "grid grid-cols-2 gap-4" : "space-y-6",
81
- disabled: disabled || mutation.isPending
82
- },
83
- children: [
84
- cancel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
85
- import_button.default,
86
- {
87
- ...{
88
- onClick: cancel.onClick,
89
- variant: "secondary"
90
- },
91
- children: cancel.action || "Cancel"
92
- }
93
- ),
94
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
95
- import_button.default,
96
- {
97
- ...{
98
- loading: mutation == null ? void 0 : mutation.isPending,
99
- type: "submit"
100
- },
101
- children: action
102
- }
103
- )
104
- ]
105
- }
106
- ),
107
- fineprint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
108
- "div",
109
- {
110
- ...{
111
- className: "text-sm text-center"
112
- },
113
- children: fineprint
114
- }
115
- )
116
- ]
64
+ ...methods,
65
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
66
+ import_form2.default,
67
+ {
68
+ ...{
69
+ className: "w-full space-y-6",
70
+ onSubmit: methods.handleSubmit((values) => mutation.mutate(values))
71
+ },
72
+ children: [
73
+ children && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
74
+ "div",
75
+ {
76
+ ...{
77
+ className
78
+ },
79
+ children
80
+ }
81
+ ),
82
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
83
+ import_fieldset.default,
84
+ {
85
+ ...{
86
+ // Side by side once there is something to cancel, so the pair reads as a
87
+ // prompt; equal grid columns, as a Button never shrinks in a flex row.
88
+ className: cancel ? "grid grid-cols-2 gap-4" : "space-y-6",
89
+ disabled: disabled || mutation.isPending
90
+ },
91
+ children: [
92
+ cancel && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
93
+ import_button.default,
94
+ {
95
+ ...{
96
+ onClick: cancel.onClick,
97
+ variant: "secondary"
98
+ },
99
+ children: cancel.action || "Cancel"
100
+ }
101
+ ),
102
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
103
+ import_button.default,
104
+ {
105
+ ...{
106
+ loading: mutation == null ? void 0 : mutation.isPending,
107
+ type: "submit"
108
+ },
109
+ children: action
110
+ }
111
+ )
112
+ ]
113
+ }
114
+ ),
115
+ fineprint && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
116
+ "div",
117
+ {
118
+ ...{
119
+ className: "text-sm text-center"
120
+ },
121
+ children: fineprint
122
+ }
123
+ )
124
+ ]
125
+ }
126
+ )
117
127
  }
118
128
  );
119
129
  };
@@ -1,5 +1,5 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useForm } from "../shared/lib/form.mjs";
2
+ import { FormProvider, useForm } from "../shared/lib/form.mjs";
3
3
  import { useMutation } from "@tanstack/react-query";
4
4
  import Form from "../shared/form.mjs";
5
5
  import Fieldset from "../shared/fieldset.mjs";
@@ -10,78 +10,88 @@ const FormAction = ({
10
10
  children,
11
11
  callback,
12
12
  className = "space-y-4",
13
+ defaultValues,
13
14
  disabled,
14
15
  fineprint,
15
16
  onError,
16
17
  onSuccess
17
18
  }) => {
18
- const { handleSubmit } = useForm();
19
+ const methods = useForm({
20
+ defaultValues,
21
+ guard: false
22
+ });
19
23
  const mutation = useMutation({
20
24
  mutationFn: callback,
21
25
  onSuccess,
22
26
  onError,
23
27
  retry: false
24
28
  });
25
- return /* @__PURE__ */ jsxs(
26
- Form,
29
+ return /* @__PURE__ */ jsx(
30
+ FormProvider,
27
31
  {
28
- ...{
29
- className: "w-full space-y-6",
30
- onSubmit: handleSubmit(() => mutation.mutate())
31
- },
32
- children: [
33
- children && /* @__PURE__ */ jsx(
34
- "div",
35
- {
36
- ...{
37
- className
38
- },
39
- children
40
- }
41
- ),
42
- /* @__PURE__ */ jsxs(
43
- Fieldset,
44
- {
45
- ...{
46
- // Side by side once there is something to cancel, so the pair reads as a
47
- // prompt; equal grid columns, as a Button never shrinks in a flex row.
48
- className: cancel ? "grid grid-cols-2 gap-4" : "space-y-6",
49
- disabled: disabled || mutation.isPending
50
- },
51
- children: [
52
- cancel && /* @__PURE__ */ jsx(
53
- Button,
54
- {
55
- ...{
56
- onClick: cancel.onClick,
57
- variant: "secondary"
58
- },
59
- children: cancel.action || "Cancel"
60
- }
61
- ),
62
- /* @__PURE__ */ jsx(
63
- Button,
64
- {
65
- ...{
66
- loading: mutation == null ? void 0 : mutation.isPending,
67
- type: "submit"
68
- },
69
- children: action
70
- }
71
- )
72
- ]
73
- }
74
- ),
75
- fineprint && /* @__PURE__ */ jsx(
76
- "div",
77
- {
78
- ...{
79
- className: "text-sm text-center"
80
- },
81
- children: fineprint
82
- }
83
- )
84
- ]
32
+ ...methods,
33
+ children: /* @__PURE__ */ jsxs(
34
+ Form,
35
+ {
36
+ ...{
37
+ className: "w-full space-y-6",
38
+ onSubmit: methods.handleSubmit((values) => mutation.mutate(values))
39
+ },
40
+ children: [
41
+ children && /* @__PURE__ */ jsx(
42
+ "div",
43
+ {
44
+ ...{
45
+ className
46
+ },
47
+ children
48
+ }
49
+ ),
50
+ /* @__PURE__ */ jsxs(
51
+ Fieldset,
52
+ {
53
+ ...{
54
+ // Side by side once there is something to cancel, so the pair reads as a
55
+ // prompt; equal grid columns, as a Button never shrinks in a flex row.
56
+ className: cancel ? "grid grid-cols-2 gap-4" : "space-y-6",
57
+ disabled: disabled || mutation.isPending
58
+ },
59
+ children: [
60
+ cancel && /* @__PURE__ */ jsx(
61
+ Button,
62
+ {
63
+ ...{
64
+ onClick: cancel.onClick,
65
+ variant: "secondary"
66
+ },
67
+ children: cancel.action || "Cancel"
68
+ }
69
+ ),
70
+ /* @__PURE__ */ jsx(
71
+ Button,
72
+ {
73
+ ...{
74
+ loading: mutation == null ? void 0 : mutation.isPending,
75
+ type: "submit"
76
+ },
77
+ children: action
78
+ }
79
+ )
80
+ ]
81
+ }
82
+ ),
83
+ fineprint && /* @__PURE__ */ jsx(
84
+ "div",
85
+ {
86
+ ...{
87
+ className: "text-sm text-center"
88
+ },
89
+ children: fineprint
90
+ }
91
+ )
92
+ ]
93
+ }
94
+ )
85
95
  }
86
96
  );
87
97
  };
@@ -49,6 +49,7 @@ var import_input_checkbox = __toESM(require("../input-checkbox"));
49
49
  var import_input_switch = __toESM(require("../shared/input-switch"));
50
50
  var import_button = __toESM(require("../shared/button"));
51
51
  var import_copy_to_clipboard = __toESM(require("../shared/copy-to-clipboard"));
52
+ var import_connection_controls = require("./lib/connection-controls");
52
53
  const Submit = ({
53
54
  disabled,
54
55
  isSubmitting,
@@ -252,7 +253,7 @@ const FormOrganizationConnection = ({
252
253
  onError(error);
253
254
  }
254
255
  };
255
- const connected = Boolean(values == null ? void 0 : values.id) && (values == null ? void 0 : values.status) !== "error";
256
+ const connected = (0, import_connection_controls.isConnected)(values);
256
257
  const inputs = (0, import_react.useMemo)(
257
258
  () => fields.filter((field) => field.input && !(connected && field.secret)),
258
259
  [connected, fields]
@@ -17,6 +17,7 @@ import InputCheckbox from "../input-checkbox.mjs";
17
17
  import InputSwitch from "../shared/input-switch.mjs";
18
18
  import Button from "../shared/button.mjs";
19
19
  import CopyToClipboard from "../shared/copy-to-clipboard.mjs";
20
+ import { isConnected } from "./lib/connection-controls.mjs";
20
21
  const Submit = ({
21
22
  disabled,
22
23
  isSubmitting,
@@ -220,7 +221,7 @@ const FormOrganizationConnection = ({
220
221
  onError(error);
221
222
  }
222
223
  };
223
- const connected = Boolean(values == null ? void 0 : values.id) && (values == null ? void 0 : values.status) !== "error";
224
+ const connected = isConnected(values);
224
225
  const inputs = useMemo(
225
226
  () => fields.filter((field) => field.input && !(connected && field.secret)),
226
227
  [connected, fields]
@@ -17,9 +17,12 @@ var __copyProps = (to, from, except, desc) => {
17
17
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
18
18
  var connection_controls_exports = {};
19
19
  __export(connection_controls_exports, {
20
- connectionControls: () => connectionControls
20
+ connectionControls: () => connectionControls,
21
+ isConnected: () => isConnected
21
22
  });
22
23
  module.exports = __toCommonJS(connection_controls_exports);
24
+ var import_reconnect = require("@drawbridge/drawbridge-utils/connections/reconnect");
25
+ const isConnected = (connection) => Boolean(connection == null ? void 0 : connection.id) && !(0, import_reconnect.requiresReconnect)(connection);
23
26
  const connectionControls = (connection) => {
24
27
  var _a;
25
28
  const paused = Boolean(connection == null ? void 0 : connection.paused);
@@ -30,5 +33,6 @@ const connectionControls = (connection) => {
30
33
  };
31
34
  // Annotate the CommonJS export names for ESM import in node:
32
35
  0 && (module.exports = {
33
- connectionControls
36
+ connectionControls,
37
+ isConnected
34
38
  });
@@ -1,3 +1,5 @@
1
+ import { requiresReconnect } from "@drawbridge/drawbridge-utils/connections/reconnect";
2
+ const isConnected = (connection) => Boolean(connection == null ? void 0 : connection.id) && !requiresReconnect(connection);
1
3
  const connectionControls = (connection) => {
2
4
  var _a;
3
5
  const paused = Boolean(connection == null ? void 0 : connection.paused);
@@ -7,5 +9,6 @@ const connectionControls = (connection) => {
7
9
  };
8
10
  };
9
11
  export {
10
- connectionControls
12
+ connectionControls,
13
+ isConnected
11
14
  };
@@ -29,10 +29,12 @@ var steps_exports = {};
29
29
  __export(steps_exports, {
30
30
  default: () => steps_default,
31
31
  groupTitle: () => groupTitle,
32
+ replyExcerpt: () => replyExcerpt,
32
33
  rowLabel: () => rowLabel
33
34
  });
34
35
  module.exports = __toCommonJS(steps_exports);
35
36
  var import_jsx_runtime = require("react/jsx-runtime");
37
+ var import_react_query = require("@tanstack/react-query");
36
38
  var import_drawbridge_utils = require("@drawbridge/drawbridge-utils");
37
39
  var import_accordion = __toESM(require("./accordion"));
38
40
  var import_paged = __toESM(require("./paged"));
@@ -57,6 +59,14 @@ const indicatorType = (status) => {
57
59
  if (status === "partial" || status === "skipped") return "warning";
58
60
  return "inactive";
59
61
  };
62
+ const replyExcerpt = (response, length = 300) => {
63
+ const wrapped = response && typeof response === "object" && !Array.isArray(response) && Object.keys(response).length === 1 && "body" in response;
64
+ const reply = wrapped ? response.body : response;
65
+ if (reply === void 0 || reply === null) return null;
66
+ const text = (typeof reply === "string" ? reply : JSON.stringify(reply)).replace(/<(script|style)\b[\s\S]*?<\/\1>/gi, " ").replace(/<[^>]*>/g, " ").replace(/\s+/g, " ").trim();
67
+ if (!text) return null;
68
+ return text.length > length ? text.slice(0, length) + "\u2026" : text;
69
+ };
60
70
  const StepItem = ({
61
71
  updatedAt,
62
72
  error: chunkError,
@@ -69,6 +79,7 @@ const StepItem = ({
69
79
  }) => {
70
80
  const error = chunkError || status === "failed" && message || null;
71
81
  const notice = error || status === "skipped" && message || null;
82
+ const reply = error ? replyExcerpt(response) : null;
72
83
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
73
84
  "div",
74
85
  {
@@ -136,13 +147,51 @@ const StepItem = ({
136
147
  }
137
148
  )
138
149
  },
139
- children: notice ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
140
- import_card_task.default,
150
+ children: notice ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
151
+ "div",
141
152
  {
142
153
  ...{
143
- message: notice,
144
- type: error ? "error" : "warning"
145
- }
154
+ className: "space-y-3"
155
+ },
156
+ children: [
157
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
158
+ import_card_task.default,
159
+ {
160
+ ...{
161
+ message: notice,
162
+ type: error ? "error" : "warning"
163
+ }
164
+ }
165
+ ),
166
+ reply && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
167
+ "div",
168
+ {
169
+ ...{
170
+ className: "space-y-1"
171
+ },
172
+ children: [
173
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
174
+ "p",
175
+ {
176
+ ...{
177
+ className: "text-xs text-gray-dark"
178
+ },
179
+ children: "Response"
180
+ }
181
+ ),
182
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
183
+ "p",
184
+ {
185
+ ...{
186
+ className: "text-xs break-words"
187
+ },
188
+ children: reply
189
+ }
190
+ )
191
+ ]
192
+ }
193
+ )
194
+ ]
146
195
  }
147
196
  ) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
148
197
  import_code_formatter.default,
@@ -175,11 +224,15 @@ const groupTitle = ({ count, email, items, kind, pending }) => {
175
224
  const entries = count + " new " + (count === 1 ? "entry" : "entries");
176
225
  return "Team notification \u2014 " + entries + (pending ? ", sending" : "");
177
226
  };
178
- const rowLabel = ({ item, kind }) => {
179
- if (kind !== "notify") return item == null ? void 0 : item.slug;
227
+ const rowLabel = ({ item, kind, labels = [] }) => {
228
+ var _a;
229
+ if (kind !== "notify") return ((_a = labels.find((option) => option.value === (item == null ? void 0 : item.slug))) == null ? void 0 : _a.key) || (item == null ? void 0 : item.slug);
180
230
  return NOTIFY_LABELS[item == null ? void 0 : item.status] || NOTIFY_LABELS.succeeded;
181
231
  };
182
- const Group = ({ group }) => {
232
+ const Group = ({
233
+ group,
234
+ labels
235
+ }) => {
183
236
  const { items, kind } = group;
184
237
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
185
238
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -206,7 +259,7 @@ const Group = ({ group }) => {
206
259
  {
207
260
  ...{
208
261
  ...item,
209
- label: rowLabel({ item, kind })
262
+ label: rowLabel({ item, kind, labels })
210
263
  }
211
264
  },
212
265
  key
@@ -220,6 +273,8 @@ const Steps = ({
220
273
  limit = 24,
221
274
  organization
222
275
  }) => {
276
+ var _a, _b, _c, _d;
277
+ const labels = (_d = (_c = (_b = (_a = (0, import_react_query.useQueryClient)().getQueryData(["app"])) == null ? void 0 : _a.enums) == null ? void 0 : _b.step) == null ? void 0 : _c.type) == null ? void 0 : _d.options;
223
278
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
224
279
  import_paged.default,
225
280
  {
@@ -245,7 +300,8 @@ const Steps = ({
245
300
  Group,
246
301
  {
247
302
  ...{
248
- group
303
+ group,
304
+ labels
249
305
  }
250
306
  },
251
307
  key
@@ -259,5 +315,6 @@ var steps_default = Steps;
259
315
  // Annotate the CommonJS export names for ESM import in node:
260
316
  0 && (module.exports = {
261
317
  groupTitle,
318
+ replyExcerpt,
262
319
  rowLabel
263
320
  });