create-pmos 0.106.0 → 0.108.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/guide/notes.md +7 -5
- package/workspace/lib/notes.js +98 -1
- package/workspace/lib/routes-notes.js +32 -11
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/app.js +2 -2
- package/workspace/public/pm/components.css +315 -163
- package/workspace/public/pm/dest-notes.js +383 -0
- package/workspace/public/pm/dom.js +70 -3
- package/workspace/public/pm/frame-copy.js +1 -0
- package/workspace/public/pm/note-dock.js +78 -22
- package/workspace/public/pm/notes-links.js +3 -3
- package/workspace/public/pm/notes-task.js +36 -0
- package/workspace/public/pm/panel-cockpit.js +1 -2
- package/workspace/public/pm/registry.js +2 -0
- package/workspace/public/pm/topbar.js +42 -17
- package/workspace/public/pm/view-notes.js +135 -165
- package/workspace/public/vendor/lucide-sprite.js +8 -2
- package/workspace/test-files.json +3 -1
- package/workspace/test-lists/meeting-notes.json +5 -3
- package/workspace/third-party/README.md +1 -1
- package/workspace/public/pm/view-notes-menu.js +0 -317
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"doc-category.test.js",
|
|
43
43
|
"doc-category-corrections.test.js",
|
|
44
44
|
"notes-highlight.test.js",
|
|
45
|
+
"notes-page.test.js",
|
|
45
46
|
"notes-conflicts.test.js",
|
|
46
47
|
"proactive-prepare.test.js",
|
|
47
48
|
"learn.test.js",
|
|
@@ -149,6 +150,7 @@
|
|
|
149
150
|
"doc-category.test.js": 1,
|
|
150
151
|
"doc-category-corrections.test.js": 1,
|
|
151
152
|
"notes-highlight.test.js": 1,
|
|
153
|
+
"notes-page.test.js": 1,
|
|
152
154
|
"notes-conflicts.test.js": 1,
|
|
153
155
|
"proactive-prepare.test.js": 9,
|
|
154
156
|
"learn.test.js": 3,
|
|
@@ -206,7 +208,7 @@
|
|
|
206
208
|
"pm-r29-filespad-test.js": 5,
|
|
207
209
|
"pm-r29-newsession-alignment-test.js": 8,
|
|
208
210
|
"pm-live-notes-test.js": 24,
|
|
209
|
-
"pm-notes-menu-test.js":
|
|
211
|
+
"pm-notes-menu-test.js": 10,
|
|
210
212
|
"pm-r30-test.js": 4,
|
|
211
213
|
"ui-test.js": 30,
|
|
212
214
|
"plan-items.test.js": 1,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"_comment": "meeting-notes Runs 1 to 10: the Notes engine and routes (notes.test.js), local Whisper, line labels and repository checks (notes-listen.test.js), the final pass after Stop and the Wispr Flow fetch (notes-final.test.js), Qwen3-ASR kept loaded (notes-qwen.test.js), and the Notes view in a browser (pm-notes-test.js).",
|
|
2
|
+
"_comment": "meeting-notes Runs 1 to 10: the Notes engine and routes (notes.test.js), local Whisper, line labels and repository checks (notes-listen.test.js), the final pass after Stop and the Wispr Flow fetch (notes-final.test.js), Qwen3-ASR kept loaded (notes-qwen.test.js), and the Notes view in a browser (pm-notes-test.js), plus the note pop-up patched in place while it listens (pm-note-dock-test.js).",
|
|
3
3
|
"modules": [
|
|
4
4
|
"notes.test.js",
|
|
5
5
|
"notes-listen.test.js",
|
|
@@ -7,13 +7,15 @@
|
|
|
7
7
|
"notes-qwen.test.js"
|
|
8
8
|
],
|
|
9
9
|
"panels": [
|
|
10
|
-
"pm-notes-test.js"
|
|
10
|
+
"pm-notes-test.js",
|
|
11
|
+
"pm-note-dock-test.js"
|
|
11
12
|
],
|
|
12
13
|
"seconds": {
|
|
13
14
|
"notes.test.js": 12,
|
|
14
15
|
"pm-notes-test.js": 40,
|
|
15
16
|
"notes-listen.test.js": 10,
|
|
16
17
|
"notes-final.test.js": 6,
|
|
17
|
-
"notes-qwen.test.js": 4
|
|
18
|
+
"notes-qwen.test.js": 4,
|
|
19
|
+
"pm-note-dock-test.js": 20
|
|
18
20
|
}
|
|
19
21
|
}
|
|
@@ -12,7 +12,7 @@ The workspace runs with no network (D62). Two third-party browser files are comm
|
|
|
12
12
|
| Package | Version | Licence | Tarball | Tarball sha256 | Shipped file |
|
|
13
13
|
|---|---|---|---|---|---|
|
|
14
14
|
| sortablejs | 1.15.7 | MIT | https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz | `66cbfc8fa18860783584fe0c346baf6cbf3399da72d23d21053425ba232abed4` | `public/vendor/Sortable.min.js`: an unmodified copy of `package/Sortable.min.js` (45 478 bytes, sha256 `bf4241bc73fef7f11c59a283a69fe8051cdd31c6d8ff5a2b9ba219e7831fcf76`; npm integrity `sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==`) |
|
|
15
|
-
| lucide-static | 1.52.0 | ISC, plus the Feather MIT notice | https://registry.npmjs.org/lucide-static/-/lucide-static-1.52.0.tgz | `4d2aa079171e4eba4c704844b8b0c9edb6e4cfb2fb8bd86f7b14353994f04a87` | `public/vendor/lucide-sprite.js`: generated by `build-lucide-sprite.js` (
|
|
15
|
+
| lucide-static | 1.52.0 | ISC, plus the Feather MIT notice | https://registry.npmjs.org/lucide-static/-/lucide-static-1.52.0.tgz | `4d2aa079171e4eba4c704844b8b0c9edb6e4cfb2fb8bd86f7b14353994f04a87` | `public/vendor/lucide-sprite.js`: generated by `build-lucide-sprite.js` (23 263 bytes, sha256 `6866d7d284f4f09139daa9f31cff28c4cc56b6c008aa0b58e6ee0e61907aac2f`). It holds 67 icons: grip-vertical, plus, check, square, square-check-big, x, undo-2, flag, bot, chevron-right, chevron-down, ellipsis, circle-alert, history, arrow-up, arrow-down, diamond, then the eight the frame's chrome draws (Run 3): settings, search, refresh-cw, chevron-left, sun, moon, arrow-left, arrow-right, then globe, columns-2, arrow-up-down and arrow-left-right for the dock strip's web, split and orientation chips (Run 3, C6), then panel-top, panel-left, maximize-2, minimize-2, git-branch and clock for the dock's icon controls and session rows (0.54.2), then bold, italic, code, link, type, heading, list, list-checks, text-quote, file-code, git-compare, cloud and share-2 for the document tab's in-place editor, its Confluence copy and its connections, then house, target, square-kanban, file-text, square-terminal, calendar and chart-column for the phone's tab bar and view switches, then users, git-merge, mail, message-square, ticket and ban for the People place's nav icon, merge button and interaction sources, then notebook-pen, mic, pause, play, highlighter and circle-dot for the Notes place, the Record button and the highlight |
|
|
16
16
|
|
|
17
17
|
The licences are in this folder: [sortablejs-1.15.7.LICENSE](sortablejs-1.15.7.LICENSE) and [lucide-static-1.52.0.LICENSE](lucide-static-1.52.0.LICENSE). The sprite also carries the lucide licence text as its first comment.
|
|
18
18
|
|
|
@@ -1,317 +0,0 @@
|
|
|
1
|
-
/* pm/view-notes-menu.js — the Notes menu: all notes grouped by initiative (Run 11, R47).
|
|
2
|
-
*
|
|
3
|
-
* Two-column layout:
|
|
4
|
-
* left all notes grouped by initiative; click an initiative title to collapse/expand;
|
|
5
|
-
* click a note to open its detail on the right
|
|
6
|
-
* right the note detail (same single column as the full-screen note, Run 7)
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { el, setChildren } from './dom.js';
|
|
10
|
-
import { askConfirm } from './dialogs.js';
|
|
11
|
-
import { pmGet, pmPost } from './http.js';
|
|
12
|
-
import { icon } from './icons.js';
|
|
13
|
-
import { button, iconButton, empty, toast, ensureComponentsCss } from './primitives.js';
|
|
14
|
-
import * as viewNotes from './view-notes.js';
|
|
15
|
-
|
|
16
|
-
ensureComponentsCss();
|
|
17
|
-
|
|
18
|
-
let cache = new Map();
|
|
19
|
-
let list = null;
|
|
20
|
-
let selectedId = null;
|
|
21
|
-
let expandedInitiatives = new Set();
|
|
22
|
-
|
|
23
|
-
async function loadList() {
|
|
24
|
-
const r = await pmGet('/api/notes');
|
|
25
|
-
if (r && r.ok) {
|
|
26
|
-
list = { notes: r.notes || [] };
|
|
27
|
-
} else {
|
|
28
|
-
list = { notes: [], error: (r && r.error) || 'the notes could not be read' };
|
|
29
|
-
}
|
|
30
|
-
return list;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
async function loadNote(id) {
|
|
34
|
-
const r = await pmGet('/api/notes/one?id=' + encodeURIComponent(id));
|
|
35
|
-
if (r && r.ok) {
|
|
36
|
-
cache.set(id, r.note);
|
|
37
|
-
return r.note;
|
|
38
|
-
}
|
|
39
|
-
return null;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function groupByInitiative(notes) {
|
|
43
|
-
const groups = {};
|
|
44
|
-
notes.forEach((n) => {
|
|
45
|
-
// a note's initiative is a link object ({ id, title, by }), never a bare string
|
|
46
|
-
const key = n.initiative ? (n.initiative.title || n.initiative.id) : '(no initiative)';
|
|
47
|
-
if (!groups[key]) groups[key] = [];
|
|
48
|
-
groups[key].push(n);
|
|
49
|
-
});
|
|
50
|
-
return Object.entries(groups).sort((a, b) => {
|
|
51
|
-
if (a[0] === '(no initiative)') return 1;
|
|
52
|
-
if (b[0] === '(no initiative)') return -1;
|
|
53
|
-
return a[0].localeCompare(b[0]);
|
|
54
|
-
});
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function renderNoteRow(note, isSelected, onClick) {
|
|
58
|
-
// the list row carries summary_lines (up to three plain lines); its `summary` is only a has-a-summary flag
|
|
59
|
-
const summaryText = (note.summary_lines || []).slice(0, 2).join(' ').substring(0, 60);
|
|
60
|
-
|
|
61
|
-
return el('div', {
|
|
62
|
-
class: 'nt-menu-row' + (isSelected ? ' nt-menu-row--selected' : ''),
|
|
63
|
-
'data-nt-menu-row': note.id,
|
|
64
|
-
onClick,
|
|
65
|
-
},
|
|
66
|
-
el('div', { class: 'nt-menu-row-title' }, note.title || 'Untitled note'),
|
|
67
|
-
el('div', { class: 'nt-menu-row-date' }, new Date(note.updated_at).toLocaleDateString()),
|
|
68
|
-
summaryText && el('div', { class: 'nt-menu-row-summary' }, summaryText),
|
|
69
|
-
);
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
/** The note's summary as plain text: the agent's markdown, else the local overview lines. */
|
|
73
|
-
function summaryOf(note) {
|
|
74
|
-
if (note.agent && note.agent.markdown) return String(note.agent.markdown);
|
|
75
|
-
const ov = note.summary && note.summary.sections && note.summary.sections.overview;
|
|
76
|
-
return Array.isArray(ov) ? ov.map((x) => String((x && x.text) || '')).filter(Boolean).join('\n') : '';
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
function renderLeftPanel(notes, selectedId, onSelectNote, onToggleInitiative) {
|
|
80
|
-
const groups = groupByInitiative(notes);
|
|
81
|
-
|
|
82
|
-
return el('div', { class: 'nt-menu-left' },
|
|
83
|
-
...groups.map(([initiative, initiativeNotes]) => {
|
|
84
|
-
const isExpanded = expandedInitiatives.has(initiative);
|
|
85
|
-
return el('div', { class: 'nt-menu-group' },
|
|
86
|
-
el('div', {
|
|
87
|
-
class: 'nt-menu-group-title',
|
|
88
|
-
onClick: () => onToggleInitiative(initiative),
|
|
89
|
-
},
|
|
90
|
-
icon(isExpanded ? 'chevron-down' : 'chevron-right', { class: 'nt-menu-group-icon' }),
|
|
91
|
-
el('span', {}, initiative),
|
|
92
|
-
),
|
|
93
|
-
isExpanded && el('div', { class: 'nt-menu-group-items' },
|
|
94
|
-
...initiativeNotes.map((note) =>
|
|
95
|
-
renderNoteRow(note, note.id === selectedId, () => onSelectNote(note.id)),
|
|
96
|
-
),
|
|
97
|
-
),
|
|
98
|
-
);
|
|
99
|
-
}),
|
|
100
|
-
);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// the cockpit calls renderBoard(inner, ctx) and ignores what it returns: the view draws into `inner`
|
|
104
|
-
export function renderBoard(inner, ctx) {
|
|
105
|
-
ensureComponentsCss();
|
|
106
|
-
const root = el('div', { class: 'nt-menu' });
|
|
107
|
-
inner.appendChild(root);
|
|
108
|
-
|
|
109
|
-
async function render() {
|
|
110
|
-
if (!list) await loadList();
|
|
111
|
-
const note = selectedId ? cache.get(selectedId) : null;
|
|
112
|
-
|
|
113
|
-
const leftPanel = renderLeftPanel(
|
|
114
|
-
list.notes || [],
|
|
115
|
-
selectedId,
|
|
116
|
-
async (id) => {
|
|
117
|
-
selectedId = id;
|
|
118
|
-
await loadNote(id);
|
|
119
|
-
render();
|
|
120
|
-
},
|
|
121
|
-
(initiative) => {
|
|
122
|
-
if (expandedInitiatives.has(initiative)) {
|
|
123
|
-
expandedInitiatives.delete(initiative);
|
|
124
|
-
} else {
|
|
125
|
-
expandedInitiatives.add(initiative);
|
|
126
|
-
}
|
|
127
|
-
render();
|
|
128
|
-
},
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
const rightPanel = note
|
|
132
|
-
? await (async () => {
|
|
133
|
-
try {
|
|
134
|
-
const noteModule = await import('./view-notes.js');
|
|
135
|
-
// Parse sections from the note's data
|
|
136
|
-
let actions = [];
|
|
137
|
-
let decisions = [];
|
|
138
|
-
let questions = [];
|
|
139
|
-
let highlights = [];
|
|
140
|
-
let conflicts = [];
|
|
141
|
-
if (note.final_state) {
|
|
142
|
-
if (note.final_state.actions) actions = note.final_state.actions;
|
|
143
|
-
if (note.final_state.decisions) decisions = note.final_state.decisions;
|
|
144
|
-
if (note.final_state.questions) questions = note.final_state.questions;
|
|
145
|
-
}
|
|
146
|
-
if (note.highlights) highlights = note.highlights;
|
|
147
|
-
if (note.conflicts) conflicts = note.conflicts;
|
|
148
|
-
|
|
149
|
-
const parts = [];
|
|
150
|
-
|
|
151
|
-
// Header with title, date, initiative, and action buttons
|
|
152
|
-
parts.push(
|
|
153
|
-
el('div', { class: 'nt-detail-header' },
|
|
154
|
-
el('div', { class: 'nt-detail-title-bar' },
|
|
155
|
-
el('div', { class: 'nt-detail-title' }, note.title || 'Untitled note'),
|
|
156
|
-
el('div', { class: 'nt-detail-actions-bar' },
|
|
157
|
-
iconButton({ icon: 'play', ariaLabel: 'Resume recording', attrs: { class: 'nt-detail-resume' } }),
|
|
158
|
-
el('div', { class: 'nt-menu-dots' },
|
|
159
|
-
iconButton({ icon: 'more-vertical', ariaLabel: 'More options', attrs: { class: 'nt-detail-more' } })
|
|
160
|
-
)
|
|
161
|
-
)
|
|
162
|
-
),
|
|
163
|
-
el('div', { class: 'nt-detail-meta' },
|
|
164
|
-
el('span', { class: 'nt-detail-date' }, new Date(note.updated_at).toLocaleDateString()),
|
|
165
|
-
note.initiative && el('span', { class: 'nt-detail-initiative' }, typeof note.initiative === 'string' ? note.initiative : (note.initiative.title || note.initiative.id)),
|
|
166
|
-
),
|
|
167
|
-
)
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
// Summary/Overview
|
|
171
|
-
parts.push(
|
|
172
|
-
el('div', { class: 'nt-detail-summary' },
|
|
173
|
-
el('h3', { class: 'nt-detail-section-title' }, 'Overview'),
|
|
174
|
-
note.summary
|
|
175
|
-
? el('div', { class: 'nt-detail-summary-text' }, note.summary)
|
|
176
|
-
: el('p', { class: 'nt-hint' }, 'No summary yet'),
|
|
177
|
-
)
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
// Conflicts section
|
|
181
|
-
if (conflicts && conflicts.length > 0) {
|
|
182
|
-
parts.push(
|
|
183
|
-
el('div', { class: 'nt-detail-conflicts' },
|
|
184
|
-
el('h3', { class: 'nt-detail-section-title' }, `Conflicts (${conflicts.length})`),
|
|
185
|
-
el('div', { class: 'nt-detail-conflicts-list' },
|
|
186
|
-
...conflicts.map((conflict) =>
|
|
187
|
-
el('div', { class: 'nt-detail-conflict-item' },
|
|
188
|
-
el('div', { class: 'nt-conflict-text' }, conflict.text || ''),
|
|
189
|
-
el('div', { class: 'nt-conflict-details' },
|
|
190
|
-
conflict.file && el('span', { class: 'nt-conflict-file' }, conflict.file),
|
|
191
|
-
iconButton({ icon: 'eye', ariaLabel: 'Review conflict', attrs: { class: 'nt-conflict-review' } })
|
|
192
|
-
)
|
|
193
|
-
)
|
|
194
|
-
)
|
|
195
|
-
)
|
|
196
|
-
)
|
|
197
|
-
);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
// Highlights section
|
|
201
|
-
if (highlights && highlights.length > 0) {
|
|
202
|
-
parts.push(
|
|
203
|
-
el('div', { class: 'nt-detail-highlights' },
|
|
204
|
-
el('h3', { class: 'nt-detail-section-title' }, `Highlights (${highlights.length})`),
|
|
205
|
-
el('div', { class: 'nt-detail-highlights-list' },
|
|
206
|
-
...highlights.map((h) =>
|
|
207
|
-
el('div', { class: 'nt-detail-highlight-item' },
|
|
208
|
-
el('div', { class: 'nt-highlight-text' }, h.text || ''),
|
|
209
|
-
el('div', { class: 'nt-highlight-meta' },
|
|
210
|
-
el('span', { class: 'nt-highlight-label' }, h.label || ''),
|
|
211
|
-
h.timestamp && el('span', { class: 'nt-highlight-time' }, h.timestamp),
|
|
212
|
-
iconButton({ icon: 'play', ariaLabel: 'Play audio', attrs: { class: 'nt-highlight-play' } })
|
|
213
|
-
)
|
|
214
|
-
)
|
|
215
|
-
)
|
|
216
|
-
)
|
|
217
|
-
)
|
|
218
|
-
);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Decisions section
|
|
222
|
-
if (decisions && decisions.length > 0) {
|
|
223
|
-
parts.push(
|
|
224
|
-
el('div', { class: 'nt-detail-decisions' },
|
|
225
|
-
el('h3', { class: 'nt-detail-section-title' }, `Decisions (${decisions.length})`),
|
|
226
|
-
el('div', { class: 'nt-detail-decisions-list' },
|
|
227
|
-
...decisions.map((decision, idx) =>
|
|
228
|
-
el('div', { class: 'nt-detail-decision-item' },
|
|
229
|
-
el('div', { class: 'nt-decision-text' }, decision.text || decision),
|
|
230
|
-
el('div', { class: 'nt-decision-actions' },
|
|
231
|
-
iconButton({ icon: 'edit', ariaLabel: 'Edit decision', attrs: { class: 'nt-decision-edit' } })
|
|
232
|
-
)
|
|
233
|
-
)
|
|
234
|
-
)
|
|
235
|
-
)
|
|
236
|
-
)
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Open questions section
|
|
241
|
-
if (questions && questions.length > 0) {
|
|
242
|
-
parts.push(
|
|
243
|
-
el('div', { class: 'nt-detail-questions' },
|
|
244
|
-
el('h3', { class: 'nt-detail-section-title' }, `Open questions (${questions.length})`),
|
|
245
|
-
el('div', { class: 'nt-detail-questions-list' },
|
|
246
|
-
...questions.map((question) =>
|
|
247
|
-
el('div', { class: 'nt-detail-question-item' },
|
|
248
|
-
el('div', { class: 'nt-question-text' }, question.text || question),
|
|
249
|
-
el('div', { class: 'nt-question-actions' },
|
|
250
|
-
button({ label: 'Draft answer', variant: 'ghost', size: 'sm' })
|
|
251
|
-
)
|
|
252
|
-
)
|
|
253
|
-
)
|
|
254
|
-
)
|
|
255
|
-
)
|
|
256
|
-
);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
// Actions/Next steps section
|
|
260
|
-
if (actions && actions.length > 0) {
|
|
261
|
-
parts.push(
|
|
262
|
-
el('div', { class: 'nt-detail-actions' },
|
|
263
|
-
el('h3', { class: 'nt-detail-section-title' }, `Next steps (${actions.length})`),
|
|
264
|
-
el('div', { class: 'nt-detail-actions-list' },
|
|
265
|
-
...actions.map((action) =>
|
|
266
|
-
el('div', { class: 'nt-detail-action-row' },
|
|
267
|
-
el('div', { class: 'nt-detail-action-text' }, action.text || ''),
|
|
268
|
-
el('div', { class: 'nt-action-meta' },
|
|
269
|
-
action.owner && el('span', { class: 'nt-action-owner' }, `Owner: ${action.owner}`),
|
|
270
|
-
action.due && el('span', { class: 'nt-action-due' }, `Due: ${action.due}`),
|
|
271
|
-
),
|
|
272
|
-
iconButton({
|
|
273
|
-
icon: 'plus',
|
|
274
|
-
ariaLabel: 'Create task',
|
|
275
|
-
attrs: { class: 'nt-detail-make-task' },
|
|
276
|
-
onClick: async () => {
|
|
277
|
-
try {
|
|
278
|
-
const result = await pmPost('/api/notes/create-task', {
|
|
279
|
-
id: note.id,
|
|
280
|
-
text: action.text,
|
|
281
|
-
owner: action.owner,
|
|
282
|
-
due: action.due,
|
|
283
|
-
});
|
|
284
|
-
if (result && result.ok) {
|
|
285
|
-
toast({ text: 'Task created' });
|
|
286
|
-
} else {
|
|
287
|
-
toast({ text: 'Could not create task' });
|
|
288
|
-
}
|
|
289
|
-
} catch (e) {
|
|
290
|
-
console.error('Task creation error:', e);
|
|
291
|
-
toast({ text: 'Error creating task' });
|
|
292
|
-
}
|
|
293
|
-
},
|
|
294
|
-
}),
|
|
295
|
-
)
|
|
296
|
-
)
|
|
297
|
-
)
|
|
298
|
-
)
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// Files section (placeholder for future)
|
|
303
|
-
// Discussion/Transcript section (collapsible placeholder)
|
|
304
|
-
|
|
305
|
-
return el('div', { class: 'nt-menu-right' }, ...parts);
|
|
306
|
-
} catch (e) {
|
|
307
|
-
console.error('Note detail error:', e);
|
|
308
|
-
return el('div', { class: 'nt-menu-right' }, empty({ text: 'Could not load note detail' }));
|
|
309
|
-
}
|
|
310
|
-
})()
|
|
311
|
-
: el('div', { class: 'nt-menu-right' }, empty({ text: 'Select a note to view details' }));
|
|
312
|
-
|
|
313
|
-
setChildren(root, [leftPanel, rightPanel]);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
render();
|
|
317
|
-
}
|