@burdenoff/microfe-devportal 2026.720.6 → 2026.724.1

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 (26) hide show
  1. package/dist/devportal/components/ConfirmDialog.d.ts.map +1 -1
  2. package/dist/devportal/components/ConfirmDialog.js +12 -14
  3. package/dist/devportal/components/ConfirmDialog.js.map +1 -1
  4. package/dist/devportal/components/MarkdownEditor.d.ts +24 -0
  5. package/dist/devportal/components/MarkdownEditor.d.ts.map +1 -0
  6. package/dist/devportal/components/MarkdownEditor.js +136 -0
  7. package/dist/devportal/components/MarkdownEditor.js.map +1 -0
  8. package/dist/devportal/components/index.d.ts +1 -0
  9. package/dist/devportal/components/index.d.ts.map +1 -1
  10. package/dist/devportal/components/index.js +1 -0
  11. package/dist/devportal/pages/ApplicationDetailPage.d.ts.map +1 -1
  12. package/dist/devportal/pages/ApplicationDetailPage.js +749 -747
  13. package/dist/devportal/pages/ApplicationDetailPage.js.map +1 -1
  14. package/dist/devportal/pages/ApplicationVersionsPage.d.ts.map +1 -1
  15. package/dist/devportal/pages/ApplicationVersionsPage.js +390 -374
  16. package/dist/devportal/pages/ApplicationVersionsPage.js.map +1 -1
  17. package/dist/devportal/pages/CreateApplicationPage.d.ts.map +1 -1
  18. package/dist/devportal/pages/CreateApplicationPage.js +11 -9
  19. package/dist/devportal/pages/CreateApplicationPage.js.map +1 -1
  20. package/dist/devportal/pages/EditApplicationPage.d.ts.map +1 -1
  21. package/dist/devportal/pages/EditApplicationPage.js +284 -283
  22. package/dist/devportal/pages/EditApplicationPage.js.map +1 -1
  23. package/dist/devportal/pages/ListingPreviewPage.d.ts.map +1 -1
  24. package/dist/devportal/pages/ListingPreviewPage.js +162 -158
  25. package/dist/devportal/pages/ListingPreviewPage.js.map +1 -1
  26. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK1E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAqKhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAwCA,CAAC"}
1
+ {"version":3,"file":"ConfirmDialog.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,EAAE,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAK1E,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACtC,SAAS,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAC;IACvE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;GAEG;AACH,eAAO,MAAM,aAAa,EAAE,EAAE,CAAC,kBAAkB,CAgLhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,KAAK,EAAE,EAAE,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,CAAC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAwCA,CAAC"}
@@ -8,8 +8,10 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
8
8
  let x = n(), S = n(), C = r(null), w = r(null), T = r(null), { t: E } = i(), D = (e, t) => {
9
9
  let n = E(e);
10
10
  return n === e ? t : n;
11
- };
11
+ }, O = r(f);
12
12
  if (t(() => {
13
+ O.current = f;
14
+ }, [f]), t(() => {
13
15
  if (!d) return;
14
16
  T.current = document.activeElement;
15
17
  let e = document.body.style.overflow;
@@ -19,17 +21,13 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
19
21
  t?.focus();
20
22
  });
21
23
  let n = (e) => {
22
- e.key === "Escape" && !y && (e.preventDefault(), f(!1));
24
+ e.key === "Escape" && !y && (e.preventDefault(), O.current(!1));
23
25
  };
24
26
  return document.addEventListener("keydown", n), () => {
25
27
  document.body.style.overflow = e, document.removeEventListener("keydown", n), T.current?.focus();
26
28
  };
27
- }, [
28
- y,
29
- f,
30
- d
31
- ]), !d) return null;
32
- let O = {
29
+ }, [y, d]), !d) return null;
30
+ let k = {
33
31
  default: {
34
32
  icon: /* @__PURE__ */ l(s, { className: "h-6 w-6 text-accent-blue" }),
35
33
  iconBg: "bg-status-info-bg-subtle",
@@ -50,14 +48,14 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
50
48
  iconBg: "bg-status-success-bg-subtle",
51
49
  button: "bg-status-success-bg text-white hover:bg-status-success-bg/90"
52
50
  }
53
- }[v === "destructive" ? "danger" : v], k = () => f(!1);
51
+ }[v === "destructive" ? "danger" : v], A = () => f(!1);
54
52
  return /* @__PURE__ */ u("div", {
55
53
  className: "fixed inset-0 z-50 flex items-center justify-center",
56
54
  children: [/* @__PURE__ */ l("button", {
57
55
  type: "button",
58
56
  "aria-label": "Close dialog backdrop",
59
57
  className: "absolute inset-0 bg-bg-overlay",
60
- onClick: k
58
+ onClick: A
61
59
  }), /* @__PURE__ */ u("div", {
62
60
  ref: C,
63
61
  role: "dialog",
@@ -69,8 +67,8 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
69
67
  children: [/* @__PURE__ */ u("div", {
70
68
  className: "flex items-start gap-4",
71
69
  children: [/* @__PURE__ */ l("div", {
72
- className: e("rounded-full p-2", O.iconBg),
73
- children: O.icon
70
+ className: e("rounded-full p-2", k.iconBg),
71
+ children: k.icon
74
72
  }), /* @__PURE__ */ u("div", {
75
73
  className: "flex-1",
76
74
  children: [
@@ -95,7 +93,7 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
95
93
  className: "mt-6 flex justify-end gap-3",
96
94
  children: [/* @__PURE__ */ l("button", {
97
95
  type: "button",
98
- onClick: k,
96
+ onClick: A,
99
97
  disabled: y,
100
98
  className: "rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50",
101
99
  children: _
@@ -104,7 +102,7 @@ var d = ({ open: d, onOpenChange: f, onConfirm: p, title: m, description: h, con
104
102
  type: "button",
105
103
  onClick: p,
106
104
  disabled: y,
107
- className: e("rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50", O.button),
105
+ className: e("rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50", k.button),
108
106
  children: y ? D("devportal.processing", "Processing...") : g
109
107
  })]
110
108
  })]
@@ -1 +1 @@
1
- {"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"sourcesContent":["import { useEffect, useId, useRef, type FC, type ReactNode } from \"react\";\nimport { AlertTriangle, Info, CheckCircle, XCircle } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void | Promise<void>;\n title: string;\n description?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: \"default\" | \"danger\" | \"warning\" | \"success\" | \"destructive\";\n loading?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Confirmation dialog component\n */\nexport const ConfirmDialog: FC<ConfirmDialogProps> = ({\n open,\n onOpenChange,\n onConfirm,\n title,\n description,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n variant = \"default\",\n loading = false,\n children,\n}) => {\n const titleId = useId();\n const descriptionId = useId();\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const confirmButtonRef = useRef<HTMLButtonElement | null>(null);\n const previousFocusRef = useRef<HTMLElement | null>(null);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n previousFocusRef.current = document.activeElement as HTMLElement | null;\n const originalOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n // For form dialogs (with children), focus the container so Tab naturally\n // walks through the form fields first. Only auto-focus the confirm button\n // for simple confirmation dialogs that have no form fields.\n const focusTarget = children\n ? dialogRef.current\n : (confirmButtonRef.current ?? dialogRef.current);\n window.requestAnimationFrame(() => {\n focusTarget?.focus();\n });\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !loading) {\n event.preventDefault();\n onOpenChange(false);\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.body.style.overflow = originalOverflow;\n document.removeEventListener(\"keydown\", onKeyDown);\n previousFocusRef.current?.focus();\n };\n }, [loading, onOpenChange, open]);\n\n if (!open) return null;\n\n // Map 'destructive' to 'danger' for internal use\n const normalizedVariant = variant === \"destructive\" ? \"danger\" : variant;\n\n const variantStyles = {\n default: {\n icon: <Info className=\"h-6 w-6 text-accent-blue\" />,\n iconBg: \"bg-status-info-bg-subtle\",\n button:\n \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\",\n },\n danger: {\n icon: <XCircle className=\"h-6 w-6 text-status-error-text\" />,\n iconBg: \"bg-status-error-bg-subtle\",\n button: \"bg-status-error-bg text-white hover:bg-status-error-bg/90\",\n },\n warning: {\n icon: <AlertTriangle className=\"h-6 w-6 text-status-warning-text\" />,\n iconBg: \"bg-status-warning-bg-subtle\",\n button: \"bg-status-warning-bg text-white hover:bg-status-warning-bg/90\",\n },\n success: {\n icon: <CheckCircle className=\"h-6 w-6 text-status-success-text\" />,\n iconBg: \"bg-status-success-bg-subtle\",\n button: \"bg-status-success-bg text-white hover:bg-status-success-bg/90\",\n },\n };\n\n const styles = variantStyles[normalizedVariant];\n\n const handleClose = () => onOpenChange(false);\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <button\n type=\"button\"\n aria-label=\"Close dialog backdrop\"\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={handleClose}\n />\n\n {/* Dialog */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description || children ? descriptionId : undefined}\n tabIndex={-1}\n className=\"relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl\"\n >\n <div className=\"flex items-start gap-4\">\n <div className={cn(\"rounded-full p-2\", styles.iconBg)}>\n {styles.icon}\n </div>\n <div className=\"flex-1\">\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h3>\n {description && (\n <p\n id={descriptionId}\n className=\"mt-2 text-sm text-text-secondary\"\n >\n {description}\n </p>\n )}\n {children && (\n <div\n className=\"mt-4\"\n id={description ? undefined : descriptionId}\n >\n {children}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={loading}\n className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmButtonRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={loading}\n className={cn(\n \"rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50\",\n styles.button,\n )}\n >\n {loading\n ? tr(\"devportal.processing\", \"Processing...\")\n : confirmLabel}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * Alert banner component\n */\nexport const Alert: FC<{\n variant?: \"info\" | \"warning\" | \"error\" | \"success\";\n title?: string;\n children: ReactNode;\n onDismiss?: () => void;\n className?: string;\n}> = ({ variant = \"info\", title, children, onDismiss, className }) => {\n const variantStyles = {\n info: \"bg-status-info-bg-subtle border-border-subtle text-status-info-text\",\n warning:\n \"bg-status-warning-bg-subtle border-border-subtle text-status-warning-text\",\n error:\n \"bg-status-error-bg-subtle border-border-subtle text-status-error-text\",\n success:\n \"bg-status-success-bg-subtle border-border-subtle text-status-success-text\",\n };\n\n const icons = {\n info: <Info className=\"h-5 w-5\" />,\n warning: <AlertTriangle className=\"h-5 w-5\" />,\n error: <XCircle className=\"h-5 w-5\" />,\n success: <CheckCircle className=\"h-5 w-5\" />,\n };\n\n return (\n <div\n className={cn(\"rounded-lg border p-4\", variantStyles[variant], className)}\n >\n <div className=\"flex items-start gap-3\">\n {icons[variant]}\n <div className=\"flex-1\">\n {title && <h4 className=\"font-medium\">{title}</h4>}\n <div className=\"text-sm\">{children}</div>\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"hover:opacity-70\"\n >\n <XCircle className=\"h-5 w-5\" />\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ConfirmDialog.js","names":[],"sources":["../../../src/devportal/components/ConfirmDialog.tsx"],"sourcesContent":["import { useEffect, useId, useRef, type FC, type ReactNode } from \"react\";\nimport { AlertTriangle, Info, CheckCircle, XCircle } from \"lucide-react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { cn } from \"../utils/cn\";\n\nexport interface ConfirmDialogProps {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n onConfirm: () => void | Promise<void>;\n title: string;\n description?: string;\n confirmLabel?: string;\n cancelLabel?: string;\n variant?: \"default\" | \"danger\" | \"warning\" | \"success\" | \"destructive\";\n loading?: boolean;\n children?: ReactNode;\n}\n\n/**\n * Confirmation dialog component\n */\nexport const ConfirmDialog: FC<ConfirmDialogProps> = ({\n open,\n onOpenChange,\n onConfirm,\n title,\n description,\n confirmLabel = \"Confirm\",\n cancelLabel = \"Cancel\",\n variant = \"default\",\n loading = false,\n children,\n}) => {\n const titleId = useId();\n const descriptionId = useId();\n const dialogRef = useRef<HTMLDivElement | null>(null);\n const confirmButtonRef = useRef<HTMLButtonElement | null>(null);\n const previousFocusRef = useRef<HTMLElement | null>(null);\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n // Keep a stable ref to the latest onOpenChange. Parents commonly pass an\n // inline arrow (new reference every render), so depending on it directly\n // would re-run the focus effect on every keystroke and yank focus away from\n // form inputs. Storing it in a ref lets the effect depend only on open/loading.\n const onOpenChangeRef = useRef(onOpenChange);\n useEffect(() => {\n onOpenChangeRef.current = onOpenChange;\n }, [onOpenChange]);\n\n useEffect(() => {\n if (!open) {\n return undefined;\n }\n\n previousFocusRef.current = document.activeElement as HTMLElement | null;\n const originalOverflow = document.body.style.overflow;\n document.body.style.overflow = \"hidden\";\n\n // For form dialogs (with children), focus the container so Tab naturally\n // walks through the form fields first. Only auto-focus the confirm button\n // for simple confirmation dialogs that have no form fields.\n const focusTarget = children\n ? dialogRef.current\n : (confirmButtonRef.current ?? dialogRef.current);\n window.requestAnimationFrame(() => {\n focusTarget?.focus();\n });\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\" && !loading) {\n event.preventDefault();\n onOpenChangeRef.current(false);\n }\n };\n\n document.addEventListener(\"keydown\", onKeyDown);\n\n return () => {\n document.body.style.overflow = originalOverflow;\n document.removeEventListener(\"keydown\", onKeyDown);\n previousFocusRef.current?.focus();\n };\n // children is intentionally omitted: it re-creates every render and would\n // re-trigger focus-stealing on each keystroke. Only open/loading gate this.\n }, [loading, open]);\n\n if (!open) return null;\n\n // Map 'destructive' to 'danger' for internal use\n const normalizedVariant = variant === \"destructive\" ? \"danger\" : variant;\n\n const variantStyles = {\n default: {\n icon: <Info className=\"h-6 w-6 text-accent-blue\" />,\n iconBg: \"bg-status-info-bg-subtle\",\n button:\n \"bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90\",\n },\n danger: {\n icon: <XCircle className=\"h-6 w-6 text-status-error-text\" />,\n iconBg: \"bg-status-error-bg-subtle\",\n button: \"bg-status-error-bg text-white hover:bg-status-error-bg/90\",\n },\n warning: {\n icon: <AlertTriangle className=\"h-6 w-6 text-status-warning-text\" />,\n iconBg: \"bg-status-warning-bg-subtle\",\n button: \"bg-status-warning-bg text-white hover:bg-status-warning-bg/90\",\n },\n success: {\n icon: <CheckCircle className=\"h-6 w-6 text-status-success-text\" />,\n iconBg: \"bg-status-success-bg-subtle\",\n button: \"bg-status-success-bg text-white hover:bg-status-success-bg/90\",\n },\n };\n\n const styles = variantStyles[normalizedVariant];\n\n const handleClose = () => onOpenChange(false);\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center\">\n {/* Backdrop */}\n <button\n type=\"button\"\n aria-label=\"Close dialog backdrop\"\n className=\"absolute inset-0 bg-bg-overlay\"\n onClick={handleClose}\n />\n\n {/* Dialog */}\n <div\n ref={dialogRef}\n role=\"dialog\"\n aria-modal=\"true\"\n aria-labelledby={titleId}\n aria-describedby={description || children ? descriptionId : undefined}\n tabIndex={-1}\n className=\"relative z-10 w-full max-w-md rounded-lg bg-bg-surface p-6 shadow-xl\"\n >\n <div className=\"flex items-start gap-4\">\n <div className={cn(\"rounded-full p-2\", styles.iconBg)}>\n {styles.icon}\n </div>\n <div className=\"flex-1\">\n <h3 id={titleId} className=\"text-lg font-semibold\">\n {title}\n </h3>\n {description && (\n <p\n id={descriptionId}\n className=\"mt-2 text-sm text-text-secondary\"\n >\n {description}\n </p>\n )}\n {children && (\n <div\n className=\"mt-4\"\n id={description ? undefined : descriptionId}\n >\n {children}\n </div>\n )}\n </div>\n </div>\n\n <div className=\"mt-6 flex justify-end gap-3\">\n <button\n type=\"button\"\n onClick={handleClose}\n disabled={loading}\n className=\"rounded-md border px-4 py-2 text-sm font-medium hover:bg-bg-sunken transition-colors disabled:opacity-50\"\n >\n {cancelLabel}\n </button>\n <button\n ref={confirmButtonRef}\n type=\"button\"\n onClick={onConfirm}\n disabled={loading}\n className={cn(\n \"rounded-md px-4 py-2 text-sm font-medium transition-colors disabled:opacity-50\",\n styles.button,\n )}\n >\n {loading\n ? tr(\"devportal.processing\", \"Processing...\")\n : confirmLabel}\n </button>\n </div>\n </div>\n </div>\n );\n};\n\n/**\n * Alert banner component\n */\nexport const Alert: FC<{\n variant?: \"info\" | \"warning\" | \"error\" | \"success\";\n title?: string;\n children: ReactNode;\n onDismiss?: () => void;\n className?: string;\n}> = ({ variant = \"info\", title, children, onDismiss, className }) => {\n const variantStyles = {\n info: \"bg-status-info-bg-subtle border-border-subtle text-status-info-text\",\n warning:\n \"bg-status-warning-bg-subtle border-border-subtle text-status-warning-text\",\n error:\n \"bg-status-error-bg-subtle border-border-subtle text-status-error-text\",\n success:\n \"bg-status-success-bg-subtle border-border-subtle text-status-success-text\",\n };\n\n const icons = {\n info: <Info className=\"h-5 w-5\" />,\n warning: <AlertTriangle className=\"h-5 w-5\" />,\n error: <XCircle className=\"h-5 w-5\" />,\n success: <CheckCircle className=\"h-5 w-5\" />,\n };\n\n return (\n <div\n className={cn(\"rounded-lg border p-4\", variantStyles[variant], className)}\n >\n <div className=\"flex items-start gap-3\">\n {icons[variant]}\n <div className=\"flex-1\">\n {title && <h4 className=\"font-medium\">{title}</h4>}\n <div className=\"text-sm\">{children}</div>\n </div>\n {onDismiss && (\n <button\n type=\"button\"\n onClick={onDismiss}\n className=\"hover:opacity-70\"\n >\n <XCircle className=\"h-5 w-5\" />\n </button>\n )}\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { FC, TextareaHTMLAttributes } from 'react';
2
+
3
+ interface MarkdownEditorProps extends Omit<TextareaHTMLAttributes<HTMLTextAreaElement>, "className" | "value" | "onChange"> {
4
+ label: string;
5
+ value: string;
6
+ onChange: (value: string) => void;
7
+ error?: string;
8
+ hint?: string;
9
+ required?: boolean;
10
+ className?: string;
11
+ rows?: number;
12
+ showToolbar?: boolean;
13
+ previewLabel?: string;
14
+ writeLabel?: string;
15
+ }
16
+ /**
17
+ * Reusable markdown editor with live preview and an optional formatting toolbar.
18
+ *
19
+ * The toolbar inserts markdown syntax around the current selection or at the
20
+ * cursor position, mirroring common markdown editor behaviour.
21
+ */
22
+ export declare const MarkdownEditor: FC<MarkdownEditorProps>;
23
+ export {};
24
+ //# sourceMappingURL=MarkdownEditor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/MarkdownEditor.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,sBAAsB,EAAa,MAAM,OAAO,CAAC;AAMnE,UAAU,mBAAoB,SAAQ,IAAI,CACxC,sBAAsB,CAAC,mBAAmB,CAAC,EAC3C,WAAW,GAAG,OAAO,GAAG,UAAU,CACnC;IACC,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAgBD;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,EAAE,CAAC,mBAAmB,CAiLlD,CAAC"}
@@ -0,0 +1,136 @@
1
+ import { cn as e } from "../utils/cn.js";
2
+ import { useCallback as t, useRef as n, useState as r } from "react";
3
+ import { Markdown as i } from "@burdenoff/fe-libs/shared/components";
4
+ import { Bold as a, Code as o, Italic as s, Link as c, List as l } from "lucide-react";
5
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
6
+ //#region src/devportal/components/MarkdownEditor.tsx
7
+ var f = [
8
+ {
9
+ key: "bold",
10
+ icon: a,
11
+ syntax: "**",
12
+ label: "Bold"
13
+ },
14
+ {
15
+ key: "italic",
16
+ icon: s,
17
+ syntax: "_",
18
+ label: "Italic"
19
+ },
20
+ {
21
+ key: "code",
22
+ icon: o,
23
+ syntax: "`",
24
+ label: "Inline code"
25
+ },
26
+ {
27
+ key: "link",
28
+ icon: c,
29
+ syntax: "[",
30
+ suffix: "](url)",
31
+ label: "Link"
32
+ },
33
+ {
34
+ key: "list",
35
+ icon: l,
36
+ syntax: "- ",
37
+ label: "Bullet list"
38
+ }
39
+ ], p = ({ label: a, value: o, onChange: s, error: c, hint: l, required: p, className: m, rows: h = 6, showToolbar: g = !0, previewLabel: _ = "Preview", writeLabel: v = "Write", id: y, placeholder: b, ...x }) => {
40
+ let S = y || a.toLowerCase().replace(/\s+/g, "-"), C = n(null), [w, T] = r("write"), E = t((e, t = e) => {
41
+ let n = C.current;
42
+ if (!n) return;
43
+ let r = n.selectionStart, i = n.selectionEnd, a = o.slice(0, r), c = o.slice(r, i), l = o.slice(i), u, d, f;
44
+ c.length > 0 ? (u = `${a}${e}${c}${t}${l}`, d = r + e.length, f = i + e.length) : (u = `${a}${e}${t}${l}`, d = r + e.length, f = d), s(u), requestAnimationFrame(() => {
45
+ n.focus(), n.setSelectionRange(d, f);
46
+ });
47
+ }, [s, o]), D = t(() => {
48
+ let e = C.current;
49
+ if (!e) return;
50
+ let t = e.selectionStart, n = e.selectionEnd, r = o.slice(t, n), i;
51
+ i = r.length > 0 ? r.split("\n").map((e) => e.startsWith("- ") ? e : `- ${e}`).join("\n") : "- ";
52
+ let a = o.slice(0, t), c = o.slice(n);
53
+ s(`${a}${i}${c}`), requestAnimationFrame(() => {
54
+ e.focus();
55
+ let n = t + i.length;
56
+ e.setSelectionRange(n, n);
57
+ });
58
+ }, [s, o]);
59
+ return /* @__PURE__ */ d("div", {
60
+ className: e("space-y-2", m),
61
+ children: [
62
+ /* @__PURE__ */ d("label", {
63
+ htmlFor: S,
64
+ className: "block text-sm font-medium",
65
+ children: [a, p && /* @__PURE__ */ u("span", {
66
+ className: "text-status-error-text ml-1",
67
+ children: "*"
68
+ })]
69
+ }),
70
+ g && /* @__PURE__ */ u("div", {
71
+ className: "flex items-center gap-1",
72
+ children: f.map((e) => /* @__PURE__ */ u("button", {
73
+ type: "button",
74
+ onClick: () => e.key === "list" ? D() : E(e.syntax, e.suffix),
75
+ title: e.label,
76
+ className: "inline-flex items-center justify-center rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary focus:outline-none focus:ring-1 focus:ring-primary",
77
+ children: /* @__PURE__ */ u(e.icon, { size: 14 })
78
+ }, e.key))
79
+ }),
80
+ /* @__PURE__ */ d("div", {
81
+ className: "rounded-md border border-border-default overflow-hidden",
82
+ children: [
83
+ /* @__PURE__ */ d("div", {
84
+ className: "flex border-b border-border-default bg-bg-sunken",
85
+ children: [/* @__PURE__ */ u("button", {
86
+ type: "button",
87
+ onClick: () => T("write"),
88
+ className: e("px-3 py-1.5 text-xs font-medium", w === "write" ? "bg-bg-surface text-text-primary border-b-2 border-primary" : "text-text-secondary hover:text-text-primary"),
89
+ children: v
90
+ }), /* @__PURE__ */ u("button", {
91
+ type: "button",
92
+ onClick: () => T("preview"),
93
+ className: e("px-3 py-1.5 text-xs font-medium", w === "preview" ? "bg-bg-surface text-text-primary border-b-2 border-primary" : "text-text-secondary hover:text-text-primary"),
94
+ children: _
95
+ })]
96
+ }),
97
+ /* @__PURE__ */ u("div", {
98
+ className: e(w !== "write" && "hidden"),
99
+ children: /* @__PURE__ */ u("textarea", {
100
+ ref: C,
101
+ id: S,
102
+ rows: h,
103
+ value: o,
104
+ onChange: (e) => s(e.target.value),
105
+ placeholder: b,
106
+ className: e("w-full bg-bg-surface px-3 py-2 text-sm resize-y", "focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent", "placeholder:text-text-secondary"),
107
+ ...x
108
+ })
109
+ }),
110
+ w === "preview" && /* @__PURE__ */ u("div", {
111
+ className: "min-h-[theme(spacing.24)] bg-bg-surface px-3 py-2",
112
+ children: o.trim() ? /* @__PURE__ */ u(i, {
113
+ className: "text-sm text-text-primary",
114
+ children: o
115
+ }) : /* @__PURE__ */ u("p", {
116
+ className: "text-sm text-text-secondary italic",
117
+ children: b || "Nothing to preview yet."
118
+ })
119
+ })
120
+ ]
121
+ }),
122
+ l && !c && /* @__PURE__ */ u("p", {
123
+ className: "text-xs text-text-secondary",
124
+ children: l
125
+ }),
126
+ c && /* @__PURE__ */ u("p", {
127
+ className: "text-xs text-status-error-text",
128
+ children: c
129
+ })
130
+ ]
131
+ });
132
+ };
133
+ //#endregion
134
+ export { p as MarkdownEditor };
135
+
136
+ //# sourceMappingURL=MarkdownEditor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MarkdownEditor.js","names":[],"sources":["../../../src/devportal/components/MarkdownEditor.tsx"],"sourcesContent":["import type { FC, TextareaHTMLAttributes, ReactNode } from \"react\";\nimport { useState, useRef, useCallback } from \"react\";\nimport { Bold, Italic, Link, Code, List } from \"lucide-react\";\nimport { Markdown } from \"@burdenoff/fe-libs/shared/components\";\nimport { cn } from \"../utils/cn\";\n\ninterface MarkdownEditorProps extends Omit<\n TextareaHTMLAttributes<HTMLTextAreaElement>,\n \"className\" | \"value\" | \"onChange\"\n> {\n label: string;\n value: string;\n onChange: (value: string) => void;\n error?: string;\n hint?: string;\n required?: boolean;\n className?: string;\n rows?: number;\n showToolbar?: boolean;\n previewLabel?: string;\n writeLabel?: string;\n}\n\nconst TOOLBAR_BUTTONS: Array<{\n key: string;\n icon: typeof Bold;\n syntax: string;\n suffix?: string;\n label: string;\n}> = [\n { key: \"bold\", icon: Bold, syntax: \"**\", label: \"Bold\" },\n { key: \"italic\", icon: Italic, syntax: \"_\", label: \"Italic\" },\n { key: \"code\", icon: Code, syntax: \"`\", label: \"Inline code\" },\n { key: \"link\", icon: Link, syntax: \"[\", suffix: \"](url)\", label: \"Link\" },\n { key: \"list\", icon: List, syntax: \"- \", label: \"Bullet list\" },\n];\n\n/**\n * Reusable markdown editor with live preview and an optional formatting toolbar.\n *\n * The toolbar inserts markdown syntax around the current selection or at the\n * cursor position, mirroring common markdown editor behaviour.\n */\nexport const MarkdownEditor: FC<MarkdownEditorProps> = ({\n label,\n value,\n onChange,\n error,\n hint,\n required,\n className,\n rows = 6,\n showToolbar = true,\n previewLabel = \"Preview\",\n writeLabel = \"Write\",\n id,\n placeholder,\n ...props\n}) => {\n const fieldId = id || label.toLowerCase().replace(/\\s+/g, \"-\");\n const textareaRef = useRef<HTMLTextAreaElement>(null);\n const [activeTab, setActiveTab] = useState<\"write\" | \"preview\">(\"write\");\n\n const insertSyntax = useCallback(\n (syntax: string, suffix = syntax) => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const before = value.slice(0, start);\n const selected = value.slice(start, end);\n const after = value.slice(end);\n\n let nextValue: string;\n let nextStart: number;\n let nextEnd: number;\n\n if (selected.length > 0) {\n // Wrap selection with syntax.\n nextValue = `${before}${syntax}${selected}${suffix}${after}`;\n nextStart = start + syntax.length;\n nextEnd = end + syntax.length;\n } else {\n // Insert syntax pair at cursor and place cursor between them.\n nextValue = `${before}${syntax}${suffix}${after}`;\n nextStart = start + syntax.length;\n nextEnd = nextStart;\n }\n\n onChange(nextValue);\n\n // Restore selection after React re-render.\n requestAnimationFrame(() => {\n textarea.focus();\n textarea.setSelectionRange(nextStart, nextEnd);\n });\n },\n [onChange, value],\n );\n\n const handleList = useCallback(() => {\n const textarea = textareaRef.current;\n if (!textarea) return;\n\n const start = textarea.selectionStart;\n const end = textarea.selectionEnd;\n const selected = value.slice(start, end);\n\n let replacement: string;\n if (selected.length > 0) {\n replacement = selected\n .split(\"\\n\")\n .map((line) => (line.startsWith(\"- \") ? line : `- ${line}`))\n .join(\"\\n\");\n } else {\n replacement = \"- \";\n }\n\n const before = value.slice(0, start);\n const after = value.slice(end);\n const nextValue = `${before}${replacement}${after}`;\n onChange(nextValue);\n\n requestAnimationFrame(() => {\n textarea.focus();\n const cursorPos = start + replacement.length;\n textarea.setSelectionRange(cursorPos, cursorPos);\n });\n }, [onChange, value]);\n\n return (\n <div className={cn(\"space-y-2\", className)}>\n <label htmlFor={fieldId} className=\"block text-sm font-medium\">\n {label}\n {required && <span className=\"text-status-error-text ml-1\">*</span>}\n </label>\n\n {showToolbar && (\n <div className=\"flex items-center gap-1\">\n {TOOLBAR_BUTTONS.map((button) => (\n <button\n key={button.key}\n type=\"button\"\n onClick={() =>\n button.key === \"list\"\n ? handleList()\n : insertSyntax(button.syntax, button.suffix)\n }\n title={button.label}\n className=\"inline-flex items-center justify-center rounded-md p-1.5 text-text-secondary hover:bg-bg-sunken hover:text-text-primary focus:outline-none focus:ring-1 focus:ring-primary\"\n >\n <button.icon size={14} />\n </button>\n ))}\n </div>\n )}\n\n <div className=\"rounded-md border border-border-default overflow-hidden\">\n <div className=\"flex border-b border-border-default bg-bg-sunken\">\n <button\n type=\"button\"\n onClick={() => setActiveTab(\"write\")}\n className={cn(\n \"px-3 py-1.5 text-xs font-medium\",\n activeTab === \"write\"\n ? \"bg-bg-surface text-text-primary border-b-2 border-primary\"\n : \"text-text-secondary hover:text-text-primary\",\n )}\n >\n {writeLabel}\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab(\"preview\")}\n className={cn(\n \"px-3 py-1.5 text-xs font-medium\",\n activeTab === \"preview\"\n ? \"bg-bg-surface text-text-primary border-b-2 border-primary\"\n : \"text-text-secondary hover:text-text-primary\",\n )}\n >\n {previewLabel}\n </button>\n </div>\n\n <div className={cn(activeTab !== \"write\" && \"hidden\")}>\n <textarea\n ref={textareaRef}\n id={fieldId}\n rows={rows}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n placeholder={placeholder}\n className={cn(\n \"w-full bg-bg-surface px-3 py-2 text-sm resize-y\",\n \"focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent\",\n \"placeholder:text-text-secondary\",\n )}\n {...props}\n />\n </div>\n\n {activeTab === \"preview\" && (\n <div className=\"min-h-[theme(spacing.24)] bg-bg-surface px-3 py-2\">\n {value.trim() ? (\n <Markdown className=\"text-sm text-text-primary\">{value}</Markdown>\n ) : (\n <p className=\"text-sm text-text-secondary italic\">\n {placeholder || \"Nothing to preview yet.\"}\n </p>\n )}\n </div>\n )}\n </div>\n\n {hint && !error && <p className=\"text-xs text-text-secondary\">{hint}</p>}\n {error && <p className=\"text-xs text-status-error-text\">{error}</p>}\n </div>\n );\n};\n"],"mappings":"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"}
@@ -18,4 +18,5 @@ export * from './PermissionGuard';
18
18
  export * from './FeatureFlaggedRoute';
19
19
  export * from './TelemetryErrorBoundary';
20
20
  export * from './SectionNav';
21
+ export * from './MarkdownEditor';
21
22
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/devportal/components/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC;AACvC,cAAc,mBAAmB,CAAC;AAClC,cAAc,uBAAuB,CAAC;AACtC,cAAc,0BAA0B,CAAC;AACzC,cAAc,cAAc,CAAC;AAC7B,cAAc,kBAAkB,CAAC"}
@@ -13,3 +13,4 @@ import "./FormField.js";
13
13
  import "./ConfirmDialog.js";
14
14
  import "./GrantDelegationModal.js";
15
15
  import "./PermissionGuard.js";
16
+ import "./MarkdownEditor.js";
@@ -1 +1 @@
1
- {"version":3,"file":"ApplicationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/ApplicationDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA2ThC;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,EA06BnC,CAAC"}
1
+ {"version":3,"file":"ApplicationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/devportal/pages/ApplicationDetailPage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC;AA6ThC;;GAEG;AACH,eAAO,MAAM,qBAAqB,EAAE,EA06BnC,CAAC"}