@leviyuan/lodestar 0.1.3 → 0.1.4
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/src/cards.ts +41 -21
- package/src/session.ts +8 -2
package/package.json
CHANGED
package/src/cards.ts
CHANGED
|
@@ -337,6 +337,11 @@ export function askUserQuestionElement(
|
|
|
337
337
|
toolUseId: string,
|
|
338
338
|
questions: AskQuestion[],
|
|
339
339
|
status: '🤔' | '✅' | '❌' = '🤔',
|
|
340
|
+
/** When set, only this option is rendered as "picked" — every other
|
|
341
|
+
* option turns into plain text (no click target). Used after the
|
|
342
|
+
* user has answered so the panel freezes in a sensible terminal
|
|
343
|
+
* state instead of inviting another click. */
|
|
344
|
+
pickedOptionIdx?: number,
|
|
340
345
|
resolvedNote?: string,
|
|
341
346
|
): object {
|
|
342
347
|
const primary = questions[0]
|
|
@@ -345,18 +350,38 @@ export function askUserQuestionElement(
|
|
|
345
350
|
const bodyElements: any[] = []
|
|
346
351
|
if (primary) {
|
|
347
352
|
bodyElements.push({ tag: 'markdown', content: `**${primary.question}**` })
|
|
348
|
-
//
|
|
349
|
-
//
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
353
|
+
// One row per option, each a full-width interactive_container so
|
|
354
|
+
// the entire row (label + description) is the click target. Looks
|
|
355
|
+
// cleaner than a row of squat buttons and matches the way IM
|
|
356
|
+
// quick-replies usually present themselves. After the user picks,
|
|
357
|
+
// we still render the same row layout (no JSON dump) but strip
|
|
358
|
+
// the callbacks — selected option marked ✅, others dimmed.
|
|
359
|
+
for (let optIdx = 0; optIdx < primary.options.length; optIdx++) {
|
|
360
|
+
const opt = primary.options[optIdx]
|
|
361
|
+
const isPicked = pickedOptionIdx === optIdx
|
|
362
|
+
const isAnswered = pickedOptionIdx !== undefined
|
|
363
|
+
const labelLine = isPicked
|
|
364
|
+
? `**✅ ${opt.label}**`
|
|
365
|
+
: isAnswered
|
|
366
|
+
? `~~${opt.label}~~`
|
|
367
|
+
: `**${opt.label}**`
|
|
368
|
+
const descLine = opt.description ? `\n${opt.description}` : ''
|
|
369
|
+
const rowContent = { tag: 'markdown', content: `${labelLine}${descLine}` }
|
|
370
|
+
if (isAnswered) {
|
|
371
|
+
// Frozen — no behaviors, plain container so it stops looking
|
|
372
|
+
// clickable.
|
|
373
|
+
bodyElements.push({
|
|
374
|
+
tag: 'div',
|
|
375
|
+
elements: [rowContent],
|
|
376
|
+
})
|
|
377
|
+
} else {
|
|
378
|
+
bodyElements.push({
|
|
379
|
+
tag: 'interactive_container',
|
|
380
|
+
background_style: 'default',
|
|
381
|
+
has_border: true,
|
|
382
|
+
corner_radius: '6px',
|
|
383
|
+
padding: '8px 12px',
|
|
384
|
+
margin: '4px 0',
|
|
360
385
|
behaviors: [{
|
|
361
386
|
type: 'callback',
|
|
362
387
|
value: {
|
|
@@ -366,15 +391,10 @@ export function askUserQuestionElement(
|
|
|
366
391
|
option_idx: optIdx,
|
|
367
392
|
},
|
|
368
393
|
}],
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
// read context without hovering.
|
|
374
|
-
const descLines = primary.options
|
|
375
|
-
.map((o, idx) => o.description ? `- **${o.label}** — ${o.description}` : `- **${o.label}**`)
|
|
376
|
-
.join('\n')
|
|
377
|
-
if (descLines) bodyElements.push({ tag: 'markdown', content: descLines })
|
|
394
|
+
elements: [rowContent],
|
|
395
|
+
})
|
|
396
|
+
}
|
|
397
|
+
}
|
|
378
398
|
}
|
|
379
399
|
// Secondary questions get text-only treatment (TODO: multi-question
|
|
380
400
|
// panels when actually requested by a real prompt).
|
package/src/session.ts
CHANGED
|
@@ -382,8 +382,8 @@ export class Session {
|
|
|
382
382
|
if (turn && meta) {
|
|
383
383
|
meta.output = JSON.stringify({ answers })
|
|
384
384
|
meta.isError = false
|
|
385
|
-
const resolvedNote = `\n\n
|
|
386
|
-
const el = cards.askUserQuestionElement(meta.i, toolUseId, pending.questions, '✅', resolvedNote)
|
|
385
|
+
const resolvedNote = `\n\n*已由 ${user || '匿名'} 回答*`
|
|
386
|
+
const el = cards.askUserQuestionElement(meta.i, toolUseId, pending.questions, '✅', optionIdx, resolvedNote)
|
|
387
387
|
void cardkit.replaceElement(turn.cardId, cards.ELEMENTS.tool(meta.i), el)
|
|
388
388
|
}
|
|
389
389
|
|
|
@@ -606,6 +606,12 @@ export class Session {
|
|
|
606
606
|
// can't discard the output after the first paint.
|
|
607
607
|
meta.output = output
|
|
608
608
|
meta.isError = isError
|
|
609
|
+
// AskUserQuestion already had its final panel painted by resolveAsk
|
|
610
|
+
// (✅ + the chosen option marked, others dimmed). The tool_result
|
|
611
|
+
// arriving here is just the SDK's synthesised echo — re-rendering
|
|
612
|
+
// via toolCallElement would clobber the nice option-row layout
|
|
613
|
+
// with a generic JSON dump. Bail out; the panel is done.
|
|
614
|
+
if (meta.name === 'AskUserQuestion') return
|
|
609
615
|
// Update the local todo mirror BEFORE rendering so the just-
|
|
610
616
|
// completed panel shows the new state too (e.g. a TaskCreate panel
|
|
611
617
|
// already lists the task it just created).
|