@namzu/cli 17.0.1 → 18.0.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.
@@ -1 +1 @@
1
- {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAON,KAAK,OAAO,EAiBZ,MAAM,YAAY,CAAA;AA+GnB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AA2CvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAU/D,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC1D,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAA;CAC/C;AAOD,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAC5B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAuKrB,6EAA6E;AAC7E,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,MAAM,EAAE,CAM7F;AAED,sEAAsE;AACtE,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,MAAM,EAAE,MAAM,MAAM,EACpB,iBAAiB,GAAE,SAAS,MAAM,EAAO,GACvC,SAAS,iBAAiB,EAAE,CAuE9B;AA8GD,wBAAgB,GAAG,CAAC,EACnB,GAAG,EAAE,UAAU,EACf,aAAa,EACb,cAAsC,GACtC,EAAE,QAAQ,2CAkoKV"}
1
+ {"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/tui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,OAAO,EAON,KAAK,OAAO,EAiBZ,MAAM,YAAY,CAAA;AAiHnB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AA2CvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAU/D,MAAM,WAAW,QAAQ;IACxB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAA;IACxB,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,KAAK,IAAI,CAAA;IAC1D,0EAA0E;IAC1E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAA;CAC/C;AAOD,MAAM,MAAM,qBAAqB,GAAG,CAAC,OAAO,EAAE;IAC7C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;CAC5B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAuKrB,6EAA6E;AAC7E,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,GAAG,SAAS,MAAM,EAAE,CAM7F;AAED,sEAAsE;AACtE,wBAAgB,mBAAmB,CAClC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAC5B,MAAM,EAAE,MAAM,MAAM,EACpB,iBAAiB,GAAE,SAAS,MAAM,EAAO,GACvC,SAAS,iBAAiB,EAAE,CAuE9B;AA+GD,wBAAgB,GAAG,CAAC,EACnB,GAAG,EAAE,UAAU,EACf,aAAa,EACb,cAAsC,GACtC,EAAE,QAAQ,2CAwrKV"}
package/dist/tui/App.js CHANGED
@@ -28,7 +28,7 @@ import { checkUpdates } from '../integrations/updates.js';
28
28
  import { appendMemory, composeMemoryPrompt, readMemory } from '../memory/store.js';
29
29
  import { composeSkillsPrompt, discoverSkills, loadSkillBody } from '../skills/store.js';
30
30
  import { discoverUserCommands } from '../user-commands/store.js';
31
- import { AgentCockpit, AgentTranscript, agentCockpitIsWide, agentPickerPageSize, agentPhasePageSize, agentPhases, agentTranscriptPageSize, maxAgentTranscriptTailOffset, } from './AgentExplorer.js';
31
+ import { AgentCockpit, AgentTaskPanel, AgentTranscript, activeSubagentCohorts, agentCockpitIsWide, agentPhasePageSize, agentPhases, agentPickerPageSize, agentTranscriptPageSize, maxAgentTranscriptTailOffset, } from './AgentExplorer.js';
32
32
  import { ChoicePicker } from './ChoicePicker.js';
33
33
  import { Composer, suggestionWindowSize, } from './Composer.js';
34
34
  import { CopyPicker } from './CopyPicker.js';
@@ -462,9 +462,16 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
462
462
  subagentsRef.current = next;
463
463
  setSubagentsState(next);
464
464
  const surface = agentSurfaceRef.current;
465
- if (!surface || next.some((agent) => agent.viewId === surface.selectedId))
465
+ if (!surface)
466
466
  return;
467
- const phases = agentPhases(next);
467
+ const activeCohorts = activeSubagentCohorts(next);
468
+ if (activeCohorts.length === 0) {
469
+ setAgentSurface(null);
470
+ return;
471
+ }
472
+ if (activeCohorts.some((agent) => agent.viewId === surface.selectedId))
473
+ return;
474
+ const phases = agentPhases(activeCohorts);
468
475
  const fallbackPhase = phases.find((phase) => phase.id === surface.selectedPhaseId) ?? phases[0];
469
476
  const fallbackAgent = fallbackPhase?.agents[0];
470
477
  if (!fallbackPhase || !fallbackAgent) {
@@ -479,7 +486,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
479
486
  });
480
487
  }, [setAgentSurface]);
481
488
  const openAgentCockpit = useCallback(() => {
482
- const agents = subagentsRef.current;
489
+ const agents = activeSubagentCohorts(subagentsRef.current);
483
490
  const firstPhase = agentPhases(agents)[0];
484
491
  const firstAgent = firstPhase?.agents[0];
485
492
  if (!firstPhase || !firstAgent)
@@ -2502,6 +2509,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
2502
2509
  setState('tool');
2503
2510
  const tool = {
2504
2511
  id: event.toolUseId,
2512
+ ...(event.runId ? { runId: event.runId } : {}),
2505
2513
  toolName: event.toolName,
2506
2514
  label: formatToolCall(event.toolName, event.summary, event.standalone),
2507
2515
  startedAt: Date.now(),
@@ -2513,7 +2521,8 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
2513
2521
  }
2514
2522
  case 'tool-progress': {
2515
2523
  const running = activeToolsRef.current;
2516
- const index = running.findIndex((tool) => tool.id === event.toolUseId);
2524
+ const index = running.findIndex((tool) => tool.id === event.toolUseId &&
2525
+ (event.runId === undefined || tool.runId === event.runId));
2517
2526
  // A terminal event may already have removed this call. Never recreate
2518
2527
  // a live row from late diagnostic state.
2519
2528
  if (index < 0)
@@ -2539,12 +2548,25 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
2539
2548
  // active tool" — under parallel calls that mis-attributes a
2540
2549
  // result to the wrong call. If no id matches, render the
2541
2550
  // completion on its own (label from the end event itself).
2542
- const i = running.findIndex((t) => t.id === event.toolUseId);
2551
+ const i = running.findIndex((tool) => tool.id === event.toolUseId &&
2552
+ (event.runId === undefined || tool.runId === event.runId));
2543
2553
  const done = i >= 0 ? running[i] : undefined;
2544
2554
  if (i >= 0) {
2545
2555
  activeToolsRef.current = [...running.slice(0, i), ...running.slice(i + 1)];
2546
2556
  setActiveTools(activeToolsRef.current);
2547
2557
  }
2558
+ const representedSuccessfulAgent = !event.isError &&
2559
+ event.toolName.toLowerCase() === 'agent' &&
2560
+ subagentsRef.current.some((agent) => agent.toolUseId === event.toolUseId &&
2561
+ (event.runId === undefined || agent.workflowId === event.runId));
2562
+ if (representedSuccessfulAgent) {
2563
+ // The automatic panel already owned this successful child from
2564
+ // start through settlement. Re-emitting its internal Agent call as
2565
+ // a transcript tool row after the panel closes is duplicate protocol,
2566
+ // not useful history. Failures remain durable transcript rows.
2567
+ setState(activeToolsRef.current.length > 0 ? 'tool' : 'thinking');
2568
+ break;
2569
+ }
2548
2570
  pushMessage('tool', done?.label ?? formatToolCall(event.toolName, event.summary), false, event.isError ? '✗' : '✓', done?.detail, event.isError ? theme.status.error : theme.status.ok, event.durationMs !== undefined
2549
2571
  ? formatElapsed(event.durationMs)
2550
2572
  : done
@@ -3101,7 +3123,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
3101
3123
  }
3102
3124
  case 'agent-cockpit': {
3103
3125
  if (!openAgentCockpit()) {
3104
- pushMessage('system', 'No delegated agents are available in this conversation yet. When an Agent tool starts one, /agent opens its live activity and retained transcript.');
3126
+ pushMessage('system', 'No delegated agents are active. When an Agent tool starts one, its live activity appears below the composer and this view can inspect it.');
3105
3127
  }
3106
3128
  return;
3107
3129
  }
@@ -4241,8 +4263,14 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
4241
4263
  // Open the same retained cockpit as `/agent`; Enter then drills into a
4242
4264
  // child's transcript. Keep the shortcut below consent so it can never
4243
4265
  // hide an approval that owns the keyboard.
4244
- if (key.ctrl && input === 't' && openAgentCockpit())
4245
- return;
4266
+ if (key.ctrl && input === 't') {
4267
+ if (agentSurfaceRef.current) {
4268
+ setAgentSurface(null);
4269
+ return;
4270
+ }
4271
+ if (openAgentCockpit())
4272
+ return;
4273
+ }
4246
4274
  // Child observation owns navigation, but never outranks consent: the
4247
4275
  // permission branch above preempts it if a child or parent call asks.
4248
4276
  const agentView = agentSurfaceRef.current;
@@ -4251,7 +4279,7 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
4251
4279
  // highlighted child before the list has appeared on screen.
4252
4280
  if (agentSurfaceCommittedRef.current !== agentView)
4253
4281
  return;
4254
- const agents = subagentsRef.current;
4282
+ const agents = activeSubagentCohorts(subagentsRef.current);
4255
4283
  if (agentView.kind === 'cockpit') {
4256
4284
  const phases = agentPhases(agents);
4257
4285
  const phaseIndex = Math.max(0, phases.findIndex((phase) => phase.id === agentView.selectedPhaseId));
@@ -4533,9 +4561,21 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
4533
4561
  // Background is left natural — we inherit the terminal's own background
4534
4562
  // and only theme the foreground. Forcing
4535
4563
  // a filled bg left mismatched patches around bordered areas, so we don't.
4536
- const liveSubagentCount = subagents.filter((agent) => agent.status === 'starting' || agent.status === 'working').length;
4564
+ const liveSubagents = activeSubagentCohorts(subagents);
4565
+ const representedSubagentTools = new Set(subagents
4566
+ .filter((agent) => agent.toolUseId !== undefined)
4567
+ .map((agent) => JSON.stringify([agent.workflowId, agent.toolUseId])));
4568
+ const representedUnscopedSubagentToolUseIds = new Set(subagents.map((agent) => agent.toolUseId).filter((id) => id !== undefined));
4569
+ // Keep the exact parent Agent row suppressed through the child-settled →
4570
+ // tool-end hand-off. The monitor publishes terminal state synchronously,
4571
+ // while the provider event that removes the generic tool row arrives on the
4572
+ // next tick; filtering only live children made that raw row flash back.
4573
+ const visibleActiveTools = activeTools.filter((tool) => tool.toolName.toLowerCase() !== 'agent' ||
4574
+ !(tool.runId
4575
+ ? representedSubagentTools.has(JSON.stringify([tool.runId, tool.id]))
4576
+ : representedUnscopedSubagentToolUseIds.has(tool.id)));
4537
4577
  const selectedSubagent = agentSurface
4538
- ? subagents.find((agent) => agent.viewId === agentSurface.selectedId)
4578
+ ? liveSubagents.find((agent) => agent.viewId === agentSurface.selectedId)
4539
4579
  : undefined;
4540
4580
  const lifecycleOwnsViewport = phase === 'trust' ||
4541
4581
  phase === 'resume' ||
@@ -4580,16 +4620,13 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
4580
4620
  : goalStatus && phase === 'ready' && state === 'idle'
4581
4621
  ? undefined
4582
4622
  : hintForPhase(phase, state, session?.hasProvider === true, composerHasDraft);
4583
- return (_jsx(Box, { flexDirection: "column", display: externalEditorRequest ? 'none' : 'flex', children: _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [transcriptOwned || !lifecycleOwnsViewport ? (_jsx(TranscriptFrame, { children: _jsx(Transcript, { messages: finalized, pending: messages.find((m) => m.pending) ?? null, state: state, settled: renderedSettled, resetKey: resetKey, raw: rawOutput, hyperlinks: hyperlinks, showLive: !lifecycleOwnsViewport, header: transcriptOwned ? (_jsx(Banner, { version: ctx.version, session: session, permissionMode: permissionMode, cwd: ctx.cwd })) : undefined }) })) : null, phase === 'trust' ? (_jsx(TrustPrompt, { cwd: ctx.cwd })) : phase === 'resume' ? (_jsx(ResumePicker, { conversations: resumeList, selected: selectedResume })) : phase === 'edit' ? (_jsx(EditPromptPicker, { prompts: editList, selected: selectedEdit })) : phase === 'picker' ? (_jsx(Picker, { detected: pickerDetected ?? detected, currentProvider: currentProvider, selectionKind: pickerSelectionKind, currentModel: session?.modelSummary ?? null, initialView: pickerInitialView, onSubmit: handlePickerSubmit, onCancel: handlePickerCancel, onCredential: handleTypedCredential, onLogin: startLoginFromPicker, onLoginComplete: finishLoginFromPicker, keyEntryFor: keyEntryFor, notice: pickerNotice })) : (_jsxs(_Fragment, { children: [agentSurface === null ? (_jsx(LiveActivity, { activeTools: activeTools, working: state === 'thinking' || state === 'tool', agentCount: liveSubagentCount, interruptible: abortRef.current !== null, animate: stdout.isTTY === true })) : null, permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls, review: permission.review, summary: permission.summary, detailsOpen: permissionDetailsOpen, reviewOffset: permissionReviewOffset, columns: terminal.columns })) : null, permission === null && agentSurface?.kind === 'cockpit' ? (_jsx(AgentCockpit, { agents: subagents, selectedPhaseId: agentSurface.selectedPhaseId, selectedId: agentSurface.selectedId, focus: agentSurface.focus, terminalRows: terminal.rows, terminalColumns: terminal.columns })) : permission === null && agentSurface?.kind === 'transcript' && selectedSubagent ? (_jsx(AgentTranscript, { agent: selectedSubagent, tailOffset: agentSurface.tailOffset, terminalRows: terminal.rows, terminalColumns: terminal.columns })) : null, textPrompt ? (_jsx(TextPrompt, { title: textPrompt.title, placeholder: textPrompt.placeholder, initialValue: textPrompt.initialValue, emptyNotice: textPrompt.emptyNotice, hidden: permission !== null || agentSurface !== null, onSubmit: submitTextPrompt, onCancel: cancelTextPrompt }, textPrompt.token)) : permission === null && agentSurface === null && choicePicker ? (_jsx(ChoicePicker, { title: choicePicker.title, notice: choicePicker.notice, options: choicePicker.options, selected: selectedChoice, windowSize: choicePicker.kind === 'command' ? choicePicker.windowSize : undefined })) : permission === null && agentSurface === null && copyPicker ? (_jsx(CopyPicker, { targets: copyPicker.targets, selected: selectedCopy })) : null, _jsxs(ComposerFrame, { focus: state === 'idle' &&
4623
+ return (_jsx(Box, { flexDirection: "column", display: externalEditorRequest ? 'none' : 'flex', children: _jsxs(Box, { flexDirection: "column", paddingX: 1, children: [transcriptOwned || !lifecycleOwnsViewport ? (_jsx(TranscriptFrame, { children: _jsx(Transcript, { messages: finalized, pending: messages.find((m) => m.pending) ?? null, state: state, settled: renderedSettled, resetKey: resetKey, raw: rawOutput, hyperlinks: hyperlinks, showLive: !lifecycleOwnsViewport, header: transcriptOwned ? (_jsx(Banner, { version: ctx.version, session: session, permissionMode: permissionMode, cwd: ctx.cwd })) : undefined }) })) : null, phase === 'trust' ? (_jsx(TrustPrompt, { cwd: ctx.cwd })) : phase === 'resume' ? (_jsx(ResumePicker, { conversations: resumeList, selected: selectedResume })) : phase === 'edit' ? (_jsx(EditPromptPicker, { prompts: editList, selected: selectedEdit })) : phase === 'picker' ? (_jsx(Picker, { detected: pickerDetected ?? detected, currentProvider: currentProvider, selectionKind: pickerSelectionKind, currentModel: session?.modelSummary ?? null, initialView: pickerInitialView, onSubmit: handlePickerSubmit, onCancel: handlePickerCancel, onCredential: handleTypedCredential, onLogin: startLoginFromPicker, onLoginComplete: finishLoginFromPicker, keyEntryFor: keyEntryFor, notice: pickerNotice })) : (_jsxs(_Fragment, { children: [agentSurface === null ? (_jsx(LiveActivity, { activeTools: visibleActiveTools, working: state === 'thinking' || state === 'tool', interruptible: abortRef.current !== null, animate: stdout.isTTY === true })) : null, permission ? (_jsx(PermissionOverlay, { toolCalls: permission.toolCalls, review: permission.review, summary: permission.summary, detailsOpen: permissionDetailsOpen, reviewOffset: permissionReviewOffset, columns: terminal.columns })) : null, textPrompt ? (_jsx(TextPrompt, { title: textPrompt.title, placeholder: textPrompt.placeholder, initialValue: textPrompt.initialValue, emptyNotice: textPrompt.emptyNotice, hidden: permission !== null || agentSurface !== null, onSubmit: submitTextPrompt, onCancel: cancelTextPrompt }, textPrompt.token)) : permission === null && agentSurface === null && choicePicker ? (_jsx(ChoicePicker, { title: choicePicker.title, notice: choicePicker.notice, options: choicePicker.options, selected: selectedChoice, windowSize: choicePicker.kind === 'command' ? choicePicker.windowSize : undefined })) : permission === null && agentSurface === null && copyPicker ? (_jsx(CopyPicker, { targets: copyPicker.targets, selected: selectedCopy })) : null, _jsxs(ComposerFrame, { focus: state === 'idle' &&
4584
4624
  phase === 'ready' &&
4585
4625
  conversationMutation === null &&
4586
4626
  textPrompt === null &&
4587
4627
  choicePicker === null &&
4588
4628
  copyPicker === null &&
4589
- agentSurface === null, hidden: permission !== null ||
4590
- choicePicker !== null ||
4591
- copyPicker !== null ||
4592
- agentSurface !== null, children: [pendingSteers.length > 0 &&
4629
+ agentSurface === null, hidden: permission !== null || choicePicker !== null || copyPicker !== null, children: [pendingSteers.length > 0 &&
4593
4630
  permission === null &&
4594
4631
  textPrompt === null &&
4595
4632
  choicePicker === null &&
@@ -4611,11 +4648,10 @@ export function App({ ctx: initialCtx, onExitSummary, externalEditor = defaultEx
4611
4648
  agentSurface !== null, hidden: permission !== null ||
4612
4649
  textPrompt !== null ||
4613
4650
  choicePicker !== null ||
4614
- copyPicker !== null ||
4615
- agentSurface !== null,
4651
+ copyPicker !== null,
4616
4652
  // A turn is running, so Esc is the interrupt and not
4617
4653
  // the composer's clear.
4618
- escapeInterrupts: !compacting && (state === 'thinking' || state === 'tool'), onSubmit: handleSubmit, onNotice: (text) => pushMessage('system', text), onStepReasoningEffort: stepReasoningEffort, onExternalEdit: requestExternalEditor, onEditPrevious: openPromptEditor, draftToRestore: composerDraft, onDraftRestored: (token) => setComposerDraft((draft) => (draft?.token === token ? null : draft)), onDraftPresenceChange: setComposerHasDraft, userCommands: userCommands, mentionCandidates: mentionCandidates, history: history })] })] })), _jsx(Box, { paddingTop: 1, children: _jsx(StatusBar, { cwd: ctx.cwd, provider: session?.providerSummary ?? null, model: session?.modelSummary ?? null, effort: reasoningEffort ?? 'default', goal: statusGoal, state: state, hint: statusHint }) })] }) }));
4654
+ escapeInterrupts: !compacting && (state === 'thinking' || state === 'tool'), onSubmit: handleSubmit, onNotice: (text) => pushMessage('system', text), onStepReasoningEffort: stepReasoningEffort, onExternalEdit: requestExternalEditor, onEditPrevious: openPromptEditor, draftToRestore: composerDraft, onDraftRestored: (token) => setComposerDraft((draft) => (draft?.token === token ? null : draft)), onDraftPresenceChange: setComposerHasDraft, onOpenAgentPanel: openAgentCockpit, userCommands: userCommands, mentionCandidates: mentionCandidates, history: history })] }), permission === null && agentSurface === null && liveSubagents.length > 0 ? (_jsx(AgentTaskPanel, { agents: liveSubagents, terminalRows: terminal.rows, terminalColumns: terminal.columns })) : permission === null && agentSurface?.kind === 'cockpit' ? (_jsx(AgentCockpit, { agents: liveSubagents, selectedPhaseId: agentSurface.selectedPhaseId, selectedId: agentSurface.selectedId, focus: agentSurface.focus, terminalRows: terminal.rows, terminalColumns: terminal.columns })) : permission === null && agentSurface?.kind === 'transcript' && selectedSubagent ? (_jsx(AgentTranscript, { agent: selectedSubagent, tailOffset: agentSurface.tailOffset, terminalRows: terminal.rows, terminalColumns: terminal.columns })) : null] })), _jsx(Box, { paddingTop: 1, children: _jsx(StatusBar, { cwd: ctx.cwd, provider: session?.providerSummary ?? null, model: session?.modelSummary ?? null, effort: reasoningEffort ?? 'default', goal: statusGoal, state: state, hint: statusHint }) })] }) }));
4619
4655
  }
4620
4656
  function Banner({ version, session, permissionMode, cwd, }) {
4621
4657
  const { columns } = useWindowSize();