@protobase/ui 0.19.8 → 0.19.10

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 (85) hide show
  1. package/dist/app/app.js +4 -4
  2. package/dist/app/create/create-offered.js +9 -0
  3. package/dist/app/create/create-page.js +4 -4
  4. package/dist/app/create/create-record-form.js +26 -62
  5. package/dist/app/create/create-route.js +29 -0
  6. package/dist/app/create/form.d.ts +30 -0
  7. package/dist/app/create/form.js +72 -0
  8. package/dist/app/custom-section.js +1 -1
  9. package/dist/app/displays/map-display.js +1 -1
  10. package/dist/app/displays/record-card.js +18 -4
  11. package/dist/app/displays/record-link.js +1 -1
  12. package/dist/app/displays/use-map-records.js +2 -2
  13. package/dist/app/embedded-list/embedded-columns.js +2 -2
  14. package/dist/app/embedded-list/embedded-list.js +5 -5
  15. package/dist/app/embedded-list/use-embedded-rows.js +2 -2
  16. package/dist/app/field-editor.js +21 -5
  17. package/dist/app/gallery/gallery-section.js +1 -1
  18. package/dist/app/gallery/photo-details.js +2 -2
  19. package/dist/app/list-page.js +18 -17
  20. package/dist/app/pages/blocks/card-row.js +2 -2
  21. package/dist/app/pages/blocks/field.js +2 -2
  22. package/dist/app/pages/blocks/link.js +1 -1
  23. package/dist/app/pages/blocks/modal-form.js +6 -6
  24. package/dist/app/pages/blocks/progress.js +1 -1
  25. package/dist/app/pages/blocks/record-card.js +2 -2
  26. package/dist/app/pages/blocks/record-row.js +1 -1
  27. package/dist/app/pages/blocks/show.js +2 -2
  28. package/dist/app/pages/blocks/stat.js +3 -3
  29. package/dist/app/pages/blocks/table.js +1 -1
  30. package/dist/app/pages/field-value.js +5 -2
  31. package/dist/app/pages/record-actions.js +2 -2
  32. package/dist/app/pages/use-run-action.js +2 -2
  33. package/dist/app/record-page.js +15 -11
  34. package/dist/app/record-row/record-row-body.js +2 -2
  35. package/dist/app/record-row/saved-record-row.js +1 -1
  36. package/dist/app/related-section.js +1 -1
  37. package/dist/app/searches/saved-search-rows.js +1 -1
  38. package/dist/app/searches/saved-search-section.js +2 -2
  39. package/dist/app/searches/saved-searches.js +2 -2
  40. package/dist/app/searches/search-controls.js +1 -1
  41. package/dist/app/searches/search-name-dialog.js +1 -1
  42. package/dist/app/searches/update-search-dialog.js +2 -2
  43. package/dist/app/searches/use-filter-chips.js +1 -1
  44. package/dist/app/searches/use-save-search.js +2 -2
  45. package/dist/app/searches/use-saved-searches.js +1 -1
  46. package/dist/app/searches/use-search-row.js +1 -1
  47. package/dist/app/sensitive-field.js +1 -1
  48. package/dist/app/use-app-address.js +1 -1
  49. package/dist/app/value-badge.js +1 -1
  50. package/dist/auth/sign-in-prompt.js +1 -1
  51. package/dist/data-grid/cells.js +1 -1
  52. package/dist/index.d.ts +9 -1
  53. package/dist/index.js +10 -2
  54. package/dist/live/columns-from-model.js +3 -0
  55. package/dist/live/grid-loader.js +1 -1
  56. package/dist/live/use-filter-config.js +1 -1
  57. package/dist/markdown/markdown-content.js +18 -0
  58. package/dist/markdown/markdown-editor.js +46 -0
  59. package/dist/markdown/markdown-excerpt.js +36 -0
  60. package/dist/markdown/markdown-hint.js +5 -0
  61. package/dist/markdown/markdown-html.js +59 -0
  62. package/dist/markdown/markdown.js +15 -0
  63. package/dist/package.js +1 -1
  64. package/dist/primitives/choices.d.ts +22 -0
  65. package/dist/primitives/choices.js +33 -0
  66. package/dist/primitives/dialog.d.ts +15 -0
  67. package/dist/primitives/menu-select.d.ts +14 -0
  68. package/dist/primitives/segmented.d.ts +23 -0
  69. package/dist/primitives/textarea.d.ts +9 -0
  70. package/dist/record-view/field.d.ts +27 -0
  71. package/dist/record-view/section.d.ts +10 -0
  72. package/dist/shell/assets/app-GKl91FLS.js +69 -0
  73. package/dist/shell/assets/{index-cMmFPuUQ.js → index-BB2BN-6s.js} +1 -1
  74. package/dist/shell/assets/index-DxV5Rp_F.css +2 -0
  75. package/dist/shell/assets/{maplibre-9XYrB9k6.js → maplibre-DHotMteL.js} +2 -2
  76. package/dist/shell/assets/markdown-content-C4zE1qZp.js +67 -0
  77. package/dist/shell/assets/{record-map-OBtKCiUB.js → record-map-BBbVY6vn.js} +1 -1
  78. package/dist/shell/assets/{records-map-C4oMPL5C.js → records-map-ur6HR-I5.js} +1 -1
  79. package/dist/shell/assets/shared-protobase-ui-D7kkYiTL.js +1 -0
  80. package/dist/shell/index.html +4 -4
  81. package/dist/shell/protobase.shell.json +1 -1
  82. package/package.json +6 -4
  83. package/dist/shell/assets/app-D8i5r_6U.js +0 -68
  84. package/dist/shell/assets/index-Cuulz26c.css +0 -2
  85. package/dist/shell/assets/shared-protobase-ui-CN1HmyDa.js +0 -1
@@ -1,5 +1,5 @@
1
- import { AppLink } from "../../use-app-address.js";
2
1
  import { useLayoutRecord } from "../record-scope.js";
2
+ import { AppLink } from "../../use-app-address.js";
3
3
  import { propsOf } from "./block-props.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  import { appPath } from "@protobase/schema";
@@ -4,15 +4,15 @@ import { useClient } from "../../../data/api-provider.js";
4
4
  import { humanize } from "../../../live/naming.js";
5
5
  import { useToast } from "../../../toasts/toast-provider.js";
6
6
  import { useAdminMeta, usePermissions } from "../../meta-gate.js";
7
- import { differs, toDraft, toPatchValue } from "../../../live/field-values.js";
8
- import { ErrorBanner } from "../../error-banner.js";
9
- import { FieldEditor } from "../../field-editor.js";
7
+ import { useLayoutRecord } from "../record-scope.js";
10
8
  import { can } from "../../permissions.js";
11
- import { Field } from "../../../record-view/field.js";
12
- import { createBody, isChosen } from "../../create/create-values.js";
13
9
  import { useFirstRecord, useRefreshResource } from "../use-layout-data.js";
14
- import { useLayoutRecord } from "../record-scope.js";
15
10
  import { propsOf } from "./block-props.js";
11
+ import { ErrorBanner } from "../../error-banner.js";
12
+ import { differs, toDraft, toPatchValue } from "../../../live/field-values.js";
13
+ import { Field } from "../../../record-view/field.js";
14
+ import { createBody, isChosen } from "../../create/create-values.js";
15
+ import { FieldEditor } from "../../field-editor.js";
16
16
  import { useState } from "react";
17
17
  import { isApiError } from "@protobase/client";
18
18
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -1,7 +1,7 @@
1
1
  import { Skeleton } from "../../../primitives/skeleton.js";
2
- import { usePathValue } from "../use-path-value.js";
3
2
  import { useLayoutRecord } from "../record-scope.js";
4
3
  import { propsOf } from "./block-props.js";
4
+ import { usePathValue } from "../use-path-value.js";
5
5
  import { jsx, jsxs } from "react/jsx-runtime";
6
6
  //#region src/app/pages/blocks/progress.tsx
7
7
  /** A number as given, or the number in a field of the record around it. */
@@ -1,10 +1,10 @@
1
1
  import { Skeleton } from "../../../primitives/skeleton.js";
2
2
  import { useAdminMeta } from "../../meta-gate.js";
3
- import { ErrorBanner } from "../../error-banner.js";
4
- import { useFirstRecord } from "../use-layout-data.js";
5
3
  import { RecordScope } from "../record-scope.js";
4
+ import { useFirstRecord } from "../use-layout-data.js";
6
5
  import { propsOf } from "./block-props.js";
7
6
  import { CardFrame, Quiet } from "./card-frame.js";
7
+ import { ErrorBanner } from "../../error-banner.js";
8
8
  import { jsx, jsxs } from "react/jsx-runtime";
9
9
  //#region src/app/pages/blocks/record-card.tsx
10
10
  const LoadedRecordCard = ({ model, filter, sort, recordKey, title, description, empty, actions, children }) => {
@@ -1,7 +1,7 @@
1
1
  import { useAdminMeta } from "../../meta-gate.js";
2
- import { RecordRowBody } from "../../record-row/record-row-body.js";
3
2
  import { propsOf } from "./block-props.js";
4
3
  import { CardFrame } from "./card-frame.js";
4
+ import { RecordRowBody } from "../../record-row/record-row-body.js";
5
5
  import { SavedRecordRow } from "../../record-row/saved-record-row.js";
6
6
  import { SavedSearchRows } from "../../searches/saved-search-rows.js";
7
7
  import { jsx } from "react/jsx-runtime";
@@ -1,9 +1,9 @@
1
1
  import { useClient } from "../../../data/api-provider.js";
2
2
  import { useAdminMeta } from "../../meta-gate.js";
3
- import { countQuery } from "../use-layout-data.js";
4
- import { usePathValues } from "../use-path-value.js";
5
3
  import { useLayoutRecord } from "../record-scope.js";
4
+ import { countQuery } from "../use-layout-data.js";
6
5
  import { propsOf } from "./block-props.js";
6
+ import { usePathValues } from "../use-path-value.js";
7
7
  import { useMemo } from "react";
8
8
  import { Fragment as Fragment$1, jsx } from "react/jsx-runtime";
9
9
  import { useQueries } from "@tanstack/react-query";
@@ -1,12 +1,12 @@
1
1
  import { Skeleton } from "../../../primitives/skeleton.js";
2
2
  import { useAdminMeta } from "../../meta-gate.js";
3
+ import { useLayoutRecord } from "../record-scope.js";
3
4
  import { AppLink } from "../../use-app-address.js";
4
5
  import { useCount } from "../use-layout-data.js";
5
- import { FieldValue } from "../field-value.js";
6
- import { usePathValue } from "../use-path-value.js";
7
- import { useLayoutRecord } from "../record-scope.js";
8
6
  import { propsOf } from "./block-props.js";
9
7
  import { CardFrame } from "./card-frame.js";
8
+ import { FieldValue } from "../field-value.js";
9
+ import { usePathValue } from "../use-path-value.js";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
11
11
  //#region src/app/pages/blocks/stat.tsx
12
12
  const big = "text-2xl font-semibold tracking-tight tabular-nums";
@@ -1,7 +1,7 @@
1
1
  import { useAdminMeta } from "../../meta-gate.js";
2
- import { EmbeddedDisplaySwitch, EmbeddedList } from "../../embedded-list/embedded-list.js";
3
2
  import { propsOf } from "./block-props.js";
4
3
  import { CardFrame } from "./card-frame.js";
4
+ import { EmbeddedDisplaySwitch, EmbeddedList } from "../../embedded-list/embedded-list.js";
5
5
  import { useEmbeddedDisplay } from "../../embedded-list/use-embedded-display.js";
6
6
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/app/pages/blocks/table.tsx
@@ -1,15 +1,17 @@
1
1
  import { cn } from "../../lib/cn.js";
2
2
  import { keys } from "../../data/query-keys.js";
3
3
  import { useClient } from "../../data/api-provider.js";
4
- import { fetchLabels } from "../../live/relation-labels.js";
4
+ import { useAdminMeta } from "../meta-gate.js";
5
5
  import { fileShown, shownImage } from "../../live/file-values.js";
6
6
  import { displayValue } from "../../live/display-value.js";
7
- import { useAdminMeta } from "../meta-gate.js";
8
7
  import { AppLink } from "../use-app-address.js";
9
8
  import { FileImage } from "../../live/file-image.js";
9
+ import { fetchLabels } from "../../live/relation-labels.js";
10
10
  import { useShownWidget } from "../field-types/shown-widget.js";
11
11
  import { WidgetPart } from "../field-types/widget-part.js";
12
+ import { isMarkdown } from "../../markdown/markdown-hint.js";
12
13
  import { ValueBadge } from "../value-badge.js";
14
+ import { Markdown } from "../../markdown/markdown.js";
13
15
  import { jsx, jsxs } from "react/jsx-runtime";
14
16
  import { useQuery, useQueryClient } from "@tanstack/react-query";
15
17
  //#region src/app/pages/field-value.tsx
@@ -70,6 +72,7 @@ const BuiltInValue = ({ model, field, value, format, relationLabel }) => {
70
72
  })]
71
73
  });
72
74
  }
75
+ if (isMarkdown(field, hints)) return /* @__PURE__ */ jsx(Markdown, { source: String(value) });
73
76
  const text = displayValue(field, value, hints);
74
77
  if (field.type === "enum" || hints.format === "badge") return /* @__PURE__ */ jsx(ValueBadge, {
75
78
  value,
@@ -1,7 +1,7 @@
1
- import { AppLink, useAppAddress } from "../use-app-address.js";
2
- import { ActionRail } from "../../record-view/action-rail.js";
3
1
  import { ConfirmAction } from "./confirm-action.js";
2
+ import { AppLink, useAppAddress } from "../use-app-address.js";
4
3
  import { useActionRunner } from "./use-run-action.js";
4
+ import { ActionRail } from "../../record-view/action-rail.js";
5
5
  import { actionIcon } from "../action-icon.js";
6
6
  import { useState } from "react";
7
7
  import { Pencil, Trash2 } from "lucide-react";
@@ -2,10 +2,10 @@ import { useClient, useResourceClient } from "../../data/api-provider.js";
2
2
  import { useToast } from "../../toasts/toast-provider.js";
3
3
  import { useProjectUi } from "./project-ui.js";
4
4
  import { useAdminMeta, usePermissions } from "../meta-gate.js";
5
- import { useAppAddress } from "../use-app-address.js";
6
5
  import { can } from "../permissions.js";
7
- import { useRefreshResource } from "./use-layout-data.js";
6
+ import { useAppAddress } from "../use-app-address.js";
8
7
  import { actionFor, linkTarget, openLink } from "./action-href.js";
8
+ import { useRefreshResource } from "./use-layout-data.js";
9
9
  import { useState } from "react";
10
10
  import { fillAddress } from "@protobase/schema/internal";
11
11
  //#region src/app/pages/use-run-action.ts
@@ -9,27 +9,29 @@ import { useRecord } from "../data/use-record.js";
9
9
  import { recordTitle } from "../live/model-helpers.js";
10
10
  import { humanize } from "../live/naming.js";
11
11
  import { useProjectUi } from "./pages/project-ui.js";
12
+ import { Notice, useAdminMeta, usePermissions } from "./meta-gate.js";
13
+ import { can, fieldAccess } from "./permissions.js";
12
14
  import { displayValue } from "../live/display-value.js";
13
15
  import { filledFields } from "./filled-fields.js";
14
- import { Notice, useAdminMeta, usePermissions } from "./meta-gate.js";
15
16
  import { AppLink, useAppAddress } from "./use-app-address.js";
16
17
  import { ErrorBanner } from "./error-banner.js";
17
18
  import { shownWidget } from "./field-types/shown-widget.js";
18
19
  import { WidgetPart } from "./field-types/widget-part.js";
19
- import { FieldEditor } from "./field-editor.js";
20
- import { can, fieldAccess } from "./permissions.js";
20
+ import { isMarkdown } from "../markdown/markdown-hint.js";
21
+ import { toneFor } from "../live/columns-from-model.js";
22
+ import { ValueBadge } from "./value-badge.js";
23
+ import { Markdown } from "../markdown/markdown.js";
24
+ import { FieldValue } from "./pages/field-value.js";
21
25
  import { Field } from "../record-view/field.js";
22
26
  import { Section } from "../record-view/section.js";
23
27
  import { MetadataCard, RelatedRecords, SidebarCard } from "../record-view/record-sidebar.js";
24
28
  import { RecordHeader } from "../record-view/record-header.js";
25
29
  import { SpecTable } from "../record-view/spec-table.js";
26
30
  import { KeyFacts } from "../record-view/key-facts.js";
27
- import { toneFor } from "../live/columns-from-model.js";
31
+ import { FieldEditor } from "./field-editor.js";
32
+ import { namedIcon } from "./named-icon.js";
28
33
  import { useDeleteFlow } from "./delete/use-delete-flow.js";
29
34
  import { useRestore } from "./delete/use-restore.js";
30
- import { namedIcon } from "./named-icon.js";
31
- import { ValueBadge } from "./value-badge.js";
32
- import { FieldValue } from "./pages/field-value.js";
33
35
  import { auditField } from "../live/audit-fields.js";
34
36
  import { CustomSection } from "./custom-section.js";
35
37
  import { MapSection } from "./map/map-section.js";
@@ -164,7 +166,7 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
164
166
  label: hint(name)?.label ?? humanize(name),
165
167
  help: fieldHelp(hint(name), view, editing),
166
168
  error: editor.errors[name],
167
- wide: Boolean(hint(name)?.multiline),
169
+ wide: Boolean(hint(name)?.multiline || hint(name)?.markdown),
168
170
  conflict: conflict === void 0 ? void 0 : {
169
171
  user: {
170
172
  name: "Someone else",
@@ -211,7 +213,7 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
211
213
  return {
212
214
  label: hint(name)?.label ?? humanize(name),
213
215
  help: fieldHelp(hint(name), view, editing),
214
- wide: Boolean(hint(name)?.multiline),
216
+ wide: Boolean(hint(name)?.multiline || hint(name)?.markdown),
215
217
  value
216
218
  };
217
219
  };
@@ -222,11 +224,13 @@ const LoadedRecord = ({ model, recordKey, stored }) => {
222
224
  const field = model.fields[name];
223
225
  if (!field || !visible(name)) return [];
224
226
  const text = displayValue(field, stored.record[name], hint(name));
227
+ const value = stored.record[name];
228
+ const markdown = isMarkdown(field, hint(name)) && typeof value === "string" && value !== "";
225
229
  const builtIn = field.type === "enum" ? /* @__PURE__ */ jsx(ValueBadge, {
226
- value: stored.record[name],
230
+ value,
227
231
  hints: hint(name),
228
232
  children: text
229
- }) : /* @__PURE__ */ jsx("strong", {
233
+ }) : markdown ? /* @__PURE__ */ jsx(Markdown, { source: value }) : /* @__PURE__ */ jsx("strong", {
230
234
  className: hint(name)?.multiline ? "whitespace-pre-line break-words" : void 0,
231
235
  children: text
232
236
  });
@@ -1,14 +1,14 @@
1
1
  import { useIsXs } from "../../lib/use-media-query.js";
2
2
  import { Skeleton } from "../../primitives/skeleton.js";
3
3
  import { humanize } from "../../live/naming.js";
4
+ import { useAdminMeta } from "../meta-gate.js";
4
5
  import { displaysOf, loadedFields } from "../displays/list-displays.js";
5
6
  import { defaultOrderBy } from "../list-order.js";
6
- import { useAdminMeta } from "../meta-gate.js";
7
7
  import { AppLink } from "../use-app-address.js";
8
8
  import { ErrorBanner } from "../error-banner.js";
9
9
  import { useLiveRows } from "../../live/use-live-rows.js";
10
- import { FilterChipList } from "../searches/filter-chip-list.js";
11
10
  import { viewAllAddress } from "../embedded-list/embedded-display.js";
11
+ import { FilterChipList } from "../searches/filter-chip-list.js";
12
12
  import { useFilterChips } from "../searches/use-filter-chips.js";
13
13
  import { RecordRow } from "./record-row.js";
14
14
  import { useMemo } from "react";
@@ -1,7 +1,7 @@
1
1
  import { Skeleton } from "../../primitives/skeleton.js";
2
2
  import { humanize } from "../../live/naming.js";
3
- import { listedResource } from "../current-address.js";
4
3
  import { useAdminMeta } from "../meta-gate.js";
4
+ import { listedResource } from "../current-address.js";
5
5
  import { ErrorBanner } from "../error-banner.js";
6
6
  import { savedScope } from "../saved/saved-records.js";
7
7
  import { useSavedRecords } from "../saved/use-saved-records.js";
@@ -3,8 +3,8 @@ import { keys } from "../data/query-keys.js";
3
3
  import { useClient } from "../data/api-provider.js";
4
4
  import { useAdminMeta } from "./meta-gate.js";
5
5
  import { ErrorBanner } from "./error-banner.js";
6
- import { Section } from "../record-view/section.js";
7
6
  import { listColumnNames } from "../live/columns-from-model.js";
7
+ import { Section } from "../record-view/section.js";
8
8
  import { EmbeddedDisplaySwitch, EmbeddedList } from "./embedded-list/embedded-list.js";
9
9
  import { useEmbeddedDisplay } from "./embedded-list/use-embedded-display.js";
10
10
  import { jsx } from "react/jsx-runtime";
@@ -5,8 +5,8 @@ import { useAdminMeta } from "../meta-gate.js";
5
5
  import { AppLink } from "../use-app-address.js";
6
6
  import { ErrorBanner } from "../error-banner.js";
7
7
  import { InlineFilterChips } from "./filter-chip-list.js";
8
- import { useSavedSearches } from "./use-saved-searches.js";
9
8
  import { RecordRowBody } from "../record-row/record-row-body.js";
9
+ import { useSavedSearches } from "./use-saved-searches.js";
10
10
  import { useSearchRow } from "./use-search-row.js";
11
11
  import { jsx, jsxs } from "react/jsx-runtime";
12
12
  import { encodeKey } from "@protobase/schema/internal";
@@ -5,10 +5,10 @@ import { useToast } from "../../toasts/toast-provider.js";
5
5
  import { AppLink } from "../use-app-address.js";
6
6
  import { useRefreshResource } from "../pages/use-layout-data.js";
7
7
  import { InlineFilterChips } from "./filter-chip-list.js";
8
- import { SearchNameDialog } from "./search-name-dialog.js";
9
8
  import { RecordRowBody } from "../record-row/record-row-body.js";
10
- import { SearchControls } from "./search-controls.js";
11
9
  import { useSearchRow } from "./use-search-row.js";
10
+ import { SearchNameDialog } from "./search-name-dialog.js";
11
+ import { SearchControls } from "./search-controls.js";
12
12
  import { useState } from "react";
13
13
  import { Pencil, Trash2 } from "lucide-react";
14
14
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -3,11 +3,11 @@ import { PageHeader } from "../../app-shell/page-header.js";
3
3
  import { recordKey } from "../../live/model-helpers.js";
4
4
  import { humanize } from "../../live/naming.js";
5
5
  import { Notice, useAdminMeta, usePermissions } from "../meta-gate.js";
6
+ import { can } from "../permissions.js";
6
7
  import { AppLink } from "../use-app-address.js";
7
8
  import { ErrorBanner } from "../error-banner.js";
8
- import { can } from "../permissions.js";
9
- import { useDeleteFlow } from "../delete/use-delete-flow.js";
10
9
  import { useSavedSearches } from "./use-saved-searches.js";
10
+ import { useDeleteFlow } from "../delete/use-delete-flow.js";
11
11
  import { SavedSearchSection } from "./saved-search-section.js";
12
12
  import { jsx, jsxs } from "react/jsx-runtime";
13
13
  import { encodeKey } from "@protobase/schema/internal";
@@ -1,8 +1,8 @@
1
1
  import { useClient } from "../../data/api-provider.js";
2
2
  import { humanize } from "../../live/naming.js";
3
3
  import { useToast } from "../../toasts/toast-provider.js";
4
- import { Switch } from "../../primitives/switch.js";
5
4
  import { useRefreshResource } from "../pages/use-layout-data.js";
5
+ import { Switch } from "../../primitives/switch.js";
6
6
  import { useId } from "react";
7
7
  import { jsx, jsxs } from "react/jsx-runtime";
8
8
  import { useMutation } from "@tanstack/react-query";
@@ -1,7 +1,7 @@
1
1
  import { Input } from "../../primitives/input.js";
2
2
  import { Button } from "../../primitives/button.js";
3
- import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
4
3
  import { FilterChipList } from "./filter-chip-list.js";
4
+ import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
5
5
  import { useId, useState } from "react";
6
6
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
7
7
  //#region src/app/searches/search-name-dialog.tsx
@@ -3,11 +3,11 @@ import { Button } from "../../primitives/button.js";
3
3
  import { useClient } from "../../data/api-provider.js";
4
4
  import { humanize } from "../../live/naming.js";
5
5
  import { defaultOrderBy } from "../list-order.js";
6
- import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
7
6
  import { countQuery } from "../pages/use-layout-data.js";
7
+ import { useFilterChips } from "./use-filter-chips.js";
8
+ import { ResponsiveDialog } from "../../primitives/responsive-dialog.js";
8
9
  import { EmbeddedList } from "../embedded-list/embedded-list.js";
9
10
  import { addedFilter, filterChanges, netChange, removedFilter } from "./search-diff.js";
10
- import { useFilterChips } from "./use-filter-chips.js";
11
11
  import { useMemo } from "react";
12
12
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
13
  import { useQuery } from "@tanstack/react-query";
@@ -1,7 +1,7 @@
1
1
  import { keys } from "../../data/query-keys.js";
2
2
  import { useClient } from "../../data/api-provider.js";
3
- import { fetchLabels } from "../../live/relation-labels.js";
4
3
  import { useAdminMeta } from "../meta-gate.js";
4
+ import { fetchLabels } from "../../live/relation-labels.js";
5
5
  import { chosenRelations, filterChips } from "./saved-search.js";
6
6
  import { useQueries, useQueryClient } from "@tanstack/react-query";
7
7
  //#region src/app/searches/use-filter-chips.ts
@@ -3,11 +3,11 @@ import { recordKey } from "../../live/model-helpers.js";
3
3
  import { humanize } from "../../live/naming.js";
4
4
  import { useToast } from "../../toasts/toast-provider.js";
5
5
  import { useAdminMeta } from "../meta-gate.js";
6
- import { useAppAddress } from "../use-app-address.js";
7
6
  import { can } from "../permissions.js";
7
+ import { useAppAddress } from "../use-app-address.js";
8
8
  import { useRefreshResource } from "../pages/use-layout-data.js";
9
- import { useOpenedSearch } from "./use-opened-search.js";
10
9
  import { useSavedSearches } from "./use-saved-searches.js";
10
+ import { useOpenedSearch } from "./use-opened-search.js";
11
11
  import { useMutation } from "@tanstack/react-query";
12
12
  import { encodeKey } from "@protobase/schema/internal";
13
13
  //#region src/app/searches/use-save-search.ts
@@ -1,7 +1,7 @@
1
1
  import { keys } from "../../data/query-keys.js";
2
2
  import { useClient } from "../../data/api-provider.js";
3
- import { defaultOrderBy } from "../list-order.js";
4
3
  import { useAdminMeta } from "../meta-gate.js";
4
+ import { defaultOrderBy } from "../list-order.js";
5
5
  import { useQuery } from "@tanstack/react-query";
6
6
  //#region src/app/searches/use-saved-searches.ts
7
7
  /** Where the user's saved searches of `resource` are cached; a list key, so a write to the resource refreshes it. */
@@ -1,8 +1,8 @@
1
1
  import { useClient } from "../../data/api-provider.js";
2
2
  import { humanize } from "../../live/naming.js";
3
3
  import { countQuery, useRefreshResource } from "../pages/use-layout-data.js";
4
- import { isUnseen, unseenFilter } from "./saved-search.js";
5
4
  import { chipsText } from "./filter-chip-list.js";
5
+ import { isUnseen, unseenFilter } from "./saved-search.js";
6
6
  import { useFilterChips } from "./use-filter-chips.js";
7
7
  import { useMutation, useQuery } from "@tanstack/react-query";
8
8
  //#region src/app/searches/use-search-row.ts
@@ -3,8 +3,8 @@ import { Input } from "../primitives/input.js";
3
3
  import { Button } from "../primitives/button.js";
4
4
  import { humanize } from "../live/naming.js";
5
5
  import { displayValue } from "../live/display-value.js";
6
- import { toDraft } from "../live/field-values.js";
7
6
  import { ErrorBanner } from "./error-banner.js";
7
+ import { toDraft } from "../live/field-values.js";
8
8
  import { useState } from "react";
9
9
  import { Eye, EyeOff } from "lucide-react";
10
10
  import { jsx, jsxs } from "react/jsx-runtime";
@@ -1,7 +1,7 @@
1
1
  import { useIsXs } from "../lib/use-media-query.js";
2
2
  import { Link, useRouter } from "./router.js";
3
- import { changedList, currentAddress, listDefaults, listedResource } from "./current-address.js";
4
3
  import { useAdminMeta } from "./meta-gate.js";
4
+ import { changedList, currentAddress, listDefaults, listedResource } from "./current-address.js";
5
5
  import { useCallback, useMemo } from "react";
6
6
  import { jsx } from "react/jsx-runtime";
7
7
  import { appPath, readAppPath } from "@protobase/schema";
@@ -1,6 +1,6 @@
1
1
  import { Badge } from "../primitives/badge.js";
2
- import { valueColor } from "../live/value-color.js";
3
2
  import { toneFor } from "../live/columns-from-model.js";
3
+ import { valueColor } from "../live/value-color.js";
4
4
  import { jsx } from "react/jsx-runtime";
5
5
  //#region src/app/value-badge.tsx
6
6
  /** A stored value as a badge: filled with the colour the view gives it and in its badge shape, else in its tone. */
@@ -2,8 +2,8 @@ import { RouteLink } from "../primitives/route-link.js";
2
2
  import { useAuth } from "./auth-provider.js";
3
3
  import { Button, buttonClass } from "../primitives/button.js";
4
4
  import { useSignInAddress } from "./use-sign-in-address.js";
5
- import { ResponsiveDialog } from "../primitives/responsive-dialog.js";
6
5
  import { namedIcon } from "../app/named-icon.js";
6
+ import { ResponsiveDialog } from "../primitives/responsive-dialog.js";
7
7
  import { useId } from "react";
8
8
  import { Check, LogIn } from "lucide-react";
9
9
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
@@ -6,8 +6,8 @@ import { formatInt, formatPercent, formatSigned } from "../format/number.js";
6
6
  import { formatDate, formatDateTime } from "../format/date.js";
7
7
  import { fileShown, shownImage } from "../live/file-values.js";
8
8
  import { FileImage } from "../live/file-image.js";
9
- import { userById } from "../mocks/users.js";
10
9
  import { valueColor } from "../live/value-color.js";
10
+ import { userById } from "../mocks/users.js";
11
11
  import { Check, Paperclip } from "lucide-react";
12
12
  import { jsx, jsxs } from "react/jsx-runtime";
13
13
  import { enumLabel } from "@protobase/schema";
package/dist/index.d.ts CHANGED
@@ -48,5 +48,13 @@ import { CodeForm, CodeFormProps } from "./auth/code-form.js";
48
48
  import { ResendCodeButton, ResendCodeButtonProps } from "./auth/resend-code-button.js";
49
49
  import { Button, ButtonProps, buttonClass } from "./primitives/button.js";
50
50
  import { Input, InputProps } from "./primitives/input.js";
51
+ import { Textarea, TextareaProps } from "./primitives/textarea.js";
52
+ import { Segmented, SegmentedOption, SegmentedProps } from "./primitives/segmented.js";
53
+ import { MenuSelect, MenuSelectProps } from "./primitives/menu-select.js";
54
+ import { ChoiceOption, Choices, ChoicesProps } from "./primitives/choices.js";
55
+ import { Dialog, DialogProps } from "./primitives/dialog.js";
56
+ import { Field, FieldProps } from "./record-view/field.js";
57
+ import { Section } from "./record-view/section.js";
58
+ import { Form, FormProps } from "./app/create/form.js";
51
59
  import { Spinner } from "./primitives/spinner.js";
52
- export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, type DiffLine, DiffView, type DiffViewProps, FieldList, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type RecordCardProps, RecordLink, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
60
+ export { ActionCard, type ActionCardProps, type ActionContext, type ActionHandler, ApiProvider, type ApiProviderProps, App, AppLink, type AppProps, AssistantDock, type AssistantDockProps, AuthLayout, type AuthLayoutProps, type BackendData, type BackendRefresh, type BeforeSignInProps, Button, type ButtonProps, type CardAction, type CardTone, ChatMessage, ChatThread, type ChatThreadProps, type ChoiceOption, Choices, type ChoicesProps, CodeForm, type CodeFormProps, CompactTable, type CompactTableProps, Composer, type ComposerProps, type CurrentAddress, Dialog, type DialogProps, type DiffLine, DiffView, type DiffViewProps, Field, FieldList, type FieldProps, type FieldWidget, type FieldWidgetEditorProps, type FieldWidgetProps, Form, type FormProps, Input, type InputProps, type LayoutRecord, type ListAddress, type ListChange, type ListDisplay, type ListDisplayRow, LogoMark, MenuSelect, type MenuSelectProps, type MetaData, OtpInput, type OtpInputProps, PartView, type PluginInfo, type ProjectUi, type RecordCardProps, RecordLink, ResendCodeButton, type ResendCodeButtonProps, ResponsiveDialog, type ResponsiveDialogProps, Section, Segmented, type SegmentedOption, type SegmentedProps, type ShellLogoProps, SidePanel, type SidePanelProps, SignInPrompt, type SignInPromptProps, Spinner, type Step, StepList, type StepState, Textarea, type TextareaProps, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { LogoMark } from "./app-shell/logo.js";
2
+ import { Segmented } from "./primitives/segmented.js";
2
3
  import { Input } from "./primitives/input.js";
3
4
  import { Spinner } from "./primitives/spinner.js";
4
5
  import { Button, buttonClass } from "./primitives/button.js";
6
+ import { Dialog } from "./primitives/dialog.js";
5
7
  import { OtpInput } from "./primitives/otp-input.js";
6
8
  import { ApiProvider, useClient, useResourceClient } from "./data/api-provider.js";
7
9
  import { AuthLayout } from "./auth/auth-layout.js";
@@ -30,14 +32,20 @@ import { AssistantDock } from "./assistant-dock/assistant-dock.js";
30
32
  import { useAssistant } from "./assistant-dock/use-assistant.js";
31
33
  import { useAssistantClient } from "./assistant-dock/use-assistant-client.js";
32
34
  import { useMeta } from "./data/use-meta.js";
35
+ import { useLayoutRecord } from "./app/pages/record-scope.js";
33
36
  import { AppLink, useAppAddress } from "./app/use-app-address.js";
37
+ import { Field } from "./record-view/field.js";
38
+ import { Section } from "./record-view/section.js";
39
+ import { Textarea } from "./primitives/textarea.js";
34
40
  import { ResponsiveDialog } from "./primitives/responsive-dialog.js";
35
41
  import { SignInPrompt } from "./auth/sign-in-prompt.js";
36
42
  import { useListDisplay } from "./app/displays/display-context.js";
43
+ import { MenuSelect } from "./primitives/menu-select.js";
44
+ import { Form } from "./app/create/form.js";
37
45
  import { useFacets, useHistogram, useSeries } from "./data/use-aggregates.js";
38
- import { useLayoutRecord } from "./app/pages/record-scope.js";
39
46
  import { useList } from "./data/use-list.js";
40
47
  import { isConflict, useUpdateRecord } from "./data/use-update-record.js";
41
48
  import { App } from "./app/app.js";
42
49
  import { RecordLink } from "./app/displays/record-link.js";
43
- export { ActionCard, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, ChatMessage, ChatThread, CodeForm, CompactTable, Composer, DiffView, FieldList, Input, LogoMark, OtpInput, PartView, RecordLink, ResendCodeButton, ResponsiveDialog, SidePanel, SignInPrompt, Spinner, StepList, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
50
+ import { Choices } from "./primitives/choices.js";
51
+ export { ActionCard, ApiProvider, App, AppLink, AssistantDock, AuthLayout, Button, ChatMessage, ChatThread, Choices, CodeForm, CompactTable, Composer, Dialog, DiffView, Field, FieldList, Form, Input, LogoMark, MenuSelect, OtpInput, PartView, RecordLink, ResendCodeButton, ResponsiveDialog, Section, Segmented, SidePanel, SignInPrompt, Spinner, StepList, Textarea, buttonClass, definePlugin, defineUi, diffStats, isConflict, mergeUi, parseDiff, pluginApi, protobaseVersion, uiFromModule, useApiToken, useAppAddress, useAssistant, useAssistantClient, useBackendData, useClient, useFacets, useHistogram, useLayoutRecord, useList, useListDisplay, useMeta, usePlugin, useRecord, useResourceClient, useSeries, useUpdateRecord };
@@ -1,4 +1,6 @@
1
1
  import { humanize } from "./naming.js";
2
+ import { markdownExcerpt } from "../markdown/markdown-excerpt.js";
3
+ import { isMarkdown } from "../markdown/markdown-hint.js";
2
4
  import { enumLabel } from "@protobase/schema";
3
5
  //#region src/live/columns-from-model.ts
4
6
  const tones = {
@@ -94,6 +96,7 @@ const columnsFromModel = (resource, view, highlight = [], names = listColumnName
94
96
  value: (row) => row[name] == null ? null : currencyCents(row[name])
95
97
  },
96
98
  ...(kind === "datetime" || kind === "date") && { value: (row) => row[name] == null ? null : Date.parse(String(row[name])) },
99
+ ...isMarkdown(field, viewField) && { value: (row) => typeof row[name] === "string" ? markdownExcerpt(row[name]) : row[name] },
97
100
  ...(kind === "percent" || kind === "number" && field.type === "decimal") && { value: (row) => row[name] == null ? null : Number(row[name]) }
98
101
  }];
99
102
  });
@@ -1,7 +1,7 @@
1
1
  import { keys } from "../data/query-keys.js";
2
2
  import { recordId } from "./model-helpers.js";
3
- import { listLabels, withLabels } from "./relation-labels.js";
4
3
  import { listPhotos, withPhotos } from "./row-photos.js";
4
+ import { listLabels, withLabels } from "./relation-labels.js";
5
5
  //#region src/live/grid-loader.ts
6
6
  const exactBelow = 5e3;
7
7
  /**
@@ -5,9 +5,9 @@ import { humanize } from "./naming.js";
5
5
  import { fetchLabels } from "./relation-labels.js";
6
6
  import { setRange } from "../filter-panel/filter-state.js";
7
7
  import { stateToText } from "./filter-string.js";
8
+ import { rangeFormat, rangeHistogram, widgetFields } from "./view-fields.js";
8
9
  import { labelKeys, matchOptions } from "./reference-options.js";
9
10
  import { useReferenceSearch } from "./use-reference-search.js";
10
- import { rangeFormat, rangeHistogram, widgetFields } from "./view-fields.js";
11
11
  import { useQueries, useQueryClient } from "@tanstack/react-query";
12
12
  import { enumLabel } from "@protobase/schema";
13
13
  //#region src/live/use-filter-config.ts
@@ -0,0 +1,18 @@
1
+ import { cn } from "../lib/cn.js";
2
+ import { markdownHtml } from "./markdown-html.js";
3
+ import { useMemo } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import DOMPurify from "dompurify";
6
+ //#region src/markdown/markdown-content.tsx
7
+ const page = cn("break-words [&>*:first-child]:mt-0 [&>*:last-child]:mb-0", "[&_p]:my-2 [&_ul]:my-2 [&_ol]:my-2 [&_pre]:my-2 [&_blockquote]:my-2 [&_table]:my-2 [&_hr]:my-3", "[&_h1]:mt-4 [&_h1]:mb-2 [&_h1]:text-lg [&_h1]:font-semibold [&_h2]:mt-4 [&_h2]:mb-2 [&_h2]:text-base [&_h2]:font-semibold", "[&_h3]:mt-3 [&_h3]:mb-1 [&_h3]:font-semibold [&_h4]:mt-3 [&_h4]:mb-1 [&_h4]:font-semibold [&_h5]:font-semibold [&_h6]:font-semibold", "[&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5 [&_li:has(>input)]:list-none [&_li>input]:mr-1.5 [&_li>input]:-ml-5 [&_li>input]:align-middle", "[&_a]:text-primary-text [&_a]:underline [&_a]:underline-offset-2 hover:[&_a]:text-primary-hover", "[&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_code]:py-0.5 [&_code]:font-mono [&_code]:text-[0.9em]", "[&_pre]:whitespace-pre-wrap [&_pre]:rounded-md [&_pre]:bg-muted [&_pre]:p-3 [&_pre_code]:bg-transparent [&_pre_code]:p-0", "[&_blockquote]:border-l-2 [&_blockquote]:border-border-strong [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground", "[&_table]:w-full [&_table]:border-collapse [&_th]:border [&_th]:border-border [&_th]:bg-surface [&_th]:px-2 [&_th]:py-1 [&_th]:text-left [&_th]:font-semibold", "[&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1 [&_td]:[overflow-wrap:anywhere]", "[&_hr]:border-border [&_img]:inline [&_img]:max-w-full [&_img]:rounded [&_del]:text-muted-foreground");
8
+ const inLink = cn("[&_*]:my-0 [&_h1]:font-semibold [&_h2]:font-semibold [&_h3]:font-semibold [&_ul]:list-inside [&_ol]:list-inside [&_pre]:whitespace-pre-wrap [&_code]:font-mono [&_code]:text-[0.9em]");
9
+ /** Markdown rendered, sanitised before it reaches the page: raw HTML shows as text, links open in a new tab. */
10
+ const MarkdownContent = ({ source, inLink: insideLink = false, className }) => {
11
+ const html = useMemo(() => DOMPurify.sanitize(markdownHtml(source, { inLink: insideLink }), { ADD_ATTR: ["target"] }), [source, insideLink]);
12
+ return /* @__PURE__ */ jsx("div", {
13
+ className: cn(insideLink ? inLink : page, className),
14
+ dangerouslySetInnerHTML: { __html: html }
15
+ });
16
+ };
17
+ //#endregion
18
+ export { MarkdownContent };
@@ -0,0 +1,46 @@
1
+ import { Segmented } from "../primitives/segmented.js";
2
+ import { Markdown } from "./markdown.js";
3
+ import { Textarea } from "../primitives/textarea.js";
4
+ import { useState } from "react";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/markdown/markdown-editor.tsx
7
+ const modes = [{
8
+ value: "write",
9
+ label: "Write"
10
+ }, {
11
+ value: "preview",
12
+ label: "Preview"
13
+ }];
14
+ /**
15
+ * Markdown source in a box that grows with it, as GitHub edits a comment, and a switch to its rendered preview: the
16
+ * text is stored exactly as typed.
17
+ */
18
+ const MarkdownEditor = ({ value, onChange, label, invalid, placeholder }) => {
19
+ const [mode, setMode] = useState("write");
20
+ return /* @__PURE__ */ jsxs("div", {
21
+ className: "flex flex-col gap-1.5",
22
+ children: [/* @__PURE__ */ jsx("span", {
23
+ className: "self-start",
24
+ children: /* @__PURE__ */ jsx(Segmented, {
25
+ value: mode,
26
+ options: modes,
27
+ onChange: setMode,
28
+ label
29
+ })
30
+ }), mode === "write" ? /* @__PURE__ */ jsx(Textarea, {
31
+ rows: 5,
32
+ invalid,
33
+ placeholder,
34
+ value,
35
+ onChange: (event) => onChange(event.target.value)
36
+ }) : /* @__PURE__ */ jsx("div", {
37
+ className: "min-h-[7.5rem] rounded-md border border-border px-2.5 py-1.5",
38
+ children: value.trim() === "" ? /* @__PURE__ */ jsx("span", {
39
+ className: "text-faint-foreground",
40
+ children: "Nothing to preview"
41
+ }) : /* @__PURE__ */ jsx(Markdown, { source: value })
42
+ })]
43
+ });
44
+ };
45
+ //#endregion
46
+ export { MarkdownEditor };