pi-note 0.39.0 → 0.39.2

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
@@ -59,8 +59,9 @@ confirmation. **Clear selection** leaves its progress unchanged.
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
61
  Creation, suggestion acceptance and shared changes require complete review, then
62
- a separate Cancel-first confirmation. Completion also asks explicitly and requires
63
- idle Pi. Failed or uncertain writes offer refresh/verification, never automatic replay.
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
64
+ writes offer refresh/verification, never automatic replay.
64
65
 
65
66
  **Settings** groups prompts/guidance, task-offer policy, footer layout and field
66
67
  definitions. Editors accept plain text or numbers, not JSON; validation retains
@@ -70,9 +71,11 @@ are saved only after review and confirmation.
70
71
  Menus use Pi's native inline components, themes and configured selection keys.
71
72
  **Escape** goes back; **Ctrl+R** refreshes; page keys scroll lists and documents.
72
73
  Small terminals show compact menus or ask you to enlarge the window before acting.
73
- Fullscreen has a clickable footer: **▤** opens Pinote; task and suggestion titles
74
- only preview, never complete or accept on double-click. Regular mode shows status;
75
- `/pinote` provides all actions. No Kitty action handler is needed.
74
+ Fullscreen has a clickable footer: **▤** opens Pinote; single-click a task title
75
+ to preview, or double-click to complete immediately without another confirmation
76
+ (requires idle Pi). Suggestion titles only preview; double-click never accepts.
77
+ Regular mode shows status; `/pinote` provides all actions. No Kitty action handler
78
+ is needed.
76
79
 
77
80
  Accepting a suggestion, selecting a different task, or creating and selecting one
78
81
  renames the current Kitty tab to `[tag] task title` (tag omitted when unset),
@@ -212,10 +215,14 @@ See [setup, script contract and safeguards](mcp/FIELD_EVENTS.md). Disabled by de
212
215
 
213
216
  ## Migration
214
217
 
218
+ Since pi-note 0.39.2, fullscreen task-title double-click completes without another
219
+ confirmation; single-click still previews and menu completion still confirms.
220
+ Update the client and `/reload` Pi. The service must support footer double-click
221
+ (pi-note 0.39.1+). Suggestion acceptance remains menu-only.
222
+
215
223
  Since pi-note 0.38.0, the default HTTP extension uses task-first navigation and
216
- explicit completion/suggestion approval instead of footer-title double-click
217
- mutations. Update the client and service together, then `/reload`. The explicit
218
- local-only extension retains its existing menus.
224
+ explicit completion/suggestion approval. Update the client and service together,
225
+ then `/reload`. The explicit local-only extension retains its existing menus.
219
226
 
220
227
 
221
228
  Since pi-note 0.36.0, new-task proposals use `title` and optional `description`
@@ -262,7 +262,7 @@ function view(data, scoped = false, secondary = false, features = {}) {
262
262
  : [{ text: `${icons.note} `, width: 3 }];
263
263
  if (features.footerNativeActions) {
264
264
  if (task)
265
- footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', width: config.config.titleWidth });
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
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 });
268
268
  else
package/mcp/core.ts CHANGED
@@ -228,7 +228,7 @@ function view(data: Snapshot, scoped = false, secondary = false, features: { foo
228
228
  ? [{ text: `${icons['open-app']} `, openApp: true, width: 3 }]
229
229
  : [{ text: `${icons.note} `, width: 3 }];
230
230
  if (features.footerNativeActions) {
231
- if (task) footer.push({ text: `${task.tag ? `[${task.tag}] ` : ''}${task.text.split('\n')[0].slice(0, 2000)}`, action: 'preview', width: config.config.titleWidth });
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
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 });
233
233
  else footer.push({ text: 'Pinote · No task', width: config.config.titleWidth });
234
234
  } else if (task) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-note",
3
- "version": "0.39.0",
3
+ "version": "0.39.2",
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,7 +43,8 @@ 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
- return control ? this.dispatch(ctx, view, control) : false;
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;
47
48
  }, this.repaint);
48
49
  }, { placement: 'belowEditor' });
49
50
  this.render();
@@ -65,7 +66,7 @@ export class RemoteUI {
65
66
  }) : 'Pinote · unavailable (/pinote to reconnect)');
66
67
  this.repaint();
67
68
  }
68
- async dispatch(ctx: ExtensionContext, view: NativeView, control: NativeControl, value?: NativeControl['value']): Promise<boolean> {
69
+ async dispatch(ctx: ExtensionContext, view: NativeView, control: NativeControl, value?: NativeControl['value'], completionConfirmed = false): Promise<boolean> {
69
70
  if (ctx.mode !== 'tui') throw new Error('Pinote UI actions require TUI mode');
70
71
  if (this.acting) return false;
71
72
  if (control.id === 'done' && !ctx.isIdle()) {
@@ -75,7 +76,7 @@ export class RemoteUI {
75
76
  const current = this.session.current(), signal = this.session.signal;
76
77
  try {
77
78
  if (typeof value === 'string') editorText(value);
78
- if (control.id === 'done') {
79
+ if (control.id === 'done' && !completionConfirmed) {
79
80
  const exits = typeof view.state.selectedId === 'number' && this.exitOnComplete(view.state.selectedId);
80
81
  const accept = exits ? 'Complete task and exit' : 'Complete task';
81
82
  const summary = truncateToWidth(line(view.body ?? ''), 100);