pi-note 0.39.2 → 0.39.3

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/README.md CHANGED
@@ -58,9 +58,10 @@ confirmation. **Clear selection** leaves its progress unchanged.
58
58
 
59
59
  **Preview** opens a scrollable, read-only document. **Insert task prompt…** reviews
60
60
  and inserts into an empty editor only; it never submits or replaces a draft.
61
- Creation, suggestion acceptance and shared changes require complete review, then
62
- a separate Cancel-first confirmation. Menu completion also asks explicitly;
63
- footer double-click is sufficient approval. Both require idle Pi. Failed or uncertain
61
+ Creation, menu suggestion acceptance and shared changes require complete review,
62
+ then a separate Cancel-first confirmation. Menu completion also asks explicitly.
63
+ Footer double-click directly approves suggestion acceptance or completion;
64
+ completion requires idle Pi. Failed or uncertain
64
65
  writes offer refresh/verification, never automatic replay.
65
66
 
66
67
  **Settings** groups prompts/guidance, task-offer policy, footer layout and field
@@ -73,7 +74,8 @@ Menus use Pi's native inline components, themes and configured selection keys.
73
74
  Small terminals show compact menus or ask you to enlarge the window before acting.
74
75
  Fullscreen has a clickable footer: **▤** opens Pinote; single-click a task title
75
76
  to preview, or double-click to complete immediately without another confirmation
76
- (requires idle Pi). Suggestion titles only preview; double-click never accepts.
77
+ (requires idle Pi). Single-click a suggestion title to preview it; double-click
78
+ to accept, start and select it immediately without another confirmation.
77
79
  Regular mode shows status; `/pinote` provides all actions. No Kitty action handler
78
80
  is needed.
79
81
 
@@ -215,10 +217,14 @@ See [setup, script contract and safeguards](mcp/FIELD_EVENTS.md). Disabled by de
215
217
 
216
218
  ## Migration
217
219
 
220
+ Since pi-note 0.39.3, fullscreen suggestion-title double-click accepts immediately;
221
+ single-click still previews and menu acceptance still requires review/confirmation.
222
+ Update both client and service, then `/reload` Pi.
223
+
218
224
  Since pi-note 0.39.2, fullscreen task-title double-click completes without another
219
225
  confirmation; single-click still previews and menu completion still confirms.
220
226
  Update the client and `/reload` Pi. The service must support footer double-click
221
- (pi-note 0.39.1+). Suggestion acceptance remains menu-only.
227
+ (pi-note 0.39.1+).
222
228
 
223
229
  Since pi-note 0.38.0, the default HTTP extension uses task-first navigation and
224
230
  explicit completion/suggestion approval. Update the client and service together,
@@ -264,7 +264,7 @@ function view(data, scoped = false, secondary = false, features = {}) {
264
264
  if (task)
265
265
  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 });
266
266
  else if (state.proposal)
267
- footer.push({ text: `? ${suggestion.tag ? `[${suggestion.tag}] ` : ''}${suggestion.text.split('\n')[0].slice(0, 2000)}`, action: 'proposal:preview', ...(features.footerStyles ? { style: { italic: true, color: 'accent' } } : {}), width: config.config.titleWidth });
267
+ footer.push({ text: `? ${suggestion.tag ? `[${suggestion.tag}] ` : ''}${suggestion.text.split('\n')[0].slice(0, 2000)}`, action: 'proposal:preview', ...(features.footerDoubleActions ? { doubleAction: 'proposal:accept' } : {}), ...(features.footerStyles ? { style: { italic: true, color: 'accent' } } : {}), width: config.config.titleWidth });
268
268
  else
269
269
  footer.push({ text: 'Pinote · No task', width: config.config.titleWidth });
270
270
  }
package/mcp/core.ts CHANGED
@@ -229,7 +229,7 @@ function view(data: Snapshot, scoped = false, secondary = false, features: { foo
229
229
  : [{ text: `${icons.note} `, width: 3 }];
230
230
  if (features.footerNativeActions) {
231
231
  if (task) 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 });
232
- else if (state.proposal) footer.push({ text: `? ${suggestion!.tag ? `[${suggestion!.tag}] ` : ''}${suggestion!.text.split('\n')[0].slice(0, 2000)}`, action: 'proposal:preview', ...(features.footerStyles ? { style: { italic: true, color: 'accent' } as const } : {}), width: config.config.titleWidth });
232
+ else if (state.proposal) footer.push({ text: `? ${suggestion!.tag ? `[${suggestion!.tag}] ` : ''}${suggestion!.text.split('\n')[0].slice(0, 2000)}`, action: 'proposal:preview', ...(features.footerDoubleActions ? { doubleAction: 'proposal:accept' } : {}), ...(features.footerStyles ? { style: { italic: true, color: 'accent' } as const } : {}), width: config.config.titleWidth });
233
233
  else footer.push({ text: 'Pinote · No task', width: config.config.titleWidth });
234
234
  } else if (task) {
235
235
  footer.push({ text: `${icons.check} `, action: 'done', width: 3 });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-note",
3
- "version": "0.39.2",
3
+ "version": "0.39.3",
4
4
  "description": "Persistent pinote tasks, Markdown handoffs, and task status for Pi",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/remote/ui.ts CHANGED
@@ -43,8 +43,9 @@ export class RemoteUI {
43
43
  if (this.opening || this.acting) return false;
44
44
  if (item.openApp) { await this.open(ctx); return true; }
45
45
  const control = view.controls.find((c) => c.id === action && c.kind === 'action');
46
- // The footer's deliberate double-click is already completion approval.
47
- return control ? this.dispatch(ctx, view, control, undefined, action === 'done' && item.doubleAction === action) : false;
46
+ // Deliberate footer double-click approves completion or suggestion acceptance.
47
+ const approved = (action === 'done' || action === 'proposal:accept') && item.doubleAction === action;
48
+ return control ? this.dispatch(ctx, view, control, undefined, approved) : false;
48
49
  }, this.repaint);
49
50
  }, { placement: 'belowEditor' });
50
51
  this.render();
@@ -66,7 +67,7 @@ export class RemoteUI {
66
67
  }) : 'Pinote · unavailable (/pinote to reconnect)');
67
68
  this.repaint();
68
69
  }
69
- async dispatch(ctx: ExtensionContext, view: NativeView, control: NativeControl, value?: NativeControl['value'], completionConfirmed = false): Promise<boolean> {
70
+ async dispatch(ctx: ExtensionContext, view: NativeView, control: NativeControl, value?: NativeControl['value'], footerConfirmed = false): Promise<boolean> {
70
71
  if (ctx.mode !== 'tui') throw new Error('Pinote UI actions require TUI mode');
71
72
  if (this.acting) return false;
72
73
  if (control.id === 'done' && !ctx.isIdle()) {
@@ -76,14 +77,14 @@ export class RemoteUI {
76
77
  const current = this.session.current(), signal = this.session.signal;
77
78
  try {
78
79
  if (typeof value === 'string') editorText(value);
79
- if (control.id === 'done' && !completionConfirmed) {
80
+ if (control.id === 'done' && !footerConfirmed) {
80
81
  const exits = typeof view.state.selectedId === 'number' && this.exitOnComplete(view.state.selectedId);
81
82
  const accept = exits ? 'Complete task and exit' : 'Complete task';
82
83
  const summary = truncateToWidth(line(view.body ?? ''), 100);
83
84
  if (await ctx.ui.select(`Pinote / ${accept} #${view.state.selectedId}\n${summary}\n${exits ? 'Closes this dedicated Pi session.' : 'Clears selection; keeps this conversation and editor.'}`,
84
85
  ['Cancel', accept], { signal }) !== accept) return false;
85
86
  }
86
- if (control.confirm) {
87
+ if (control.confirm && !(footerConfirmed && control.id === 'proposal:accept')) {
87
88
  const details = control.kind === 'action' ? control.confirm : `${control.confirm}\n\n${control.id === 'add' || control.id === 'add-field' ? '' : `Before:\n${disclosure(control.value)}\n\n`}After:\n${disclosure(value)}`;
88
89
  const accept = control.id === 'add' ? 'Create, start and select' : control.id === 'proposal:accept' ? 'Accept suggestion' :
89
90
  control.id === 'remove-field' ? 'Remove display field' : control.id === 'add-field' ? 'Add field' : 'Save change';