pi-note 0.22.1 → 0.23.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/dist-mcp/mcp/server.js +12 -6
- package/mcp/README.md +7 -2
- package/mcp/server.ts +13 -7
- package/package.json +1 -1
package/dist-mcp/mcp/server.js
CHANGED
|
@@ -27,7 +27,7 @@ const Action = z.object({ method: z.literal('native-ui/action'), params: z.objec
|
|
|
27
27
|
uri: z.literal(URI), revision: z.string().min(1).max(256), action: z.string().min(1).max(256),
|
|
28
28
|
value: z.union([z.string().max(20_000), z.number().finite(), z.boolean(), z.null()]).optional(), _meta: z.record(z.unknown()).optional(),
|
|
29
29
|
}) });
|
|
30
|
-
const server = new Server({ name: 'pinote', version: '0.
|
|
30
|
+
const server = new Server({ name: 'pinote', version: '0.23.0' }, { capabilities: { tools: {}, resources: {}, experimental: { 'native-ui-v1': {} } } });
|
|
31
31
|
// Serialize complete read/check/write cycles even when a host sends parallel calls.
|
|
32
32
|
let queue = Promise.resolve();
|
|
33
33
|
function serial(run) {
|
|
@@ -137,7 +137,13 @@ function staleAction() {
|
|
|
137
137
|
function secondaryActions(meta) {
|
|
138
138
|
return z.object({ footerSecondaryActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success;
|
|
139
139
|
}
|
|
140
|
-
function
|
|
140
|
+
function footerFeatures(meta) {
|
|
141
|
+
return {
|
|
142
|
+
footerStyles: z.object({ footerStyles: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
143
|
+
footerDoubleActions: z.object({ footerDoubleActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function view(data, scoped = false, secondary = false, features = {}) {
|
|
141
147
|
const { tasks, task, config, revision } = data;
|
|
142
148
|
const state = Context.parse({ conversationId: 'outgoing-state', state: { ...data.state, selectedId: task?.id ?? null } }).state;
|
|
143
149
|
const controls = [];
|
|
@@ -196,11 +202,11 @@ function view(data, scoped = false, secondary = false) {
|
|
|
196
202
|
const footer = [{ text: `${icons.note} `, width: 3 }];
|
|
197
203
|
if (task) {
|
|
198
204
|
footer.push({ text: `${icons.check} `, action: 'done', width: 3 });
|
|
199
|
-
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', width: config.config.titleWidth });
|
|
205
|
+
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', ...(features.footerDoubleActions ? { doubleAction: 'done' } : {}), width: config.config.titleWidth });
|
|
200
206
|
}
|
|
201
207
|
else if (state.proposal) {
|
|
202
208
|
footer.push({ text: `${icons.add} `, action: 'proposal:accept', ...(secondary ? { secondaryAction: 'proposal:dismiss' } : {}), width: 3 });
|
|
203
|
-
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, width: config.config.titleWidth });
|
|
209
|
+
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, ...(features.footerDoubleActions ? { doubleAction: 'proposal:accept' } : {}), ...(features.footerStyles ? { style: { italic: true, color: 'accent' } } : {}), width: config.config.titleWidth });
|
|
204
210
|
}
|
|
205
211
|
else
|
|
206
212
|
footer.push({ text: 'Pinote · no task', width: config.config.titleWidth });
|
|
@@ -256,7 +262,7 @@ server.setRequestHandler(ReadResourceRequestSchema, (request, extra) => serial(a
|
|
|
256
262
|
if (request.params.uri !== URI)
|
|
257
263
|
throw new Error('Unknown resource');
|
|
258
264
|
const data = await snapshot(context(request.params._meta).state, extra.signal);
|
|
259
|
-
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta))) }] };
|
|
265
|
+
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta), footerFeatures(request.params._meta))) }] };
|
|
260
266
|
}));
|
|
261
267
|
server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
262
268
|
const state = context(request.params._meta).state;
|
|
@@ -354,7 +360,7 @@ server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
|
354
360
|
extra.signal.throwIfAborted();
|
|
355
361
|
saveFooterConfig(cfg, before.config.raw, handoff, newSession, policy);
|
|
356
362
|
}
|
|
357
|
-
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
363
|
+
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta), footerFeatures(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
358
364
|
await server.notification({ method: 'notifications/native-ui/changed', params: { uri: URI } });
|
|
359
365
|
return result;
|
|
360
366
|
}));
|
package/mcp/README.md
CHANGED
|
@@ -55,8 +55,13 @@ normal personal data. No desktop notifications are sent.
|
|
|
55
55
|
edits are rejected and unrelated settings are preserved. Invalid configuration
|
|
56
56
|
fails closed; repair it manually rather than replacing it with defaults.
|
|
57
57
|
- **Footer:** selected task plus configured fields; safe HTTP(S) links are clickable.
|
|
58
|
-
|
|
59
|
-
resetting the conversation or editor.
|
|
58
|
+
Single-click the active title to preview; double-click it to complete. **✔** also
|
|
59
|
+
completes and clears the selection without resetting the conversation or editor.
|
|
60
|
+
Suggested titles use italic accent text; double-click to accept/start, then the
|
|
61
|
+
title returns to normal text. Acceptance cannot reuse the same click capability
|
|
62
|
+
to complete. Styling/double-click require gateway 0.28.0+ and double-click needs
|
|
63
|
+
its updated Kitty mouse mapping (Ctrl+Shift+double-click in fullscreen).
|
|
64
|
+
Older hosts retain plain titles and icon actions. Suggestions show **✚**: left-click to
|
|
60
65
|
create/start/select, right-click to dismiss. Right-click requires gateway 0.22.0+
|
|
61
66
|
and its Kitty mouse mapping; **Dismiss suggestion** in the app menu remains the
|
|
62
67
|
keyboard/older-host fallback. These use the gateway's generic footer actions, not a Pinote
|
package/mcp/server.ts
CHANGED
|
@@ -32,7 +32,7 @@ const Action = z.object({ method: z.literal('native-ui/action'), params: z.objec
|
|
|
32
32
|
uri: z.literal(URI), revision: z.string().min(1).max(256), action: z.string().min(1).max(256),
|
|
33
33
|
value: z.union([z.string().max(20_000), z.number().finite(), z.boolean(), z.null()]).optional(), _meta: z.record(z.unknown()).optional(),
|
|
34
34
|
}) });
|
|
35
|
-
const server = new Server({ name: 'pinote', version: '0.
|
|
35
|
+
const server = new Server({ name: 'pinote', version: '0.23.0' }, { capabilities: { tools: {}, resources: {}, experimental: { 'native-ui-v1': {} } } });
|
|
36
36
|
// Serialize complete read/check/write cycles even when a host sends parallel calls.
|
|
37
37
|
let queue = Promise.resolve();
|
|
38
38
|
function serial<T>(run: () => Promise<T>): Promise<T> {
|
|
@@ -118,7 +118,13 @@ function staleAction(): never {
|
|
|
118
118
|
function secondaryActions(meta: Record<string, unknown> | undefined): boolean {
|
|
119
119
|
return z.object({ footerSecondaryActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success;
|
|
120
120
|
}
|
|
121
|
-
function
|
|
121
|
+
function footerFeatures(meta: Record<string, unknown> | undefined) {
|
|
122
|
+
return {
|
|
123
|
+
footerStyles: z.object({ footerStyles: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
124
|
+
footerDoubleActions: z.object({ footerDoubleActions: z.literal(true) }).safeParse(meta?.['native-ui/features']).success,
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
function view(data: Snapshot, scoped = false, secondary = false, features: { footerStyles?: boolean; footerDoubleActions?: boolean } = {}) {
|
|
122
128
|
const { tasks, task, config, revision } = data;
|
|
123
129
|
const state = Context.parse({ conversationId: 'outgoing-state', state: { ...data.state, selectedId: task?.id ?? null } }).state;
|
|
124
130
|
const controls: Control[] = [];
|
|
@@ -164,13 +170,13 @@ function view(data: Snapshot, scoped = false, secondary = false) {
|
|
|
164
170
|
for (const candidate of matches.slice(0, 200)) action(`select:${candidate.id}`, `${candidate.tag ? `[${candidate.tag}] ` : ''}${candidate.text.split('\n')[0]}`, { description: `#${candidate.id} · ${candidate.state}`, confirm: 'Select and start this task for this conversation?' });
|
|
165
171
|
if (matches.length > 200) body += '\nShowing the first 200 matches; use Filter tasks to narrow the list.';
|
|
166
172
|
}
|
|
167
|
-
const footer: { text: string; url?: string; action?: string; secondaryAction?: string; width?: number }[] = [{ text: `${icons.note} `, width: 3 }];
|
|
173
|
+
const footer: { text: string; url?: string; action?: string; secondaryAction?: string; doubleAction?: string; style?: { italic: boolean; color: 'accent' }; width?: number }[] = [{ text: `${icons.note} `, width: 3 }];
|
|
168
174
|
if (task) {
|
|
169
175
|
footer.push({ text: `${icons.check} `, action: 'done', width: 3 });
|
|
170
|
-
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', width: config.config.titleWidth });
|
|
176
|
+
footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', ...(features.footerDoubleActions ? { doubleAction: 'done' } : {}), width: config.config.titleWidth });
|
|
171
177
|
} else if (state.proposal) {
|
|
172
178
|
footer.push({ text: `${icons.add} `, action: 'proposal:accept', ...(secondary ? { secondaryAction: 'proposal:dismiss' } : {}), width: 3 });
|
|
173
|
-
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, width: config.config.titleWidth });
|
|
179
|
+
footer.push({ text: `${state.proposal.tag ? `[${state.proposal.tag}] ` : ''}${state.proposal.text.split('\n')[0].slice(0, 2000)}`, ...(features.footerDoubleActions ? { doubleAction: 'proposal:accept' } : {}), ...(features.footerStyles ? { style: { italic: true, color: 'accent' } as const } : {}), width: config.config.titleWidth });
|
|
174
180
|
} else footer.push({ text: 'Pinote · no task', width: config.config.titleWidth });
|
|
175
181
|
const titleParts = footer.length;
|
|
176
182
|
if (task) {
|
|
@@ -211,7 +217,7 @@ server.setRequestHandler(ListResourcesRequestSchema, async () => ({ resources: [
|
|
|
211
217
|
server.setRequestHandler(ReadResourceRequestSchema, (request, extra) => serial(async () => {
|
|
212
218
|
if (request.params.uri !== URI) throw new Error('Unknown resource');
|
|
213
219
|
const data = await snapshot(context(request.params._meta).state, extra.signal);
|
|
214
|
-
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta))) }] };
|
|
220
|
+
return { contents: [{ uri: URI, mimeType: MIME, text: JSON.stringify(view(data, scopedActions(request.params._meta), secondaryActions(request.params._meta), footerFeatures(request.params._meta))) }] };
|
|
215
221
|
}));
|
|
216
222
|
server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
217
223
|
const state = context(request.params._meta).state;
|
|
@@ -275,7 +281,7 @@ server.setRequestHandler(Action, (request, extra) => serial(async () => {
|
|
|
275
281
|
extra.signal.throwIfAborted();
|
|
276
282
|
saveFooterConfig(cfg, before.config.raw, handoff, newSession, policy);
|
|
277
283
|
}
|
|
278
|
-
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
284
|
+
const result = { view: view(await snapshot(next, extra.signal), scoped, secondaryActions(request.params._meta), footerFeatures(request.params._meta)), ...(draft ? { draft } : {}), ...(preview ? { preview } : {}) };
|
|
279
285
|
await server.notification({ method: 'notifications/native-ui/changed', params: { uri: URI } });
|
|
280
286
|
return result;
|
|
281
287
|
}));
|