@burdenoff/microfe-vibecontrols 2026.514.6 → 2026.515.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.
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { useTr as e } from "../../shared/hooks/useTr.js";
|
|
2
2
|
import { useAvailableNoteTagsQuery as t, useCreateNoteTagMutation as n } from "../../generated/wspace-operations.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { useVibeControls as r } from "../../providers/VibeControlsProvider.js";
|
|
4
|
+
import { useEffect as i, useState as a } from "react";
|
|
5
|
+
import { Check as o, Edit as s, Eye as c, Plus as l, Save as u, Tag as d, X as f } from "lucide-react";
|
|
6
|
+
import { jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/notes/NoteEditor.tsx
|
|
7
|
-
var
|
|
8
|
+
var h = [
|
|
8
9
|
"var(--color-status-error-text)",
|
|
9
10
|
"var(--color-status-warning-text)",
|
|
10
11
|
"var(--color-status-success-text)",
|
|
@@ -13,84 +14,84 @@ var m = [
|
|
|
13
14
|
"var(--color-accent-purple)",
|
|
14
15
|
"var(--color-text-tertiary)"
|
|
15
16
|
];
|
|
16
|
-
function
|
|
17
|
-
let
|
|
18
|
-
title:
|
|
19
|
-
content:
|
|
20
|
-
vibeId:
|
|
21
|
-
isPinned:
|
|
22
|
-
}), [
|
|
17
|
+
function g({ note: g, vibeOptions: _ = [], defaultVibeId: v, hideVibeSelect: y, onSubmit: b, onCancel: x, isLoading: S }) {
|
|
18
|
+
let C = e(), { workspaceId: w } = r(), [T, E] = a(!1), [D, O] = a({
|
|
19
|
+
title: g?.title || "",
|
|
20
|
+
content: g?.content || "",
|
|
21
|
+
vibeId: g?.vibeId || v || "",
|
|
22
|
+
isPinned: g?.isPinned ?? !1
|
|
23
|
+
}), [k, A] = a(/* @__PURE__ */ new Set()), [j, M] = a(!1), [N, P] = a(!1), { data: F, refetch: I } = t(), L = (F?.availableNoteTags ?? []).map((e) => ({
|
|
23
24
|
id: e.id,
|
|
24
25
|
key: e.key,
|
|
25
26
|
label: e.label,
|
|
26
27
|
color: e.color ?? null
|
|
27
28
|
}));
|
|
28
|
-
|
|
29
|
-
if (
|
|
29
|
+
i(() => {
|
|
30
|
+
if (j || L.length === 0) return;
|
|
30
31
|
let e = /* @__PURE__ */ new Set();
|
|
31
|
-
if (
|
|
32
|
-
else if (
|
|
33
|
-
let n =
|
|
32
|
+
if (g?.tagAssignments && g.tagAssignments.length > 0) g.tagAssignments.forEach((t) => e.add(t.tagId));
|
|
33
|
+
else if (g?.tags && g.tags.length > 0) for (let t of g.tags) {
|
|
34
|
+
let n = L.find((e) => e.label.toLowerCase() === t.toLowerCase() || e.key === t.toLowerCase());
|
|
34
35
|
n && e.add(n.id);
|
|
35
36
|
}
|
|
36
|
-
e.size > 0 &&
|
|
37
|
+
e.size > 0 && A(e), M(!0);
|
|
37
38
|
}, [
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
39
|
+
L,
|
|
40
|
+
g,
|
|
41
|
+
j
|
|
41
42
|
]);
|
|
42
|
-
let [
|
|
43
|
-
let e =
|
|
44
|
-
if (!(!e ||
|
|
45
|
-
|
|
43
|
+
let [R] = n(), [z, B] = a(""), [V, H] = a("var(--color-accent-purple)"), [U, W] = a(!1), G = async () => {
|
|
44
|
+
let e = z.trim();
|
|
45
|
+
if (!(!e || U)) {
|
|
46
|
+
W(!0);
|
|
46
47
|
try {
|
|
47
|
-
let t = (await
|
|
48
|
+
let t = (await R({ variables: { input: {
|
|
48
49
|
label: e,
|
|
49
|
-
color:
|
|
50
|
+
color: V
|
|
50
51
|
} } })).data?.createNoteTag;
|
|
51
|
-
t && (
|
|
52
|
+
t && (A((e) => new Set(e).add(t.id)), await I()), B("");
|
|
52
53
|
} catch (e) {
|
|
53
54
|
console.error("Failed to create tag:", e);
|
|
54
55
|
} finally {
|
|
55
|
-
|
|
56
|
+
W(!1);
|
|
56
57
|
}
|
|
57
58
|
}
|
|
58
|
-
},
|
|
59
|
-
|
|
59
|
+
}, K = (e) => {
|
|
60
|
+
A((t) => {
|
|
60
61
|
let n = new Set(t);
|
|
61
62
|
return n.has(e) ? n.delete(e) : n.add(e), n;
|
|
62
63
|
});
|
|
63
|
-
},
|
|
64
|
-
e.preventDefault(),
|
|
65
|
-
...
|
|
66
|
-
workspaceId: "
|
|
67
|
-
title:
|
|
68
|
-
content:
|
|
69
|
-
vibeId:
|
|
70
|
-
isPinned:
|
|
71
|
-
selectedTagIds: Array.from(
|
|
64
|
+
}, q = (e) => {
|
|
65
|
+
e.preventDefault(), b({
|
|
66
|
+
...g?.id ? { id: g.id } : {},
|
|
67
|
+
workspaceId: w ?? "",
|
|
68
|
+
title: D.title,
|
|
69
|
+
content: D.content,
|
|
70
|
+
vibeId: D.vibeId || void 0,
|
|
71
|
+
isPinned: D.isPinned,
|
|
72
|
+
selectedTagIds: Array.from(k)
|
|
72
73
|
});
|
|
73
|
-
},
|
|
74
|
+
}, J = (e) => {
|
|
74
75
|
let t = [], n = e, r = 0;
|
|
75
76
|
for (; n.length > 0;) {
|
|
76
77
|
let e = n.match(/^(\*\*|__)(.+?)\1/);
|
|
77
78
|
if (e) {
|
|
78
|
-
t.push(/* @__PURE__ */
|
|
79
|
+
t.push(/* @__PURE__ */ p("strong", { children: J(e[2]) }, r++)), n = n.slice(e[0].length);
|
|
79
80
|
continue;
|
|
80
81
|
}
|
|
81
82
|
let i = n.match(/^(\*|_)([^*_]+?)\1/);
|
|
82
83
|
if (i) {
|
|
83
|
-
t.push(/* @__PURE__ */
|
|
84
|
+
t.push(/* @__PURE__ */ p("em", { children: J(i[2]) }, r++)), n = n.slice(i[0].length);
|
|
84
85
|
continue;
|
|
85
86
|
}
|
|
86
87
|
let a = n.match(/^~~(.+?)~~/);
|
|
87
88
|
if (a) {
|
|
88
|
-
t.push(/* @__PURE__ */
|
|
89
|
+
t.push(/* @__PURE__ */ p("del", { children: J(a[1]) }, r++)), n = n.slice(a[0].length);
|
|
89
90
|
continue;
|
|
90
91
|
}
|
|
91
92
|
let o = n.match(/^`([^`]+)`/);
|
|
92
93
|
if (o) {
|
|
93
|
-
t.push(/* @__PURE__ */
|
|
94
|
+
t.push(/* @__PURE__ */ p("code", {
|
|
94
95
|
className: "bg-bg-elevated px-1.5 py-0.5 rounded text-sm font-mono text-text-primary",
|
|
95
96
|
children: o[1]
|
|
96
97
|
}, r++)), n = n.slice(o[0].length);
|
|
@@ -99,13 +100,13 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
99
100
|
let s = n.match(/^\[([^\]]+)\]\(([^)]+)\)/);
|
|
100
101
|
if (s) {
|
|
101
102
|
let e = s[2];
|
|
102
|
-
/^(https?:\/\/|mailto:|\/|#)/i.test(e) ? t.push(/* @__PURE__ */
|
|
103
|
+
/^(https?:\/\/|mailto:|\/|#)/i.test(e) ? t.push(/* @__PURE__ */ p("a", {
|
|
103
104
|
href: e,
|
|
104
105
|
target: "_blank",
|
|
105
106
|
rel: "noopener noreferrer",
|
|
106
107
|
className: "text-action-primary-bg hover:underline",
|
|
107
108
|
children: s[1]
|
|
108
|
-
}, r++)) : t.push(/* @__PURE__ */
|
|
109
|
+
}, r++)) : t.push(/* @__PURE__ */ p("span", {
|
|
109
110
|
className: "text-text-secondary",
|
|
110
111
|
children: s[1]
|
|
111
112
|
}, r++)), n = n.slice(s[0].length);
|
|
@@ -115,155 +116,155 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
115
116
|
}
|
|
116
117
|
return t;
|
|
117
118
|
};
|
|
118
|
-
return /* @__PURE__ */
|
|
119
|
+
return /* @__PURE__ */ p("div", {
|
|
119
120
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay",
|
|
120
|
-
children: /* @__PURE__ */
|
|
121
|
+
children: /* @__PURE__ */ m("div", {
|
|
121
122
|
className: "w-full max-w-3xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col",
|
|
122
|
-
children: [/* @__PURE__ */
|
|
123
|
+
children: [/* @__PURE__ */ m("div", {
|
|
123
124
|
className: "flex items-center justify-between px-6 py-4 border-b border-border-default",
|
|
124
|
-
children: [/* @__PURE__ */
|
|
125
|
+
children: [/* @__PURE__ */ p("h2", {
|
|
125
126
|
className: "text-lg font-semibold text-text-primary",
|
|
126
|
-
children:
|
|
127
|
-
}), /* @__PURE__ */
|
|
127
|
+
children: g ? C("vibecontrols.notes.editNote", "Edit Note") : C("vibecontrols.notes.newNote", "New Note")
|
|
128
|
+
}), /* @__PURE__ */ m("div", {
|
|
128
129
|
className: "flex items-center gap-2",
|
|
129
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ m("button", {
|
|
130
131
|
type: "button",
|
|
131
|
-
onClick: () =>
|
|
132
|
-
className: `inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md transition-colors ${
|
|
133
|
-
children: [
|
|
134
|
-
}), /* @__PURE__ */
|
|
135
|
-
onClick:
|
|
132
|
+
onClick: () => E(!T),
|
|
133
|
+
className: `inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md transition-colors ${T ? "bg-action-primary-bg text-action-primary-text" : "bg-bg-sunken text-text-primary hover:bg-bg-elevated"}`,
|
|
134
|
+
children: [p(T ? s : c, { className: "w-4 h-4" }), T ? C("vibecontrols.notes.edit", "Edit") : C("vibecontrols.notes.preview", "Preview")]
|
|
135
|
+
}), /* @__PURE__ */ p("button", {
|
|
136
|
+
onClick: x,
|
|
136
137
|
className: "p-1 rounded hover:bg-bg-sunken transition-colors",
|
|
137
|
-
children: /* @__PURE__ */ f
|
|
138
|
+
children: /* @__PURE__ */ p(f, { className: "w-5 h-5 text-text-secondary" })
|
|
138
139
|
})]
|
|
139
140
|
})]
|
|
140
|
-
}), /* @__PURE__ */
|
|
141
|
-
onSubmit:
|
|
141
|
+
}), /* @__PURE__ */ m("form", {
|
|
142
|
+
onSubmit: q,
|
|
142
143
|
className: "flex-1 flex flex-col overflow-hidden",
|
|
143
|
-
children: [/* @__PURE__ */
|
|
144
|
+
children: [/* @__PURE__ */ m("div", {
|
|
144
145
|
className: "p-6 space-y-4 flex-1 overflow-y-auto",
|
|
145
146
|
children: [
|
|
146
|
-
/* @__PURE__ */
|
|
147
|
+
/* @__PURE__ */ p("div", { children: /* @__PURE__ */ p("input", {
|
|
147
148
|
type: "text",
|
|
148
|
-
value:
|
|
149
|
-
onChange: (e) =>
|
|
150
|
-
...
|
|
149
|
+
value: D.title,
|
|
150
|
+
onChange: (e) => O({
|
|
151
|
+
...D,
|
|
151
152
|
title: e.target.value
|
|
152
153
|
}),
|
|
153
|
-
placeholder:
|
|
154
|
+
placeholder: C("vibecontrols.notes.titlePlaceholder", "Note title..."),
|
|
154
155
|
required: !0,
|
|
155
156
|
className: "w-full px-0 py-2 text-lg sm:text-2xl font-bold bg-transparent border-0 border-b border-border-default text-text-primary placeholder:text-text-placeholder focus:outline-none focus:border-action-primary-bg"
|
|
156
157
|
}) }),
|
|
157
|
-
/* @__PURE__ */
|
|
158
|
-
className: `grid ${
|
|
159
|
-
children: [!
|
|
158
|
+
/* @__PURE__ */ m("div", {
|
|
159
|
+
className: `grid ${y ? "grid-cols-1" : "grid-cols-2"} gap-4`,
|
|
160
|
+
children: [!y && /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("label", {
|
|
160
161
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
161
|
-
children:
|
|
162
|
-
}), /* @__PURE__ */
|
|
163
|
-
value:
|
|
164
|
-
onChange: (e) =>
|
|
165
|
-
...
|
|
162
|
+
children: C("vibecontrols.notes.relatedVibe", "Related Vibe (optional)")
|
|
163
|
+
}), /* @__PURE__ */ m("select", {
|
|
164
|
+
value: D.vibeId,
|
|
165
|
+
onChange: (e) => O({
|
|
166
|
+
...D,
|
|
166
167
|
vibeId: e.target.value
|
|
167
168
|
}),
|
|
168
169
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
169
|
-
children: [/* @__PURE__ */
|
|
170
|
+
children: [/* @__PURE__ */ p("option", {
|
|
170
171
|
value: "",
|
|
171
172
|
children: "No vibe"
|
|
172
|
-
}),
|
|
173
|
+
}), _.map((e) => /* @__PURE__ */ p("option", {
|
|
173
174
|
value: e.id,
|
|
174
175
|
children: e.name
|
|
175
176
|
}, e.id))]
|
|
176
|
-
})] }), /* @__PURE__ */
|
|
177
|
-
/* @__PURE__ */
|
|
177
|
+
})] }), /* @__PURE__ */ m("div", { children: [
|
|
178
|
+
/* @__PURE__ */ p("label", {
|
|
178
179
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
179
|
-
children:
|
|
180
|
+
children: C("vibecontrols.notes.tags", "Tags")
|
|
180
181
|
}),
|
|
181
|
-
/* @__PURE__ */
|
|
182
|
+
/* @__PURE__ */ m("div", {
|
|
182
183
|
className: "flex flex-wrap gap-1.5 min-h-[38px] px-3 py-2 bg-bg-sunken border border-border-default rounded-md",
|
|
183
|
-
children: [
|
|
184
|
+
children: [L.filter((e) => k.has(e.id)).map((e) => /* @__PURE__ */ m("span", {
|
|
184
185
|
className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-action-primary-bg/10 text-action-primary-bg rounded",
|
|
185
186
|
children: [
|
|
186
|
-
/* @__PURE__ */
|
|
187
|
+
/* @__PURE__ */ p(d, { className: "w-3 h-3" }),
|
|
187
188
|
e.label,
|
|
188
|
-
/* @__PURE__ */
|
|
189
|
+
/* @__PURE__ */ p("button", {
|
|
189
190
|
type: "button",
|
|
190
|
-
onClick: () =>
|
|
191
|
+
onClick: () => K(e.id),
|
|
191
192
|
className: "ml-0.5 hover:text-status-error-text",
|
|
192
|
-
children: /* @__PURE__ */ f
|
|
193
|
+
children: /* @__PURE__ */ p(f, { className: "w-3 h-3" })
|
|
193
194
|
})
|
|
194
195
|
]
|
|
195
|
-
}, e.id)), /* @__PURE__ */
|
|
196
|
+
}, e.id)), /* @__PURE__ */ m("button", {
|
|
196
197
|
type: "button",
|
|
197
|
-
onClick: () =>
|
|
198
|
+
onClick: () => P(!N),
|
|
198
199
|
className: "inline-flex items-center gap-1 px-2 py-0.5 text-xs text-text-secondary hover:text-text-primary hover:bg-bg-elevated rounded transition-colors",
|
|
199
|
-
children: [/* @__PURE__ */
|
|
200
|
+
children: [/* @__PURE__ */ p(d, { className: "w-3 h-3" }), k.size === 0 ? "Add tags..." : "+"]
|
|
200
201
|
})]
|
|
201
202
|
}),
|
|
202
|
-
|
|
203
|
+
N && /* @__PURE__ */ m("div", {
|
|
203
204
|
className: "mt-1 bg-bg-surface border border-border-default rounded-md shadow-lg",
|
|
204
205
|
children: [
|
|
205
|
-
/* @__PURE__ */
|
|
206
|
+
/* @__PURE__ */ m("div", {
|
|
206
207
|
className: "flex items-center gap-2 p-2 border-b border-border-subtle",
|
|
207
208
|
children: [
|
|
208
|
-
/* @__PURE__ */
|
|
209
|
+
/* @__PURE__ */ p("span", {
|
|
209
210
|
"aria-hidden": "true",
|
|
210
211
|
className: "w-6 h-6 rounded-full border-2 border-border-default",
|
|
211
|
-
style: { backgroundColor:
|
|
212
|
+
style: { backgroundColor: V }
|
|
212
213
|
}),
|
|
213
|
-
/* @__PURE__ */
|
|
214
|
+
/* @__PURE__ */ p("input", {
|
|
214
215
|
type: "text",
|
|
215
|
-
value:
|
|
216
|
-
onChange: (e) =>
|
|
216
|
+
value: z,
|
|
217
|
+
onChange: (e) => B(e.target.value),
|
|
217
218
|
onKeyDown: (e) => {
|
|
218
|
-
e.key === "Enter" && (e.preventDefault(),
|
|
219
|
+
e.key === "Enter" && (e.preventDefault(), G());
|
|
219
220
|
},
|
|
220
221
|
placeholder: "New tag name... (Enter to create)",
|
|
221
|
-
disabled:
|
|
222
|
+
disabled: U,
|
|
222
223
|
className: "flex-1 px-2 py-1 text-sm bg-bg-sunken border border-border-default rounded text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-1 focus:ring-action-primary-bg disabled:opacity-50"
|
|
223
224
|
}),
|
|
224
|
-
/* @__PURE__ */
|
|
225
|
+
/* @__PURE__ */ p("button", {
|
|
225
226
|
type: "button",
|
|
226
|
-
onClick:
|
|
227
|
-
disabled: !
|
|
227
|
+
onClick: G,
|
|
228
|
+
disabled: !z.trim() || U,
|
|
228
229
|
className: "p-1 text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors disabled:opacity-30",
|
|
229
|
-
children: /* @__PURE__ */
|
|
230
|
+
children: /* @__PURE__ */ p(l, { className: "w-4 h-4" })
|
|
230
231
|
})
|
|
231
232
|
]
|
|
232
233
|
}),
|
|
233
|
-
/* @__PURE__ */
|
|
234
|
+
/* @__PURE__ */ p("div", {
|
|
234
235
|
className: "flex items-center gap-1 px-2 py-1.5 border-b border-border-subtle",
|
|
235
|
-
children:
|
|
236
|
+
children: h.map((e) => /* @__PURE__ */ p("button", {
|
|
236
237
|
type: "button",
|
|
237
|
-
onClick: () =>
|
|
238
|
-
className: `w-4 h-4 rounded-full transition-transform ${
|
|
238
|
+
onClick: () => H(e),
|
|
239
|
+
className: `w-4 h-4 rounded-full transition-transform ${V === e ? "ring-2 ring-action-primary-bg scale-125" : "hover:scale-110"}`,
|
|
239
240
|
style: { backgroundColor: e }
|
|
240
241
|
}, e))
|
|
241
242
|
}),
|
|
242
|
-
/* @__PURE__ */
|
|
243
|
+
/* @__PURE__ */ p("div", {
|
|
243
244
|
className: "max-h-32 overflow-y-auto p-1",
|
|
244
|
-
children:
|
|
245
|
+
children: L.length === 0 ? /* @__PURE__ */ p("p", {
|
|
245
246
|
className: "text-xs text-text-muted px-2 py-1",
|
|
246
247
|
children: "No tags yet — create one above"
|
|
247
|
-
}) :
|
|
248
|
+
}) : L.map((e) => /* @__PURE__ */ m("button", {
|
|
248
249
|
type: "button",
|
|
249
|
-
onClick: () =>
|
|
250
|
+
onClick: () => K(e.id),
|
|
250
251
|
className: "flex items-center justify-between w-full px-2 py-1.5 text-sm text-text-primary hover:bg-bg-sunken rounded transition-colors",
|
|
251
|
-
children: [/* @__PURE__ */
|
|
252
|
+
children: [/* @__PURE__ */ m("span", {
|
|
252
253
|
className: "flex items-center gap-2",
|
|
253
|
-
children: [e.color && /* @__PURE__ */
|
|
254
|
+
children: [e.color && /* @__PURE__ */ p("span", {
|
|
254
255
|
className: "w-2.5 h-2.5 rounded-full",
|
|
255
256
|
style: { backgroundColor: e.color }
|
|
256
257
|
}), e.label]
|
|
257
|
-
}),
|
|
258
|
+
}), k.has(e.id) && /* @__PURE__ */ p(o, { className: "w-4 h-4 text-action-primary-bg" })]
|
|
258
259
|
}, e.id))
|
|
259
260
|
})
|
|
260
261
|
]
|
|
261
262
|
})
|
|
262
263
|
] })]
|
|
263
264
|
}),
|
|
264
|
-
/* @__PURE__ */
|
|
265
|
+
/* @__PURE__ */ p("div", {
|
|
265
266
|
className: "flex-1 min-h-[300px]",
|
|
266
|
-
children:
|
|
267
|
+
children: T ? /* @__PURE__ */ p("div", {
|
|
267
268
|
className: "h-full p-4 bg-bg-sunken rounded-md prose prose-sm dark:prose-invert max-w-none overflow-y-auto",
|
|
268
269
|
children: ((e) => {
|
|
269
270
|
let t = e.split("\n"), n = [], r = 0;
|
|
@@ -272,11 +273,11 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
272
273
|
if (e.startsWith("```")) {
|
|
273
274
|
let i = e.slice(3).trim(), a = [];
|
|
274
275
|
for (r++; r < t.length && !t[r].startsWith("```");) a.push(t[r]), r++;
|
|
275
|
-
n.push(/* @__PURE__ */
|
|
276
|
+
n.push(/* @__PURE__ */ p("pre", {
|
|
276
277
|
className: "bg-bg-elevated rounded-md p-3 my-2 overflow-x-auto",
|
|
277
|
-
children: /* @__PURE__ */
|
|
278
|
+
children: /* @__PURE__ */ m("code", {
|
|
278
279
|
className: "text-sm font-mono text-text-primary",
|
|
279
|
-
children: [i && /* @__PURE__ */
|
|
280
|
+
children: [i && /* @__PURE__ */ p("span", {
|
|
280
281
|
className: "text-xs text-text-secondary block mb-2",
|
|
281
282
|
children: i
|
|
282
283
|
}), a.join("\n")]
|
|
@@ -285,40 +286,40 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
285
286
|
continue;
|
|
286
287
|
}
|
|
287
288
|
if (e.startsWith("# ")) {
|
|
288
|
-
n.push(/* @__PURE__ */
|
|
289
|
+
n.push(/* @__PURE__ */ p("h1", {
|
|
289
290
|
className: "text-2xl font-bold mb-2 mt-4",
|
|
290
|
-
children:
|
|
291
|
+
children: J(e.slice(2))
|
|
291
292
|
}, n.length)), r++;
|
|
292
293
|
continue;
|
|
293
294
|
}
|
|
294
295
|
if (e.startsWith("## ")) {
|
|
295
|
-
n.push(/* @__PURE__ */
|
|
296
|
+
n.push(/* @__PURE__ */ p("h2", {
|
|
296
297
|
className: "text-xl font-bold mb-2 mt-3",
|
|
297
|
-
children:
|
|
298
|
+
children: J(e.slice(3))
|
|
298
299
|
}, n.length)), r++;
|
|
299
300
|
continue;
|
|
300
301
|
}
|
|
301
302
|
if (e.startsWith("### ")) {
|
|
302
|
-
n.push(/* @__PURE__ */
|
|
303
|
+
n.push(/* @__PURE__ */ p("h3", {
|
|
303
304
|
className: "text-lg font-bold mb-2 mt-2",
|
|
304
|
-
children:
|
|
305
|
+
children: J(e.slice(4))
|
|
305
306
|
}, n.length)), r++;
|
|
306
307
|
continue;
|
|
307
308
|
}
|
|
308
309
|
if (e.startsWith("> ")) {
|
|
309
|
-
n.push(/* @__PURE__ */
|
|
310
|
+
n.push(/* @__PURE__ */ p("blockquote", {
|
|
310
311
|
className: "border-l-4 border-border-default pl-4 my-2 italic text-text-secondary",
|
|
311
|
-
children:
|
|
312
|
+
children: J(e.slice(2))
|
|
312
313
|
}, n.length)), r++;
|
|
313
314
|
continue;
|
|
314
315
|
}
|
|
315
316
|
if (e.match(/^[-*] /)) {
|
|
316
317
|
let e = [];
|
|
317
|
-
for (; r < t.length && t[r].match(/^[-*] /);) e.push(/* @__PURE__ */
|
|
318
|
+
for (; r < t.length && t[r].match(/^[-*] /);) e.push(/* @__PURE__ */ p("li", {
|
|
318
319
|
className: "list-disc",
|
|
319
|
-
children:
|
|
320
|
+
children: J(t[r].slice(2))
|
|
320
321
|
}, e.length)), r++;
|
|
321
|
-
n.push(/* @__PURE__ */
|
|
322
|
+
n.push(/* @__PURE__ */ p("ul", {
|
|
322
323
|
className: "ml-6 my-2",
|
|
323
324
|
children: e
|
|
324
325
|
}, n.length));
|
|
@@ -329,19 +330,19 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
329
330
|
for (; r < t.length;) {
|
|
330
331
|
let n = t[r].match(/^(\d+)\. /);
|
|
331
332
|
if (!n) break;
|
|
332
|
-
e.push(/* @__PURE__ */
|
|
333
|
+
e.push(/* @__PURE__ */ p("li", {
|
|
333
334
|
className: "list-decimal",
|
|
334
|
-
children:
|
|
335
|
+
children: J(t[r].slice(n[0].length))
|
|
335
336
|
}, e.length)), r++;
|
|
336
337
|
}
|
|
337
|
-
n.push(/* @__PURE__ */
|
|
338
|
+
n.push(/* @__PURE__ */ p("ol", {
|
|
338
339
|
className: "ml-6 my-2",
|
|
339
340
|
children: e
|
|
340
341
|
}, n.length));
|
|
341
342
|
continue;
|
|
342
343
|
}
|
|
343
344
|
if (e.match(/^(-{3,}|\*{3,})$/)) {
|
|
344
|
-
n.push(/* @__PURE__ */
|
|
345
|
+
n.push(/* @__PURE__ */ p("hr", { className: "my-4 border-border-default" }, n.length)), r++;
|
|
345
346
|
continue;
|
|
346
347
|
}
|
|
347
348
|
if (e.startsWith("|") && e.endsWith("|")) {
|
|
@@ -350,19 +351,19 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
350
351
|
let a = i.filter((e) => !e.match(/^\|[\s\-:|]+\|$/)).map((e) => e.slice(1, -1).split("|").map((e) => e.trim()));
|
|
351
352
|
if (a.length > 0) {
|
|
352
353
|
let [e, ...t] = a;
|
|
353
|
-
n.push(/* @__PURE__ */
|
|
354
|
+
n.push(/* @__PURE__ */ m("table", {
|
|
354
355
|
className: "w-full my-2 border-collapse text-sm",
|
|
355
|
-
children: [/* @__PURE__ */
|
|
356
|
+
children: [/* @__PURE__ */ p("thead", { children: /* @__PURE__ */ p("tr", {
|
|
356
357
|
className: "border-b border-border-default",
|
|
357
|
-
children: e.map((e, t) => /* @__PURE__ */
|
|
358
|
+
children: e.map((e, t) => /* @__PURE__ */ p("th", {
|
|
358
359
|
className: "text-left py-2 px-3 font-semibold text-text-primary",
|
|
359
|
-
children:
|
|
360
|
+
children: J(e)
|
|
360
361
|
}, t))
|
|
361
|
-
}) }), /* @__PURE__ */
|
|
362
|
+
}) }), /* @__PURE__ */ p("tbody", { children: t.map((e, t) => /* @__PURE__ */ p("tr", {
|
|
362
363
|
className: "border-b border-border-subtle",
|
|
363
|
-
children: e.map((e, t) => /* @__PURE__ */
|
|
364
|
+
children: e.map((e, t) => /* @__PURE__ */ p("td", {
|
|
364
365
|
className: "py-2 px-3 text-text-primary",
|
|
365
|
-
children:
|
|
366
|
+
children: J(e)
|
|
366
367
|
}, t))
|
|
367
368
|
}, t)) })]
|
|
368
369
|
}, n.length));
|
|
@@ -370,57 +371,57 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
370
371
|
continue;
|
|
371
372
|
}
|
|
372
373
|
if (e === "") {
|
|
373
|
-
n.push(/* @__PURE__ */
|
|
374
|
+
n.push(/* @__PURE__ */ p("br", {}, n.length)), r++;
|
|
374
375
|
continue;
|
|
375
376
|
}
|
|
376
|
-
n.push(/* @__PURE__ */
|
|
377
|
+
n.push(/* @__PURE__ */ p("p", {
|
|
377
378
|
className: "mb-1",
|
|
378
|
-
children:
|
|
379
|
+
children: J(e)
|
|
379
380
|
}, n.length)), r++;
|
|
380
381
|
}
|
|
381
382
|
return n;
|
|
382
|
-
})(
|
|
383
|
-
}) : /* @__PURE__ */
|
|
384
|
-
value:
|
|
385
|
-
onChange: (e) =>
|
|
386
|
-
...
|
|
383
|
+
})(D.content)
|
|
384
|
+
}) : /* @__PURE__ */ p("textarea", {
|
|
385
|
+
value: D.content,
|
|
386
|
+
onChange: (e) => O({
|
|
387
|
+
...D,
|
|
387
388
|
content: e.target.value
|
|
388
389
|
}),
|
|
389
|
-
placeholder:
|
|
390
|
+
placeholder: C("vibecontrols.notes.contentPlaceholder", "Write your note in Markdown..."),
|
|
390
391
|
className: "w-full h-full min-h-[300px] px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none font-mono"
|
|
391
392
|
})
|
|
392
393
|
}),
|
|
393
|
-
/* @__PURE__ */
|
|
394
|
+
/* @__PURE__ */ m("div", {
|
|
394
395
|
className: "flex items-center gap-2",
|
|
395
|
-
children: [/* @__PURE__ */
|
|
396
|
+
children: [/* @__PURE__ */ p("input", {
|
|
396
397
|
type: "checkbox",
|
|
397
398
|
id: "isPinned",
|
|
398
|
-
checked:
|
|
399
|
-
onChange: (e) =>
|
|
400
|
-
...
|
|
399
|
+
checked: D.isPinned,
|
|
400
|
+
onChange: (e) => O({
|
|
401
|
+
...D,
|
|
401
402
|
isPinned: e.target.checked
|
|
402
403
|
}),
|
|
403
404
|
className: "w-4 h-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg"
|
|
404
|
-
}), /* @__PURE__ */
|
|
405
|
+
}), /* @__PURE__ */ p("label", {
|
|
405
406
|
htmlFor: "isPinned",
|
|
406
407
|
className: "text-sm text-text-primary",
|
|
407
|
-
children:
|
|
408
|
+
children: C("vibecontrols.notes.pinThisNote", "Pin this note")
|
|
408
409
|
})]
|
|
409
410
|
})
|
|
410
411
|
]
|
|
411
|
-
}), /* @__PURE__ */
|
|
412
|
+
}), /* @__PURE__ */ m("div", {
|
|
412
413
|
className: "flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken",
|
|
413
|
-
children: [/* @__PURE__ */
|
|
414
|
+
children: [/* @__PURE__ */ p("button", {
|
|
414
415
|
type: "button",
|
|
415
|
-
onClick:
|
|
416
|
-
disabled:
|
|
416
|
+
onClick: x,
|
|
417
|
+
disabled: S,
|
|
417
418
|
className: "px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50",
|
|
418
|
-
children:
|
|
419
|
-
}), /* @__PURE__ */
|
|
419
|
+
children: C("vibecontrols.notes.cancel", "Cancel")
|
|
420
|
+
}), /* @__PURE__ */ m("button", {
|
|
420
421
|
type: "submit",
|
|
421
|
-
disabled:
|
|
422
|
+
disabled: S,
|
|
422
423
|
className: "inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
423
|
-
children: [/* @__PURE__ */
|
|
424
|
+
children: [/* @__PURE__ */ p(u, { className: "w-4 h-4" }), S ? C("vibecontrols.notes.saving", "Saving...") : C("vibecontrols.notes.saveNote", "Save Note")]
|
|
424
425
|
})]
|
|
425
426
|
})]
|
|
426
427
|
})]
|
|
@@ -428,6 +429,6 @@ function h({ note: h, vibeOptions: g = [], defaultVibeId: _, hideVibeSelect: v,
|
|
|
428
429
|
});
|
|
429
430
|
}
|
|
430
431
|
//#endregion
|
|
431
|
-
export {
|
|
432
|
+
export { g as NoteEditor };
|
|
432
433
|
|
|
433
434
|
//# sourceMappingURL=NoteEditor.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"NoteEditor.js","names":[],"sources":["../../../src/components/notes/NoteEditor.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { X, Save, Eye, Edit, Tag, Check, Plus } from 'lucide-react';\nimport type { CreateNoteInput, Note, NoteTagAssignment } from './types';\nimport { useAvailableNoteTagsQuery, useCreateNoteTagMutation } from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\n\n/** Tag selected in the editor */\nexport interface SelectedTag {\n id: string;\n key: string;\n label: string;\n color: string | null;\n}\n\n/** Note data submitted from the editor — includes `id` and selected tag IDs */\nexport type NoteEditorSubmitData = CreateNoteInput & { id?: string; selectedTagIds?: string[] };\n\nconst TAG_COLORS = [\n 'var(--color-status-error-text)',\n 'var(--color-status-warning-text)',\n 'var(--color-status-success-text)',\n 'var(--color-accent-teal)',\n 'var(--color-text-link)',\n 'var(--color-accent-purple)',\n 'var(--color-text-tertiary)',\n];\n\ninterface NoteEditorProps {\n note?: Note;\n vibeOptions?: { id: string; name: string }[];\n /** Pre-set vibeId when creating a new note (used in vibe-scoped context) */\n defaultVibeId?: string;\n /** Hide the vibe dropdown (used when notes are scoped to a single vibe) */\n hideVibeSelect?: boolean;\n onSubmit: (data: NoteEditorSubmitData) => void;\n onCancel: () => void;\n isLoading?: boolean;\n}\n\nexport function NoteEditor({\n note,\n vibeOptions = [],\n defaultVibeId,\n hideVibeSelect,\n onSubmit,\n onCancel,\n isLoading,\n}: NoteEditorProps) {\n const tr = useTr();\n const [isPreview, setIsPreview] = useState(false);\n const [formData, setFormData] = useState({\n title: note?.title || '',\n content: note?.content || '',\n vibeId: note?.vibeId || defaultVibeId || '',\n isPinned: note?.isPinned ?? false,\n });\n\n // Tag picker state\n const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set());\n const [tagsInitialized, setTagsInitialized] = useState(false);\n const [showTagPicker, setShowTagPicker] = useState(false);\n\n // Fetch available tags from wspace-tags-svc\n const { data: tagsData, refetch: refetchTags } = useAvailableNoteTagsQuery();\n const availableTags: SelectedTag[] = (tagsData?.availableNoteTags ?? []).map(\n (t: { id: string; key: string; label: string; color?: string | null }) => ({\n id: t.id,\n key: t.key,\n label: t.label,\n color: t.color ?? null,\n })\n );\n\n // Initialize selected tags once available tags load\n useEffect(() => {\n if (tagsInitialized || availableTags.length === 0) return;\n const ids = new Set<string>();\n\n // First: pre-select from tagAssignments (tags service data)\n if (note?.tagAssignments && note.tagAssignments.length > 0) {\n note.tagAssignments.forEach((a: NoteTagAssignment) => ids.add(a.tagId));\n }\n // Fallback: match legacy string tags by label/key to available tags\n else if (note?.tags && note.tags.length > 0) {\n for (const legacyTag of note.tags) {\n const match = availableTags.find(\n (t) =>\n t.label.toLowerCase() === legacyTag.toLowerCase() || t.key === legacyTag.toLowerCase()\n );\n if (match) ids.add(match.id);\n }\n }\n\n if (ids.size > 0) setSelectedTagIds(ids);\n setTagsInitialized(true);\n }, [availableTags, note, tagsInitialized]);\n\n // Inline tag creation\n const [createNoteTag] = useCreateNoteTagMutation();\n const [newTagLabel, setNewTagLabel] = useState('');\n const [newTagColor, setNewTagColor] = useState('var(--color-accent-purple)');\n const [isCreatingTag, setIsCreatingTag] = useState(false);\n\n const handleCreateTag = async () => {\n const label = newTagLabel.trim();\n if (!label || isCreatingTag) return;\n setIsCreatingTag(true);\n try {\n const result = await createNoteTag({\n variables: { input: { label, color: newTagColor } },\n });\n const newTag = result.data?.createNoteTag;\n if (newTag) {\n setSelectedTagIds((prev) => new Set(prev).add(newTag.id));\n await refetchTags();\n }\n setNewTagLabel('');\n } catch (err) {\n console.error('Failed to create tag:', err);\n } finally {\n setIsCreatingTag(false);\n }\n };\n\n const toggleTag = (tagId: string) => {\n setSelectedTagIds((prev) => {\n const next = new Set(prev);\n if (next.has(tagId)) {\n next.delete(tagId);\n } else {\n next.add(tagId);\n }\n return next;\n });\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const data: NoteEditorSubmitData = {\n ...(note?.id ? { id: note.id } : {}),\n workspaceId: 'ws-demo-1',\n title: formData.title,\n content: formData.content,\n vibeId: formData.vibeId || undefined,\n isPinned: formData.isPinned,\n selectedTagIds: Array.from(selectedTagIds),\n };\n onSubmit(data);\n };\n\n // Enhanced markdown preview with inline formatting support\n const parseInlineFormatting = (text: string): React.ReactNode[] => {\n const parts: React.ReactNode[] = [];\n let remaining = text;\n let key = 0;\n\n while (remaining.length > 0) {\n // Bold: **text** or __text__\n const boldMatch = remaining.match(/^(\\*\\*|__)(.+?)\\1/);\n if (boldMatch) {\n parts.push(<strong key={key++}>{parseInlineFormatting(boldMatch[2])}</strong>);\n remaining = remaining.slice(boldMatch[0].length);\n continue;\n }\n\n // Italic: *text* or _text_\n const italicMatch = remaining.match(/^(\\*|_)([^*_]+?)\\1/);\n if (italicMatch) {\n parts.push(<em key={key++}>{parseInlineFormatting(italicMatch[2])}</em>);\n remaining = remaining.slice(italicMatch[0].length);\n continue;\n }\n\n // Strikethrough: ~~text~~\n const strikeMatch = remaining.match(/^~~(.+?)~~/);\n if (strikeMatch) {\n parts.push(<del key={key++}>{parseInlineFormatting(strikeMatch[1])}</del>);\n remaining = remaining.slice(strikeMatch[0].length);\n continue;\n }\n\n // Inline code: `code`\n const codeMatch = remaining.match(/^`([^`]+)`/);\n if (codeMatch) {\n parts.push(\n <code\n key={key++}\n className=\"bg-bg-elevated px-1.5 py-0.5 rounded text-sm font-mono text-text-primary\"\n >\n {codeMatch[1]}\n </code>\n );\n remaining = remaining.slice(codeMatch[0].length);\n continue;\n }\n\n // Links: [text](url) - only allow safe URL schemes to prevent XSS\n const linkMatch = remaining.match(/^\\[([^\\]]+)\\]\\(([^)]+)\\)/);\n if (linkMatch) {\n const url = linkMatch[2];\n // Only allow http, https, mailto, relative paths, and hash links to prevent XSS via javascript: URLs\n const isSafeUrl = /^(https?:\\/\\/|mailto:|\\/|#)/i.test(url);\n if (isSafeUrl) {\n parts.push(\n <a\n key={key++}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-action-primary-bg hover:underline\"\n >\n {linkMatch[1]}\n </a>\n );\n } else {\n // Render unsafe links as plain text with muted styling\n parts.push(\n <span key={key++} className=\"text-text-secondary\">\n {linkMatch[1]}\n </span>\n );\n }\n remaining = remaining.slice(linkMatch[0].length);\n continue;\n }\n\n // No match - take the next character\n parts.push(remaining[0]);\n remaining = remaining.slice(1);\n }\n\n return parts;\n };\n\n // Parse markdown content into React elements\n const renderMarkdown = (text: string) => {\n const lines = text.split('\\n');\n const elements: React.ReactNode[] = [];\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n\n // Code blocks: ```\n if (line.startsWith('```')) {\n const language = line.slice(3).trim();\n const codeLines: string[] = [];\n i++;\n while (i < lines.length && !lines[i].startsWith('```')) {\n codeLines.push(lines[i]);\n i++;\n }\n elements.push(\n <pre key={elements.length} className=\"bg-bg-elevated rounded-md p-3 my-2 overflow-x-auto\">\n <code className=\"text-sm font-mono text-text-primary\">\n {language && (\n <span className=\"text-xs text-text-secondary block mb-2\">{language}</span>\n )}\n {codeLines.join('\\n')}\n </code>\n </pre>\n );\n i++;\n continue;\n }\n\n // Headings\n if (line.startsWith('# ')) {\n elements.push(\n <h1 key={elements.length} className=\"text-2xl font-bold mb-2 mt-4\">\n {parseInlineFormatting(line.slice(2))}\n </h1>\n );\n i++;\n continue;\n }\n if (line.startsWith('## ')) {\n elements.push(\n <h2 key={elements.length} className=\"text-xl font-bold mb-2 mt-3\">\n {parseInlineFormatting(line.slice(3))}\n </h2>\n );\n i++;\n continue;\n }\n if (line.startsWith('### ')) {\n elements.push(\n <h3 key={elements.length} className=\"text-lg font-bold mb-2 mt-2\">\n {parseInlineFormatting(line.slice(4))}\n </h3>\n );\n i++;\n continue;\n }\n\n // Blockquotes: > text\n if (line.startsWith('> ')) {\n elements.push(\n <blockquote\n key={elements.length}\n className=\"border-l-4 border-border-default pl-4 my-2 italic text-text-secondary\"\n >\n {parseInlineFormatting(line.slice(2))}\n </blockquote>\n );\n i++;\n continue;\n }\n\n // Unordered list: - or * (collect consecutive items and wrap in <ul>)\n if (line.match(/^[-*] /)) {\n const listItems: React.ReactNode[] = [];\n while (i < lines.length && lines[i].match(/^[-*] /)) {\n listItems.push(\n <li key={listItems.length} className=\"list-disc\">\n {parseInlineFormatting(lines[i].slice(2))}\n </li>\n );\n i++;\n }\n elements.push(\n <ul key={elements.length} className=\"ml-6 my-2\">\n {listItems}\n </ul>\n );\n continue;\n }\n\n // Ordered list: 1. 2. etc. (collect consecutive items and wrap in <ol>)\n const orderedMatch = line.match(/^(\\d+)\\. /);\n if (orderedMatch) {\n const listItems: React.ReactNode[] = [];\n while (i < lines.length) {\n const olMatch = lines[i].match(/^(\\d+)\\. /);\n if (!olMatch) break;\n listItems.push(\n <li key={listItems.length} className=\"list-decimal\">\n {parseInlineFormatting(lines[i].slice(olMatch[0].length))}\n </li>\n );\n i++;\n }\n elements.push(\n <ol key={elements.length} className=\"ml-6 my-2\">\n {listItems}\n </ol>\n );\n continue;\n }\n\n // Horizontal rule: --- or ***\n if (line.match(/^(-{3,}|\\*{3,})$/)) {\n elements.push(<hr key={elements.length} className=\"my-4 border-border-default\" />);\n i++;\n continue;\n }\n\n // Table detection (simple): | col1 | col2 |\n if (line.startsWith('|') && line.endsWith('|')) {\n const tableLines: string[] = [line];\n i++;\n while (i < lines.length && lines[i].startsWith('|') && lines[i].endsWith('|')) {\n tableLines.push(lines[i]);\n i++;\n }\n\n // Parse table\n const rows = tableLines\n .filter((l) => !l.match(/^\\|[\\s\\-:|]+\\|$/)) // Skip separator rows (including alignment markers like :---:)\n .map((l) =>\n l\n .slice(1, -1)\n .split('|')\n .map((cell) => cell.trim())\n );\n\n if (rows.length > 0) {\n const [header, ...body] = rows;\n elements.push(\n <table key={elements.length} className=\"w-full my-2 border-collapse text-sm\">\n <thead>\n <tr className=\"border-b border-border-default\">\n {header.map((cell, ci) => (\n <th key={ci} className=\"text-left py-2 px-3 font-semibold text-text-primary\">\n {parseInlineFormatting(cell)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {body.map((row, ri) => (\n <tr key={ri} className=\"border-b border-border-subtle\">\n {row.map((cell, ci) => (\n <td key={ci} className=\"py-2 px-3 text-text-primary\">\n {parseInlineFormatting(cell)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n );\n }\n continue;\n }\n\n // Empty line\n if (line === '') {\n elements.push(<br key={elements.length} />);\n i++;\n continue;\n }\n\n // Regular paragraph with inline formatting\n elements.push(\n <p key={elements.length} className=\"mb-1\">\n {parseInlineFormatting(line)}\n </p>\n );\n i++;\n }\n\n return elements;\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-3xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {note\n ? tr('vibecontrols.notes.editNote', 'Edit Note')\n : tr('vibecontrols.notes.newNote', 'New Note')}\n </h2>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsPreview(!isPreview)}\n className={`inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md transition-colors ${\n isPreview\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-elevated'\n }`}\n >\n {isPreview ? <Edit className=\"w-4 h-4\" /> : <Eye className=\"w-4 h-4\" />}\n {isPreview\n ? tr('vibecontrols.notes.edit', 'Edit')\n : tr('vibecontrols.notes.preview', 'Preview')}\n </button>\n <button onClick={onCancel} className=\"p-1 rounded hover:bg-bg-sunken transition-colors\">\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n <form onSubmit={handleSubmit} className=\"flex-1 flex flex-col overflow-hidden\">\n <div className=\"p-6 space-y-4 flex-1 overflow-y-auto\">\n <div>\n <input\n type=\"text\"\n value={formData.title}\n onChange={(e) => setFormData({ ...formData, title: e.target.value })}\n placeholder={tr('vibecontrols.notes.titlePlaceholder', 'Note title...')}\n required\n className=\"w-full px-0 py-2 text-lg sm:text-2xl font-bold bg-transparent border-0 border-b border-border-default text-text-primary placeholder:text-text-placeholder focus:outline-none focus:border-action-primary-bg\"\n />\n </div>\n\n <div className={`grid ${hideVibeSelect ? 'grid-cols-1' : 'grid-cols-2'} gap-4`}>\n {!hideVibeSelect && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.notes.relatedVibe', 'Related Vibe (optional)')}\n </label>\n <select\n value={formData.vibeId}\n onChange={(e) => setFormData({ ...formData, vibeId: e.target.value })}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">No vibe</option>\n {vibeOptions.map((vibe) => (\n <option key={vibe.id} value={vibe.id}>\n {vibe.name}\n </option>\n ))}\n </select>\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.notes.tags', 'Tags')}\n </label>\n {/* Selected tags */}\n <div className=\"flex flex-wrap gap-1.5 min-h-[38px] px-3 py-2 bg-bg-sunken border border-border-default rounded-md\">\n {availableTags\n .filter((t) => selectedTagIds.has(t.id))\n .map((tag) => (\n <span\n key={tag.id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-action-primary-bg/10 text-action-primary-bg rounded\"\n >\n <Tag className=\"w-3 h-3\" />\n {tag.label}\n <button\n type=\"button\"\n onClick={() => toggleTag(tag.id)}\n className=\"ml-0.5 hover:text-status-error-text\"\n >\n <X className=\"w-3 h-3\" />\n </button>\n </span>\n ))}\n <button\n type=\"button\"\n onClick={() => setShowTagPicker(!showTagPicker)}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs text-text-secondary hover:text-text-primary hover:bg-bg-elevated rounded transition-colors\"\n >\n <Tag className=\"w-3 h-3\" />\n {selectedTagIds.size === 0 ? 'Add tags...' : '+'}\n </button>\n </div>\n {/* Tag picker dropdown */}\n {showTagPicker && (\n <div className=\"mt-1 bg-bg-surface border border-border-default rounded-md shadow-lg\">\n {/* Inline create: text input + color picker */}\n <div className=\"flex items-center gap-2 p-2 border-b border-border-subtle\">\n <span\n aria-hidden=\"true\"\n className=\"w-6 h-6 rounded-full border-2 border-border-default\"\n style={{ backgroundColor: newTagColor }}\n />\n <input\n type=\"text\"\n value={newTagLabel}\n onChange={(e) => setNewTagLabel(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleCreateTag();\n }\n }}\n placeholder=\"New tag name... (Enter to create)\"\n disabled={isCreatingTag}\n className=\"flex-1 px-2 py-1 text-sm bg-bg-sunken border border-border-default rounded text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-1 focus:ring-action-primary-bg disabled:opacity-50\"\n />\n <button\n type=\"button\"\n onClick={handleCreateTag}\n disabled={!newTagLabel.trim() || isCreatingTag}\n className=\"p-1 text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors disabled:opacity-30\"\n >\n <Plus className=\"w-4 h-4\" />\n </button>\n </div>\n {/* Quick color palette */}\n <div className=\"flex items-center gap-1 px-2 py-1.5 border-b border-border-subtle\">\n {TAG_COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => setNewTagColor(c)}\n className={`w-4 h-4 rounded-full transition-transform ${newTagColor === c ? 'ring-2 ring-action-primary-bg scale-125' : 'hover:scale-110'}`}\n style={{ backgroundColor: c }}\n />\n ))}\n </div>\n {/* Existing tags list */}\n <div className=\"max-h-32 overflow-y-auto p-1\">\n {availableTags.length === 0 ? (\n <p className=\"text-xs text-text-muted px-2 py-1\">\n No tags yet — create one above\n </p>\n ) : (\n availableTags.map((tag) => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => toggleTag(tag.id)}\n className=\"flex items-center justify-between w-full px-2 py-1.5 text-sm text-text-primary hover:bg-bg-sunken rounded transition-colors\"\n >\n <span className=\"flex items-center gap-2\">\n {tag.color && (\n <span\n className=\"w-2.5 h-2.5 rounded-full\"\n style={{ backgroundColor: tag.color }}\n />\n )}\n {tag.label}\n </span>\n {selectedTagIds.has(tag.id) && (\n <Check className=\"w-4 h-4 text-action-primary-bg\" />\n )}\n </button>\n ))\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"flex-1 min-h-[300px]\">\n {isPreview ? (\n <div className=\"h-full p-4 bg-bg-sunken rounded-md prose prose-sm dark:prose-invert max-w-none overflow-y-auto\">\n {renderMarkdown(formData.content)}\n </div>\n ) : (\n <textarea\n value={formData.content}\n onChange={(e) => setFormData({ ...formData, content: e.target.value })}\n placeholder={tr(\n 'vibecontrols.notes.contentPlaceholder',\n 'Write your note in Markdown...'\n )}\n className=\"w-full h-full min-h-[300px] px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none font-mono\"\n />\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id=\"isPinned\"\n checked={formData.isPinned}\n onChange={(e) => setFormData({ ...formData, isPinned: e.target.checked })}\n className=\"w-4 h-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n <label htmlFor=\"isPinned\" className=\"text-sm text-text-primary\">\n {tr('vibecontrols.notes.pinThisNote', 'Pin this note')}\n </label>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.notes.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Save className=\"w-4 h-4\" />\n {isLoading\n ? tr('vibecontrols.notes.saving', 'Saving...')\n : tr('vibecontrols.notes.saveNote', 'Save Note')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"NoteEditor.js","names":[],"sources":["../../../src/components/notes/NoteEditor.tsx"],"sourcesContent":["import { useState, useEffect } from 'react';\nimport { X, Save, Eye, Edit, Tag, Check, Plus } from 'lucide-react';\nimport type { CreateNoteInput, Note, NoteTagAssignment } from './types';\nimport { useAvailableNoteTagsQuery, useCreateNoteTagMutation } from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { useVibeControls } from '../../providers/VibeControlsProvider';\n\n/** Tag selected in the editor */\nexport interface SelectedTag {\n id: string;\n key: string;\n label: string;\n color: string | null;\n}\n\n/** Note data submitted from the editor — includes `id` and selected tag IDs */\nexport type NoteEditorSubmitData = CreateNoteInput & { id?: string; selectedTagIds?: string[] };\n\nconst TAG_COLORS = [\n 'var(--color-status-error-text)',\n 'var(--color-status-warning-text)',\n 'var(--color-status-success-text)',\n 'var(--color-accent-teal)',\n 'var(--color-text-link)',\n 'var(--color-accent-purple)',\n 'var(--color-text-tertiary)',\n];\n\ninterface NoteEditorProps {\n note?: Note;\n vibeOptions?: { id: string; name: string }[];\n /** Pre-set vibeId when creating a new note (used in vibe-scoped context) */\n defaultVibeId?: string;\n /** Hide the vibe dropdown (used when notes are scoped to a single vibe) */\n hideVibeSelect?: boolean;\n onSubmit: (data: NoteEditorSubmitData) => void;\n onCancel: () => void;\n isLoading?: boolean;\n}\n\nexport function NoteEditor({\n note,\n vibeOptions = [],\n defaultVibeId,\n hideVibeSelect,\n onSubmit,\n onCancel,\n isLoading,\n}: NoteEditorProps) {\n const tr = useTr();\n const { workspaceId } = useVibeControls();\n const [isPreview, setIsPreview] = useState(false);\n const [formData, setFormData] = useState({\n title: note?.title || '',\n content: note?.content || '',\n vibeId: note?.vibeId || defaultVibeId || '',\n isPinned: note?.isPinned ?? false,\n });\n\n // Tag picker state\n const [selectedTagIds, setSelectedTagIds] = useState<Set<string>>(new Set());\n const [tagsInitialized, setTagsInitialized] = useState(false);\n const [showTagPicker, setShowTagPicker] = useState(false);\n\n // Fetch available tags from wspace-tags-svc\n const { data: tagsData, refetch: refetchTags } = useAvailableNoteTagsQuery();\n const availableTags: SelectedTag[] = (tagsData?.availableNoteTags ?? []).map(\n (t: { id: string; key: string; label: string; color?: string | null }) => ({\n id: t.id,\n key: t.key,\n label: t.label,\n color: t.color ?? null,\n })\n );\n\n // Initialize selected tags once available tags load\n useEffect(() => {\n if (tagsInitialized || availableTags.length === 0) return;\n const ids = new Set<string>();\n\n // First: pre-select from tagAssignments (tags service data)\n if (note?.tagAssignments && note.tagAssignments.length > 0) {\n note.tagAssignments.forEach((a: NoteTagAssignment) => ids.add(a.tagId));\n }\n // Fallback: match legacy string tags by label/key to available tags\n else if (note?.tags && note.tags.length > 0) {\n for (const legacyTag of note.tags) {\n const match = availableTags.find(\n (t) =>\n t.label.toLowerCase() === legacyTag.toLowerCase() || t.key === legacyTag.toLowerCase()\n );\n if (match) ids.add(match.id);\n }\n }\n\n if (ids.size > 0) setSelectedTagIds(ids);\n setTagsInitialized(true);\n }, [availableTags, note, tagsInitialized]);\n\n // Inline tag creation\n const [createNoteTag] = useCreateNoteTagMutation();\n const [newTagLabel, setNewTagLabel] = useState('');\n const [newTagColor, setNewTagColor] = useState('var(--color-accent-purple)');\n const [isCreatingTag, setIsCreatingTag] = useState(false);\n\n const handleCreateTag = async () => {\n const label = newTagLabel.trim();\n if (!label || isCreatingTag) return;\n setIsCreatingTag(true);\n try {\n const result = await createNoteTag({\n variables: { input: { label, color: newTagColor } },\n });\n const newTag = result.data?.createNoteTag;\n if (newTag) {\n setSelectedTagIds((prev) => new Set(prev).add(newTag.id));\n await refetchTags();\n }\n setNewTagLabel('');\n } catch (err) {\n console.error('Failed to create tag:', err);\n } finally {\n setIsCreatingTag(false);\n }\n };\n\n const toggleTag = (tagId: string) => {\n setSelectedTagIds((prev) => {\n const next = new Set(prev);\n if (next.has(tagId)) {\n next.delete(tagId);\n } else {\n next.add(tagId);\n }\n return next;\n });\n };\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n const data: NoteEditorSubmitData = {\n ...(note?.id ? { id: note.id } : {}),\n workspaceId: workspaceId ?? '',\n title: formData.title,\n content: formData.content,\n vibeId: formData.vibeId || undefined,\n isPinned: formData.isPinned,\n selectedTagIds: Array.from(selectedTagIds),\n };\n onSubmit(data);\n };\n\n // Enhanced markdown preview with inline formatting support\n const parseInlineFormatting = (text: string): React.ReactNode[] => {\n const parts: React.ReactNode[] = [];\n let remaining = text;\n let key = 0;\n\n while (remaining.length > 0) {\n // Bold: **text** or __text__\n const boldMatch = remaining.match(/^(\\*\\*|__)(.+?)\\1/);\n if (boldMatch) {\n parts.push(<strong key={key++}>{parseInlineFormatting(boldMatch[2])}</strong>);\n remaining = remaining.slice(boldMatch[0].length);\n continue;\n }\n\n // Italic: *text* or _text_\n const italicMatch = remaining.match(/^(\\*|_)([^*_]+?)\\1/);\n if (italicMatch) {\n parts.push(<em key={key++}>{parseInlineFormatting(italicMatch[2])}</em>);\n remaining = remaining.slice(italicMatch[0].length);\n continue;\n }\n\n // Strikethrough: ~~text~~\n const strikeMatch = remaining.match(/^~~(.+?)~~/);\n if (strikeMatch) {\n parts.push(<del key={key++}>{parseInlineFormatting(strikeMatch[1])}</del>);\n remaining = remaining.slice(strikeMatch[0].length);\n continue;\n }\n\n // Inline code: `code`\n const codeMatch = remaining.match(/^`([^`]+)`/);\n if (codeMatch) {\n parts.push(\n <code\n key={key++}\n className=\"bg-bg-elevated px-1.5 py-0.5 rounded text-sm font-mono text-text-primary\"\n >\n {codeMatch[1]}\n </code>\n );\n remaining = remaining.slice(codeMatch[0].length);\n continue;\n }\n\n // Links: [text](url) - only allow safe URL schemes to prevent XSS\n const linkMatch = remaining.match(/^\\[([^\\]]+)\\]\\(([^)]+)\\)/);\n if (linkMatch) {\n const url = linkMatch[2];\n // Only allow http, https, mailto, relative paths, and hash links to prevent XSS via javascript: URLs\n const isSafeUrl = /^(https?:\\/\\/|mailto:|\\/|#)/i.test(url);\n if (isSafeUrl) {\n parts.push(\n <a\n key={key++}\n href={url}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"text-action-primary-bg hover:underline\"\n >\n {linkMatch[1]}\n </a>\n );\n } else {\n // Render unsafe links as plain text with muted styling\n parts.push(\n <span key={key++} className=\"text-text-secondary\">\n {linkMatch[1]}\n </span>\n );\n }\n remaining = remaining.slice(linkMatch[0].length);\n continue;\n }\n\n // No match - take the next character\n parts.push(remaining[0]);\n remaining = remaining.slice(1);\n }\n\n return parts;\n };\n\n // Parse markdown content into React elements\n const renderMarkdown = (text: string) => {\n const lines = text.split('\\n');\n const elements: React.ReactNode[] = [];\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n\n // Code blocks: ```\n if (line.startsWith('```')) {\n const language = line.slice(3).trim();\n const codeLines: string[] = [];\n i++;\n while (i < lines.length && !lines[i].startsWith('```')) {\n codeLines.push(lines[i]);\n i++;\n }\n elements.push(\n <pre key={elements.length} className=\"bg-bg-elevated rounded-md p-3 my-2 overflow-x-auto\">\n <code className=\"text-sm font-mono text-text-primary\">\n {language && (\n <span className=\"text-xs text-text-secondary block mb-2\">{language}</span>\n )}\n {codeLines.join('\\n')}\n </code>\n </pre>\n );\n i++;\n continue;\n }\n\n // Headings\n if (line.startsWith('# ')) {\n elements.push(\n <h1 key={elements.length} className=\"text-2xl font-bold mb-2 mt-4\">\n {parseInlineFormatting(line.slice(2))}\n </h1>\n );\n i++;\n continue;\n }\n if (line.startsWith('## ')) {\n elements.push(\n <h2 key={elements.length} className=\"text-xl font-bold mb-2 mt-3\">\n {parseInlineFormatting(line.slice(3))}\n </h2>\n );\n i++;\n continue;\n }\n if (line.startsWith('### ')) {\n elements.push(\n <h3 key={elements.length} className=\"text-lg font-bold mb-2 mt-2\">\n {parseInlineFormatting(line.slice(4))}\n </h3>\n );\n i++;\n continue;\n }\n\n // Blockquotes: > text\n if (line.startsWith('> ')) {\n elements.push(\n <blockquote\n key={elements.length}\n className=\"border-l-4 border-border-default pl-4 my-2 italic text-text-secondary\"\n >\n {parseInlineFormatting(line.slice(2))}\n </blockquote>\n );\n i++;\n continue;\n }\n\n // Unordered list: - or * (collect consecutive items and wrap in <ul>)\n if (line.match(/^[-*] /)) {\n const listItems: React.ReactNode[] = [];\n while (i < lines.length && lines[i].match(/^[-*] /)) {\n listItems.push(\n <li key={listItems.length} className=\"list-disc\">\n {parseInlineFormatting(lines[i].slice(2))}\n </li>\n );\n i++;\n }\n elements.push(\n <ul key={elements.length} className=\"ml-6 my-2\">\n {listItems}\n </ul>\n );\n continue;\n }\n\n // Ordered list: 1. 2. etc. (collect consecutive items and wrap in <ol>)\n const orderedMatch = line.match(/^(\\d+)\\. /);\n if (orderedMatch) {\n const listItems: React.ReactNode[] = [];\n while (i < lines.length) {\n const olMatch = lines[i].match(/^(\\d+)\\. /);\n if (!olMatch) break;\n listItems.push(\n <li key={listItems.length} className=\"list-decimal\">\n {parseInlineFormatting(lines[i].slice(olMatch[0].length))}\n </li>\n );\n i++;\n }\n elements.push(\n <ol key={elements.length} className=\"ml-6 my-2\">\n {listItems}\n </ol>\n );\n continue;\n }\n\n // Horizontal rule: --- or ***\n if (line.match(/^(-{3,}|\\*{3,})$/)) {\n elements.push(<hr key={elements.length} className=\"my-4 border-border-default\" />);\n i++;\n continue;\n }\n\n // Table detection (simple): | col1 | col2 |\n if (line.startsWith('|') && line.endsWith('|')) {\n const tableLines: string[] = [line];\n i++;\n while (i < lines.length && lines[i].startsWith('|') && lines[i].endsWith('|')) {\n tableLines.push(lines[i]);\n i++;\n }\n\n // Parse table\n const rows = tableLines\n .filter((l) => !l.match(/^\\|[\\s\\-:|]+\\|$/)) // Skip separator rows (including alignment markers like :---:)\n .map((l) =>\n l\n .slice(1, -1)\n .split('|')\n .map((cell) => cell.trim())\n );\n\n if (rows.length > 0) {\n const [header, ...body] = rows;\n elements.push(\n <table key={elements.length} className=\"w-full my-2 border-collapse text-sm\">\n <thead>\n <tr className=\"border-b border-border-default\">\n {header.map((cell, ci) => (\n <th key={ci} className=\"text-left py-2 px-3 font-semibold text-text-primary\">\n {parseInlineFormatting(cell)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {body.map((row, ri) => (\n <tr key={ri} className=\"border-b border-border-subtle\">\n {row.map((cell, ci) => (\n <td key={ci} className=\"py-2 px-3 text-text-primary\">\n {parseInlineFormatting(cell)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n );\n }\n continue;\n }\n\n // Empty line\n if (line === '') {\n elements.push(<br key={elements.length} />);\n i++;\n continue;\n }\n\n // Regular paragraph with inline formatting\n elements.push(\n <p key={elements.length} className=\"mb-1\">\n {parseInlineFormatting(line)}\n </p>\n );\n i++;\n }\n\n return elements;\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-overlay\">\n <div className=\"w-full max-w-3xl bg-bg-surface rounded-lg shadow-xl max-h-[90vh] flex flex-col\">\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {note\n ? tr('vibecontrols.notes.editNote', 'Edit Note')\n : tr('vibecontrols.notes.newNote', 'New Note')}\n </h2>\n <div className=\"flex items-center gap-2\">\n <button\n type=\"button\"\n onClick={() => setIsPreview(!isPreview)}\n className={`inline-flex items-center gap-2 px-3 py-1.5 text-sm rounded-md transition-colors ${\n isPreview\n ? 'bg-action-primary-bg text-action-primary-text'\n : 'bg-bg-sunken text-text-primary hover:bg-bg-elevated'\n }`}\n >\n {isPreview ? <Edit className=\"w-4 h-4\" /> : <Eye className=\"w-4 h-4\" />}\n {isPreview\n ? tr('vibecontrols.notes.edit', 'Edit')\n : tr('vibecontrols.notes.preview', 'Preview')}\n </button>\n <button onClick={onCancel} className=\"p-1 rounded hover:bg-bg-sunken transition-colors\">\n <X className=\"w-5 h-5 text-text-secondary\" />\n </button>\n </div>\n </div>\n\n <form onSubmit={handleSubmit} className=\"flex-1 flex flex-col overflow-hidden\">\n <div className=\"p-6 space-y-4 flex-1 overflow-y-auto\">\n <div>\n <input\n type=\"text\"\n value={formData.title}\n onChange={(e) => setFormData({ ...formData, title: e.target.value })}\n placeholder={tr('vibecontrols.notes.titlePlaceholder', 'Note title...')}\n required\n className=\"w-full px-0 py-2 text-lg sm:text-2xl font-bold bg-transparent border-0 border-b border-border-default text-text-primary placeholder:text-text-placeholder focus:outline-none focus:border-action-primary-bg\"\n />\n </div>\n\n <div className={`grid ${hideVibeSelect ? 'grid-cols-1' : 'grid-cols-2'} gap-4`}>\n {!hideVibeSelect && (\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.notes.relatedVibe', 'Related Vibe (optional)')}\n </label>\n <select\n value={formData.vibeId}\n onChange={(e) => setFormData({ ...formData, vibeId: e.target.value })}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value=\"\">No vibe</option>\n {vibeOptions.map((vibe) => (\n <option key={vibe.id} value={vibe.id}>\n {vibe.name}\n </option>\n ))}\n </select>\n </div>\n )}\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.notes.tags', 'Tags')}\n </label>\n {/* Selected tags */}\n <div className=\"flex flex-wrap gap-1.5 min-h-[38px] px-3 py-2 bg-bg-sunken border border-border-default rounded-md\">\n {availableTags\n .filter((t) => selectedTagIds.has(t.id))\n .map((tag) => (\n <span\n key={tag.id}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs font-medium bg-action-primary-bg/10 text-action-primary-bg rounded\"\n >\n <Tag className=\"w-3 h-3\" />\n {tag.label}\n <button\n type=\"button\"\n onClick={() => toggleTag(tag.id)}\n className=\"ml-0.5 hover:text-status-error-text\"\n >\n <X className=\"w-3 h-3\" />\n </button>\n </span>\n ))}\n <button\n type=\"button\"\n onClick={() => setShowTagPicker(!showTagPicker)}\n className=\"inline-flex items-center gap-1 px-2 py-0.5 text-xs text-text-secondary hover:text-text-primary hover:bg-bg-elevated rounded transition-colors\"\n >\n <Tag className=\"w-3 h-3\" />\n {selectedTagIds.size === 0 ? 'Add tags...' : '+'}\n </button>\n </div>\n {/* Tag picker dropdown */}\n {showTagPicker && (\n <div className=\"mt-1 bg-bg-surface border border-border-default rounded-md shadow-lg\">\n {/* Inline create: text input + color picker */}\n <div className=\"flex items-center gap-2 p-2 border-b border-border-subtle\">\n <span\n aria-hidden=\"true\"\n className=\"w-6 h-6 rounded-full border-2 border-border-default\"\n style={{ backgroundColor: newTagColor }}\n />\n <input\n type=\"text\"\n value={newTagLabel}\n onChange={(e) => setNewTagLabel(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === 'Enter') {\n e.preventDefault();\n handleCreateTag();\n }\n }}\n placeholder=\"New tag name... (Enter to create)\"\n disabled={isCreatingTag}\n className=\"flex-1 px-2 py-1 text-sm bg-bg-sunken border border-border-default rounded text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-1 focus:ring-action-primary-bg disabled:opacity-50\"\n />\n <button\n type=\"button\"\n onClick={handleCreateTag}\n disabled={!newTagLabel.trim() || isCreatingTag}\n className=\"p-1 text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors disabled:opacity-30\"\n >\n <Plus className=\"w-4 h-4\" />\n </button>\n </div>\n {/* Quick color palette */}\n <div className=\"flex items-center gap-1 px-2 py-1.5 border-b border-border-subtle\">\n {TAG_COLORS.map((c) => (\n <button\n key={c}\n type=\"button\"\n onClick={() => setNewTagColor(c)}\n className={`w-4 h-4 rounded-full transition-transform ${newTagColor === c ? 'ring-2 ring-action-primary-bg scale-125' : 'hover:scale-110'}`}\n style={{ backgroundColor: c }}\n />\n ))}\n </div>\n {/* Existing tags list */}\n <div className=\"max-h-32 overflow-y-auto p-1\">\n {availableTags.length === 0 ? (\n <p className=\"text-xs text-text-muted px-2 py-1\">\n No tags yet — create one above\n </p>\n ) : (\n availableTags.map((tag) => (\n <button\n key={tag.id}\n type=\"button\"\n onClick={() => toggleTag(tag.id)}\n className=\"flex items-center justify-between w-full px-2 py-1.5 text-sm text-text-primary hover:bg-bg-sunken rounded transition-colors\"\n >\n <span className=\"flex items-center gap-2\">\n {tag.color && (\n <span\n className=\"w-2.5 h-2.5 rounded-full\"\n style={{ backgroundColor: tag.color }}\n />\n )}\n {tag.label}\n </span>\n {selectedTagIds.has(tag.id) && (\n <Check className=\"w-4 h-4 text-action-primary-bg\" />\n )}\n </button>\n ))\n )}\n </div>\n </div>\n )}\n </div>\n </div>\n\n <div className=\"flex-1 min-h-[300px]\">\n {isPreview ? (\n <div className=\"h-full p-4 bg-bg-sunken rounded-md prose prose-sm dark:prose-invert max-w-none overflow-y-auto\">\n {renderMarkdown(formData.content)}\n </div>\n ) : (\n <textarea\n value={formData.content}\n onChange={(e) => setFormData({ ...formData, content: e.target.value })}\n placeholder={tr(\n 'vibecontrols.notes.contentPlaceholder',\n 'Write your note in Markdown...'\n )}\n className=\"w-full h-full min-h-[300px] px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none font-mono\"\n />\n )}\n </div>\n\n <div className=\"flex items-center gap-2\">\n <input\n type=\"checkbox\"\n id=\"isPinned\"\n checked={formData.isPinned}\n onChange={(e) => setFormData({ ...formData, isPinned: e.target.checked })}\n className=\"w-4 h-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n <label htmlFor=\"isPinned\" className=\"text-sm text-text-primary\">\n {tr('vibecontrols.notes.pinThisNote', 'Pin this note')}\n </label>\n </div>\n </div>\n\n <div className=\"flex items-center justify-end gap-3 px-6 py-4 border-t border-border-default bg-bg-sunken\">\n <button\n type=\"button\"\n onClick={onCancel}\n disabled={isLoading}\n className=\"px-4 py-2 text-sm font-medium text-text-primary bg-bg-surface border border-border-default rounded-md hover:bg-bg-elevated transition-colors disabled:opacity-50\"\n >\n {tr('vibecontrols.notes.cancel', 'Cancel')}\n </button>\n <button\n type=\"submit\"\n disabled={isLoading}\n className=\"inline-flex items-center gap-2 px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n <Save className=\"w-4 h-4\" />\n {isLoading\n ? tr('vibecontrols.notes.saving', 'Saving...')\n : tr('vibecontrols.notes.saveNote', 'Save Note')}\n </button>\n </div>\n </form>\n </div>\n </div>\n );\n}\n"],"mappings":"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"}
|