@powerhousedao/builder-profile 0.0.5 → 0.0.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.
@@ -0,0 +1,12 @@
1
+ export type MarkdownEditorMode = "preview" | "edit" | "live";
2
+ interface MarkdownEditorProps {
3
+ value: string;
4
+ onChange: (value: string) => void;
5
+ onBlur?: (value: string) => void;
6
+ height?: number;
7
+ label?: string;
8
+ labelClassName?: string;
9
+ }
10
+ export declare function MarkdownEditor({ value, onChange, onBlur, height, label, labelClassName, }: MarkdownEditorProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=markdown-editor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-editor.d.ts","sourceRoot":"","sources":["../../../../editors/builder-profile/components/markdown-editor.tsx"],"names":[],"mappings":"AAeA,MAAM,MAAM,kBAAkB,GAAG,SAAS,GAAG,MAAM,GAAG,MAAM,CAAC;AAE7D,UAAU,mBAAmB;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,QAAQ,EACR,MAAM,EACN,MAAY,EACZ,KAAiB,EACjB,cAAqD,GACtD,EAAE,mBAAmB,2CA2LrB"}
@@ -0,0 +1,133 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useState } from "react";
3
+ import remarkGfm from "remark-gfm";
4
+ import rehypeSlug from "rehype-slug";
5
+ import { useLocalStorage } from "usehooks-ts";
6
+ // Custom preview renderer to make links open in new tabs and ensure proper list rendering
7
+ const previewOptions = {
8
+ components: {
9
+ a: ({ ...props }) => (_jsx("a", { ...props, target: "_blank", rel: "noopener noreferrer" })),
10
+ },
11
+ rehypePlugins: [rehypeSlug],
12
+ remarkPlugins: [remarkGfm],
13
+ };
14
+ export function MarkdownEditor({ value, onChange, onBlur, height = 350, label = "Content", labelClassName = "text-sm leading-4 mb-3 font-medium", }) {
15
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
16
+ const [MDEditor, setMDEditor] = useState(null);
17
+ const [contentValue, setContentValue] = useState(" ");
18
+ const [isLoaded, setIsLoaded] = useState(false);
19
+ const [loadError, setLoadError] = useState(null);
20
+ const [viewMarkdownMode, setViewMarkdownMode] = useLocalStorage("markdown-editor-view-mode", "live");
21
+ // Ensure we have a valid mode for the editor
22
+ const editorMode = viewMarkdownMode || "live";
23
+ // Load the MDEditor component dynamically
24
+ useEffect(() => {
25
+ // Use a more robust dynamic import approach
26
+ const loadEditor = async () => {
27
+ try {
28
+ const module = await import("@uiw/react-md-editor");
29
+ setMDEditor(() => module.default);
30
+ setIsLoaded(true);
31
+ setLoadError(null);
32
+ }
33
+ catch (error) {
34
+ console.error("Failed to load MDEditor:", error);
35
+ setLoadError(error instanceof Error ? error.message : "Failed to load editor");
36
+ setIsLoaded(true);
37
+ }
38
+ };
39
+ // Add a small delay to ensure DOM is ready
40
+ const timer = setTimeout(loadEditor, 0);
41
+ return () => clearTimeout(timer);
42
+ }, []);
43
+ // Update contentValue when value prop changes
44
+ useEffect(() => {
45
+ if (isLoaded) {
46
+ const stringValue = typeof value === "string" ? value : "";
47
+ const safeValue = stringValue.trim() || " ";
48
+ setContentValue(safeValue);
49
+ }
50
+ }, [value, isLoaded]);
51
+ useEffect(() => {
52
+ if (!MDEditor)
53
+ return;
54
+ const handleViewButtonClick = () => {
55
+ const buttonLive = document.querySelector("button[data-name='live']");
56
+ const buttonEdit = document.querySelector("button[data-name='edit']");
57
+ const buttonPreview = document.querySelector("button[data-name='preview']");
58
+ const liveLi = buttonLive?.closest("li");
59
+ const editLi = buttonEdit?.closest("li");
60
+ const previewLi = buttonPreview?.closest("li");
61
+ if (previewLi && previewLi.classList.contains("active")) {
62
+ setViewMarkdownMode("preview");
63
+ }
64
+ if (editLi && editLi.classList.contains("active")) {
65
+ setViewMarkdownMode("edit");
66
+ }
67
+ if (liveLi && liveLi.classList.contains("active")) {
68
+ setViewMarkdownMode("live");
69
+ }
70
+ };
71
+ document.addEventListener("click", handleViewButtonClick, true);
72
+ return () => {
73
+ document.removeEventListener("click", handleViewButtonClick, true);
74
+ };
75
+ }, [MDEditor, setViewMarkdownMode]);
76
+ // Handle content changes
77
+ const handleContentChange = (newValue) => {
78
+ if (newValue !== undefined) {
79
+ const stringValue = typeof newValue === "string" ? newValue : "";
80
+ // Only replace completely empty strings with a space, preserve all other content
81
+ const safeValue = stringValue === "" ? " " : stringValue;
82
+ setContentValue(safeValue);
83
+ onChange(newValue); // Keep the original value for the parent component
84
+ }
85
+ };
86
+ // Handle content blur
87
+ const handleContentBlur = (e) => {
88
+ if (onBlur) {
89
+ onBlur(e.target.value);
90
+ }
91
+ };
92
+ return (_jsxs("div", { className: "w-full", children: [_jsx("style", { children: `
93
+ .w-md-editor-preview ul {
94
+ list-style-type: disc !important;
95
+ padding-left: 2em !important;
96
+ }
97
+
98
+ .w-md-editor-preview ol {
99
+ list-style-type: decimal !important;
100
+ padding-left: 2em !important;
101
+ }
102
+
103
+ /* Ensure proper table styling */
104
+ .w-md-editor-preview table {
105
+ border-collapse: collapse;
106
+ width: 100%;
107
+ margin: 1em 0;
108
+ }
109
+
110
+ .w-md-editor-preview th,
111
+ .w-md-editor-preview td {
112
+ border: 1px solid #ddd;
113
+ padding: 8px;
114
+ text-align: left;
115
+ }
116
+
117
+ .w-md-editor-preview th {
118
+ background-color: #f5f5f5;
119
+ }
120
+
121
+ .w-md-editor-text-pre code, .w-md-editor-text-pre div {
122
+ font-size: 16px !important;
123
+ line-height: 24px !important;
124
+ }
125
+ .w-md-editor-text-input {
126
+ font-size: 16px !important;
127
+ line-height: 24px !important;
128
+ }
129
+
130
+ ` }), label && _jsx("p", { className: labelClassName, children: label }), !isLoaded && (_jsx("div", { className: "w-full border border-gray-300 rounded-md p-3 bg-white", style: { height: `${height}px` }, children: _jsx("div", { className: "w-full h-full flex items-center justify-center text-gray-500", children: "Loading editor..." }) })), isLoaded && loadError && (_jsx("div", { className: "w-full border border-red-300 rounded-md p-3 bg-red-50", style: { height: `${height}px` }, children: _jsxs("div", { className: "w-full h-full flex flex-col items-center justify-center text-red-600", children: [_jsx("p", { className: "text-sm font-medium mb-2", children: "Failed to load markdown editor" }), _jsx("p", { className: "text-xs text-red-500", children: loadError }), _jsx("textarea", { className: "w-full h-full mt-2 p-2 border border-gray-300 rounded text-sm", placeholder: "Fallback text editor - write your content here...", value: value, onChange: (e) => onChange(e.target.value), onBlur: (e) => onBlur?.(e.target.value) })] }) })), isLoaded && MDEditor && (_jsx("div", { "data-color-mode": "light", className: "w-full", children: _jsx(MDEditor, { height: height, value: contentValue, onChange: handleContentChange, onBlur: handleContentBlur, previewOptions: previewOptions, enableScroll: true, preview: editorMode, textareaProps: {
131
+ placeholder: "Write your content here...",
132
+ } }) }))] }));
133
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/builder-profile/editor.tsx"],"names":[],"mappings":"AAuCA,MAAM,CAAC,OAAO,UAAU,MAAM,4CAugB7B"}
1
+ {"version":3,"file":"editor.d.ts","sourceRoot":"","sources":["../../../editors/builder-profile/editor.tsx"],"names":[],"mappings":"AAwCA,MAAM,CAAC,OAAO,UAAU,MAAM,4CAmgB7B"}
@@ -12,6 +12,7 @@ import { LinksSection } from "./components/LinksSection.js";
12
12
  import { ContributorsSection } from "./components/ContributorsSection.js";
13
13
  import { ProfilePreview } from "./components/ProfilePreview.js";
14
14
  import { ImageUrlInput } from "./components/ImageUrlInput.js";
15
+ import { MarkdownEditor } from "./components/markdown-editor.js";
15
16
  const STATUS_OPTIONS = [
16
17
  { value: "ACTIVE", label: "Active", color: "bg-emerald-500" },
17
18
  { value: "INACTIVE", label: "Inactive", color: "bg-slate-400" },
@@ -242,9 +243,5 @@ export default function Editor() {
242
243
  }
243
244
  }, children: [_jsx("option", { value: "", disabled: true, children: "Select status..." }), STATUS_OPTIONS.map((option) => (_jsx("option", { value: option.value, children: option.label }, option.value)))] })] }), _jsxs("div", { children: [_jsx("label", { className: "field-label", children: "Profile Type" }), _jsxs("div", { className: "type-toggle", children: [_jsx("button", { type: "button", onClick: () => handleTypeChange("INDIVIDUAL"), className: state?.type === "INDIVIDUAL" ? "active" : "", children: _jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(User, { size: 16 }), "Individual"] }) }), _jsx("button", { type: "button", onClick: () => handleTypeChange("TEAM"), className: state?.type === "TEAM" ? "active" : "", children: _jsxs("span", { className: "flex items-center justify-center gap-2", children: [_jsx(Users, { size: 16 }), "Team"] }) })] }), _jsx("p", { className: "field-hint", children: state?.type === "TEAM"
244
245
  ? "Teams can add contributors to their profile"
245
- : "Individual profiles represent a single builder" })] })] })] }), _jsxs("div", { className: "section-card p-6", children: [_jsxs("h3", { className: "text-lg font-semibold text-slate-900 mb-6 flex items-center gap-2", children: [_jsx("span", { className: "w-8 h-8 rounded-lg bg-emerald-50 flex items-center justify-center", children: _jsx(FileText, { size: 18, className: "text-emerald-600" }) }), "About"] }), _jsxs("div", { children: [_jsx("label", { className: "field-label", children: "Description" }), _jsx(Textarea, { className: "w-full", defaultValue: state?.description || "", onBlur: (e) => {
246
- if (e.target.value !== state?.description) {
247
- handleFieldChange("description", e.target.value);
248
- }
249
- }, placeholder: "Describe your expertise, focus areas, and what you bring to the ecosystem...", rows: 4, autoExpand: true }), _jsx("p", { className: "field-hint", children: "A compelling description helps others understand your capabilities" })] })] }), _jsx(SkillsSection, { skills: state?.skils || [], onAddSkill: handleAddSkill, onRemoveSkill: handleRemoveSkill }), _jsx(ScopesSection, { scopes: state?.scopes || [], onAddScope: handleAddScope, onRemoveScope: handleRemoveScope }), _jsx(LinksSection, { links: state?.links || [], onAddLink: handleAddLink, onEditLink: handleEditLink, onRemoveLink: handleRemoveLink }), state?.type === "TEAM" && (_jsx(ContributorsSection, { contributors: state.contributors, onAddContributor: handleAddContributor, onRemoveContributor: handleRemoveContributor })), _jsx(ToastContainer, { position: "bottom-right" })] })] }));
246
+ : "Individual profiles represent a single builder" })] })] })] }), _jsxs("div", { className: "section-card p-6", children: [_jsxs("h3", { className: "text-lg font-semibold text-slate-900 mb-6 flex items-center gap-2", children: [_jsx("span", { className: "w-8 h-8 rounded-lg bg-emerald-50 flex items-center justify-center", children: _jsx(FileText, { size: 18, className: "text-emerald-600" }) }), "About"] }), _jsxs("div", { children: [_jsx(MarkdownEditor, { label: "What is the builder profile about?", height: 350, value: state?.description || "", onChange: () => { }, onBlur: (value) => handleFieldChange("description", value) }), _jsx("p", { className: "field-hint", children: "A compelling description helps others understand your capabilities" })] })] }), _jsx(SkillsSection, { skills: state?.skils || [], onAddSkill: handleAddSkill, onRemoveSkill: handleRemoveSkill }), _jsx(ScopesSection, { scopes: state?.scopes || [], onAddScope: handleAddScope, onRemoveScope: handleRemoveScope }), _jsx(LinksSection, { links: state?.links || [], onAddLink: handleAddLink, onEditLink: handleEditLink, onRemoveLink: handleRemoveLink }), state?.type === "TEAM" && (_jsx(ContributorsSection, { contributors: state.contributors, onAddContributor: handleAddContributor, onRemoveContributor: handleRemoveContributor })), _jsx(ToastContainer, { position: "bottom-right" })] })] }));
250
247
  }
package/dist/style.css CHANGED
@@ -8,6 +8,7 @@
8
8
  "Courier New", monospace;
9
9
  --color-red-50: oklch(97.1% 0.013 17.38);
10
10
  --color-red-100: hsl(3 82% 96%);
11
+ --color-red-300: hsl(6 82% 87%);
11
12
  --color-red-500: hsl(5 80% 78%);
12
13
  --color-red-600: hsl(4 81% 74%);
13
14
  --color-orange-500: hsl(33 99% 70%);
@@ -83,6 +84,7 @@
83
84
  --tracking-wider: 0.05em;
84
85
  --leading-relaxed: 1.625;
85
86
  --radius-sm: 0.25rem;
87
+ --radius-md: 0.375rem;
86
88
  --radius-lg: 0.5rem;
87
89
  --radius-xl: 0.75rem;
88
90
  --radius-2xl: 1rem;
@@ -293,6 +295,9 @@
293
295
  .mt-1 {
294
296
  margin-top: calc(var(--spacing) * 1);
295
297
  }
298
+ .mt-2 {
299
+ margin-top: calc(var(--spacing) * 2);
300
+ }
296
301
  .mt-3 {
297
302
  margin-top: calc(var(--spacing) * 3);
298
303
  }
@@ -329,6 +334,9 @@
329
334
  .inline-flex {
330
335
  display: inline-flex;
331
336
  }
337
+ .table {
338
+ display: table;
339
+ }
332
340
  .h-2 {
333
341
  height: calc(var(--spacing) * 2);
334
342
  }
@@ -398,6 +406,9 @@
398
406
  .flex-shrink-0 {
399
407
  flex-shrink: 0;
400
408
  }
409
+ .border-collapse {
410
+ border-collapse: collapse;
411
+ }
401
412
  .-translate-y-1\/2 {
402
413
  --tw-translate-y: calc(calc(1/2 * 100%) * -1);
403
414
  translate: var(--tw-translate-x) var(--tw-translate-y);
@@ -414,6 +425,9 @@
414
425
  .grid-cols-1 {
415
426
  grid-template-columns: repeat(1, minmax(0, 1fr));
416
427
  }
428
+ .flex-col {
429
+ flex-direction: column;
430
+ }
417
431
  .flex-wrap {
418
432
  flex-wrap: wrap;
419
433
  }
@@ -492,6 +506,9 @@
492
506
  .rounded-lg {
493
507
  border-radius: var(--radius-lg);
494
508
  }
509
+ .rounded-md {
510
+ border-radius: var(--radius-md);
511
+ }
495
512
  .rounded-sm {
496
513
  border-radius: var(--radius-sm);
497
514
  }
@@ -533,6 +550,9 @@
533
550
  .border-indigo-100 {
534
551
  border-color: var(--color-indigo-100);
535
552
  }
553
+ .border-red-300 {
554
+ border-color: var(--color-red-300);
555
+ }
536
556
  .border-slate-100 {
537
557
  border-color: var(--color-slate-100);
538
558
  }
@@ -605,6 +625,9 @@
605
625
  .bg-purple-500 {
606
626
  background-color: var(--color-purple-500);
607
627
  }
628
+ .bg-red-50 {
629
+ background-color: var(--color-red-50);
630
+ }
608
631
  .bg-red-500 {
609
632
  background-color: var(--color-red-500);
610
633
  }
@@ -796,6 +819,10 @@
796
819
  font-size: var(--text-xs);
797
820
  line-height: var(--tw-leading, var(--text-xs--line-height));
798
821
  }
822
+ .leading-4 {
823
+ --tw-leading: calc(var(--spacing) * 4);
824
+ line-height: calc(var(--spacing) * 4);
825
+ }
799
826
  .leading-relaxed {
800
827
  --tw-leading: var(--leading-relaxed);
801
828
  line-height: var(--leading-relaxed);
@@ -863,6 +890,9 @@
863
890
  .text-red-500 {
864
891
  color: var(--color-red-500);
865
892
  }
893
+ .text-red-600 {
894
+ color: var(--color-red-600);
895
+ }
866
896
  .text-sky-700 {
867
897
  color: var(--color-sky-700);
868
898
  }
@@ -923,6 +953,10 @@
923
953
  --tw-shadow: 0 1px 3px 0 var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 1px 2px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
924
954
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
925
955
  }
956
+ .blur {
957
+ --tw-blur: blur(8px);
958
+ filter: var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,);
959
+ }
926
960
  .backdrop-blur-sm {
927
961
  --tw-backdrop-blur: blur(var(--blur-sm));
928
962
  -webkit-backdrop-filter: var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);
@@ -1304,6 +1338,59 @@
1304
1338
  inherits: false;
1305
1339
  initial-value: 0 0 #0000;
1306
1340
  }
1341
+ @property --tw-blur {
1342
+ syntax: "*";
1343
+ inherits: false;
1344
+ }
1345
+ @property --tw-brightness {
1346
+ syntax: "*";
1347
+ inherits: false;
1348
+ }
1349
+ @property --tw-contrast {
1350
+ syntax: "*";
1351
+ inherits: false;
1352
+ }
1353
+ @property --tw-grayscale {
1354
+ syntax: "*";
1355
+ inherits: false;
1356
+ }
1357
+ @property --tw-hue-rotate {
1358
+ syntax: "*";
1359
+ inherits: false;
1360
+ }
1361
+ @property --tw-invert {
1362
+ syntax: "*";
1363
+ inherits: false;
1364
+ }
1365
+ @property --tw-opacity {
1366
+ syntax: "*";
1367
+ inherits: false;
1368
+ }
1369
+ @property --tw-saturate {
1370
+ syntax: "*";
1371
+ inherits: false;
1372
+ }
1373
+ @property --tw-sepia {
1374
+ syntax: "*";
1375
+ inherits: false;
1376
+ }
1377
+ @property --tw-drop-shadow {
1378
+ syntax: "*";
1379
+ inherits: false;
1380
+ }
1381
+ @property --tw-drop-shadow-color {
1382
+ syntax: "*";
1383
+ inherits: false;
1384
+ }
1385
+ @property --tw-drop-shadow-alpha {
1386
+ syntax: "<percentage>";
1387
+ inherits: false;
1388
+ initial-value: 100%;
1389
+ }
1390
+ @property --tw-drop-shadow-size {
1391
+ syntax: "*";
1392
+ inherits: false;
1393
+ }
1307
1394
  @property --tw-backdrop-blur {
1308
1395
  syntax: "*";
1309
1396
  inherits: false;
@@ -1384,6 +1471,19 @@
1384
1471
  --tw-ring-offset-width: 0px;
1385
1472
  --tw-ring-offset-color: #fff;
1386
1473
  --tw-ring-offset-shadow: 0 0 #0000;
1474
+ --tw-blur: initial;
1475
+ --tw-brightness: initial;
1476
+ --tw-contrast: initial;
1477
+ --tw-grayscale: initial;
1478
+ --tw-hue-rotate: initial;
1479
+ --tw-invert: initial;
1480
+ --tw-opacity: initial;
1481
+ --tw-saturate: initial;
1482
+ --tw-sepia: initial;
1483
+ --tw-drop-shadow: initial;
1484
+ --tw-drop-shadow-color: initial;
1485
+ --tw-drop-shadow-alpha: 100%;
1486
+ --tw-drop-shadow-size: initial;
1387
1487
  --tw-backdrop-blur: initial;
1388
1488
  --tw-backdrop-brightness: initial;
1389
1489
  --tw-backdrop-contrast: initial;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@powerhousedao/builder-profile",
3
3
  "description": "Builder profile document model",
4
- "version": "0.0.5",
4
+ "version": "0.0.6",
5
5
  "license": "AGPL-3.0-only",
6
6
  "type": "module",
7
7
  "files": [
@@ -66,11 +66,13 @@
66
66
  "@powerhousedao/common": "latest",
67
67
  "@powerhousedao/design-system": "latest",
68
68
  "@powerhousedao/document-engineering": "^1.38.0",
69
+ "@uiw/react-md-editor": "^4.0.11",
69
70
  "document-model": "latest",
70
71
  "error": "^10.4.0",
71
72
  "graphql": "^16.10.0",
72
73
  "graphql-tag": "^2.12.6",
73
74
  "lucide-react": "^0.556.0",
75
+ "rehype-slug": "^6.0.0",
74
76
  "zod": "^3.24.2"
75
77
  },
76
78
  "devDependencies": {