spawn-term 3.2.3 → 3.2.5

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.
@@ -22,20 +22,22 @@ var _default = /*#__PURE__*/ (0, _react.memo)(function FullscreenOverlay(param)
22
22
  var title = param.title, lines = param.lines, scrollOffset = param.scrollOffset;
23
23
  var stdout = (0, _ink.useStdout)().stdout;
24
24
  var terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
25
+ var enteredRef = (0, _react.useRef)(false);
25
26
  // Reserve lines for header (title + divider) and footer (scroll hint)
26
27
  var headerLines = 2;
27
28
  var footerLines = 1;
28
29
  var maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);
29
- // Enter alternate screen on mount, exit on unmount
30
- (0, _react.useEffect)(function() {
31
- if (stdout) {
32
- stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
33
- }
30
+ // Enter alternate screen SYNCHRONOUSLY on first render, before ink outputs anything
31
+ if (!enteredRef.current && stdout) {
32
+ stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
33
+ enteredRef.current = true;
34
+ }
35
+ // Exit alternate screen on unmount
36
+ (0, _react.useLayoutEffect)(function() {
34
37
  return function() {
35
- if (stdout) {
36
- // Exit alternate screen, then clear main screen and reset cursor
37
- // This helps ink re-render cleanly to a known position
38
- stdout.write(EXIT_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
38
+ if (stdout && enteredRef.current) {
39
+ stdout.write(EXIT_ALT_SCREEN);
40
+ enteredRef.current = false;
39
41
  }
40
42
  };
41
43
  }, [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/FullscreenOverlay.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useEffect } from 'react';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\n// ANSI escape codes for alternate screen buffer\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst CLEAR_SCREEN = '\\x1b[2J';\nconst CURSOR_HOME = '\\x1b[H';\nconst HIDE_CURSOR = '\\x1b[?25l';\n\ntype Props = {\n title: string;\n lines: Line[];\n scrollOffset: number;\n onExit: () => void;\n};\n\nexport default memo(function FullscreenOverlay({ title, lines, scrollOffset }: Props) {\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Reserve lines for header (title + divider) and footer (scroll hint)\n const headerLines = 2;\n const footerLines = 1;\n const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);\n\n // Enter alternate screen on mount, exit on unmount\n useEffect(() => {\n if (stdout) {\n stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);\n }\n return () => {\n if (stdout) {\n // Exit alternate screen, then clear main screen and reset cursor\n // This helps ink re-render cleanly to a known position\n stdout.write(EXIT_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);\n }\n };\n }, [stdout]);\n\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const totalLines = lines.length;\n const currentLine = scrollOffset + 1;\n const endLine = Math.min(scrollOffset + maxVisible, totalLines);\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n <Text bold color=\"cyan\">\n {title}\n </Text>\n <Text dimColor>{'─'.repeat(Math.min(80, stdout?.columns || 80))}</Text>\n\n {/* Content */}\n <Box flexDirection=\"column\" flexGrow={1}>\n {lines.length === 0 ? (\n <Text dimColor>(no output)</Text>\n ) : (\n visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>{line.text}</Text>\n ))\n )}\n </Box>\n\n {/* Footer */}\n <Text dimColor>\n Lines {currentLine}-{endLine} of {totalLines} | j/k scroll | Tab/⇧Tab page | {isMac ? '⌥↑/↓' : 'g/G'} top/bottom | ↵/q exit\n </Text>\n </Box>\n );\n});\n"],"names":["isMac","process","platform","ENTER_ALT_SCREEN","EXIT_ALT_SCREEN","CLEAR_SCREEN","CURSOR_HOME","HIDE_CURSOR","memo","FullscreenOverlay","title","lines","scrollOffset","stdout","useStdout","terminalHeight","rows","headerLines","footerLines","maxVisible","Math","max","useEffect","write","visibleLines","slice","totalLines","length","currentLine","endLine","min","Box","flexDirection","height","Text","bold","color","dimColor","repeat","columns","flexGrow","map","line","i","text"],"mappings":";;;;+BAoBA;;;eAAA;;;;mBApBqC;qBACL;AAGhC,IAAMA,QAAQC,QAAQC,QAAQ,KAAK;AAEnC,gDAAgD;AAChD,IAAMC,mBAAmB;AACzB,IAAMC,kBAAkB;AACxB,IAAMC,eAAe;AACrB,IAAMC,cAAc;AACpB,IAAMC,cAAc;IASpB,yBAAeC,IAAAA,WAAI,EAAC,SAASC,kBAAkB,KAAqC;QAAnCC,QAAF,MAAEA,OAAOC,QAAT,MAASA,OAAOC,eAAhB,MAAgBA;IAC7D,IAAM,AAAEC,SAAWC,IAAAA,cAAS,IAApBD;IACR,IAAME,iBAAiBF,CAAAA,mBAAAA,6BAAAA,OAAQG,IAAI,KAAI;IAEvC,sEAAsE;IACtE,IAAMC,cAAc;IACpB,IAAMC,cAAc;IACpB,IAAMC,aAAaC,KAAKC,GAAG,CAAC,GAAGN,iBAAiBE,cAAcC;IAE9D,mDAAmD;IACnDI,IAAAA,gBAAS,EAAC;QACR,IAAIT,QAAQ;YACVA,OAAOU,KAAK,CAACpB,mBAAmBE,eAAeC,cAAcC;QAC/D;QACA,OAAO;YACL,IAAIM,QAAQ;gBACV,iEAAiE;gBACjE,uDAAuD;gBACvDA,OAAOU,KAAK,CAACnB,kBAAkBC,eAAeC,cAAcC;YAC9D;QACF;IACF,GAAG;QAACM;KAAO;IAEX,IAAMW,eAAeb,MAAMc,KAAK,CAACb,cAAcA,eAAeO;IAC9D,IAAMO,aAAaf,MAAMgB,MAAM;IAC/B,IAAMC,cAAchB,eAAe;IACnC,IAAMiB,UAAUT,KAAKU,GAAG,CAAClB,eAAeO,YAAYO;IAEpD,qBACE,sBAACK,QAAG;QAACC,eAAc;QAASC,QAAQlB;;0BAElC,qBAACmB,SAAI;gBAACC,IAAI;gBAACC,OAAM;0BACd1B;;0BAEH,qBAACwB,SAAI;gBAACG,QAAQ;0BAAE,IAAIC,MAAM,CAAClB,KAAKU,GAAG,CAAC,IAAIjB,CAAAA,mBAAAA,6BAAAA,OAAQ0B,OAAO,KAAI;;0BAG3D,qBAACR,QAAG;gBAACC,eAAc;gBAASQ,UAAU;0BACnC7B,MAAMgB,MAAM,KAAK,kBAChB,qBAACO,SAAI;oBAACG,QAAQ;8BAAC;qBAEfb,aAAaiB,GAAG,CAAC,SAACC,MAAMC;2BACtB,iHAAiH;kCACjH,qBAACT,SAAI;kCAAyBQ,KAAKE,IAAI;uBAA5BhC,eAAe+B;;;0BAMhC,sBAACT,SAAI;gBAACG,QAAQ;;oBAAC;oBACNT;oBAAY;oBAAEC;oBAAQ;oBAAKH;oBAAW;oBAAiC1B,QAAQ,SAAS;oBAAM;;;;;AAI7G"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/FullscreenOverlay.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useLayoutEffect, useRef } from 'react';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\n// ANSI escape codes for alternate screen buffer\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst CLEAR_SCREEN = '\\x1b[2J';\nconst CURSOR_HOME = '\\x1b[H';\nconst HIDE_CURSOR = '\\x1b[?25l';\n\ntype Props = {\n title: string;\n lines: Line[];\n scrollOffset: number;\n onExit: () => void;\n};\n\nexport default memo(function FullscreenOverlay({ title, lines, scrollOffset }: Props) {\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n const enteredRef = useRef(false);\n\n // Reserve lines for header (title + divider) and footer (scroll hint)\n const headerLines = 2;\n const footerLines = 1;\n const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);\n\n // Enter alternate screen SYNCHRONOUSLY on first render, before ink outputs anything\n if (!enteredRef.current && stdout) {\n stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);\n enteredRef.current = true;\n }\n\n // Exit alternate screen on unmount\n useLayoutEffect(() => {\n return () => {\n if (stdout && enteredRef.current) {\n stdout.write(EXIT_ALT_SCREEN);\n enteredRef.current = false;\n }\n };\n }, [stdout]);\n\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const totalLines = lines.length;\n const currentLine = scrollOffset + 1;\n const endLine = Math.min(scrollOffset + maxVisible, totalLines);\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n <Text bold color=\"cyan\">\n {title}\n </Text>\n <Text dimColor>{'─'.repeat(Math.min(80, stdout?.columns || 80))}</Text>\n\n {/* Content */}\n <Box flexDirection=\"column\" flexGrow={1}>\n {lines.length === 0 ? (\n <Text dimColor>(no output)</Text>\n ) : (\n visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>{line.text}</Text>\n ))\n )}\n </Box>\n\n {/* Footer */}\n <Text dimColor>\n Lines {currentLine}-{endLine} of {totalLines} | j/k scroll | Tab/⇧Tab page | {isMac ? '⌥↑/↓' : 'g/G'} top/bottom | ↵/q exit\n </Text>\n </Box>\n );\n});\n"],"names":["isMac","process","platform","ENTER_ALT_SCREEN","EXIT_ALT_SCREEN","CLEAR_SCREEN","CURSOR_HOME","HIDE_CURSOR","memo","FullscreenOverlay","title","lines","scrollOffset","stdout","useStdout","terminalHeight","rows","enteredRef","useRef","headerLines","footerLines","maxVisible","Math","max","current","write","useLayoutEffect","visibleLines","slice","totalLines","length","currentLine","endLine","min","Box","flexDirection","height","Text","bold","color","dimColor","repeat","columns","flexGrow","map","line","i","text"],"mappings":";;;;+BAoBA;;;eAAA;;;;mBApBqC;qBACS;AAG9C,IAAMA,QAAQC,QAAQC,QAAQ,KAAK;AAEnC,gDAAgD;AAChD,IAAMC,mBAAmB;AACzB,IAAMC,kBAAkB;AACxB,IAAMC,eAAe;AACrB,IAAMC,cAAc;AACpB,IAAMC,cAAc;IASpB,yBAAeC,IAAAA,WAAI,EAAC,SAASC,kBAAkB,KAAqC;QAAnCC,QAAF,MAAEA,OAAOC,QAAT,MAASA,OAAOC,eAAhB,MAAgBA;IAC7D,IAAM,AAAEC,SAAWC,IAAAA,cAAS,IAApBD;IACR,IAAME,iBAAiBF,CAAAA,mBAAAA,6BAAAA,OAAQG,IAAI,KAAI;IACvC,IAAMC,aAAaC,IAAAA,aAAM,EAAC;IAE1B,sEAAsE;IACtE,IAAMC,cAAc;IACpB,IAAMC,cAAc;IACpB,IAAMC,aAAaC,KAAKC,GAAG,CAAC,GAAGR,iBAAiBI,cAAcC;IAE9D,oFAAoF;IACpF,IAAI,CAACH,WAAWO,OAAO,IAAIX,QAAQ;QACjCA,OAAOY,KAAK,CAACtB,mBAAmBE,eAAeC,cAAcC;QAC7DU,WAAWO,OAAO,GAAG;IACvB;IAEA,mCAAmC;IACnCE,IAAAA,sBAAe,EAAC;QACd,OAAO;YACL,IAAIb,UAAUI,WAAWO,OAAO,EAAE;gBAChCX,OAAOY,KAAK,CAACrB;gBACba,WAAWO,OAAO,GAAG;YACvB;QACF;IACF,GAAG;QAACX;KAAO;IAEX,IAAMc,eAAehB,MAAMiB,KAAK,CAAChB,cAAcA,eAAeS;IAC9D,IAAMQ,aAAalB,MAAMmB,MAAM;IAC/B,IAAMC,cAAcnB,eAAe;IACnC,IAAMoB,UAAUV,KAAKW,GAAG,CAACrB,eAAeS,YAAYQ;IAEpD,qBACE,sBAACK,QAAG;QAACC,eAAc;QAASC,QAAQrB;;0BAElC,qBAACsB,SAAI;gBAACC,IAAI;gBAACC,OAAM;0BACd7B;;0BAEH,qBAAC2B,SAAI;gBAACG,QAAQ;0BAAE,IAAIC,MAAM,CAACnB,KAAKW,GAAG,CAAC,IAAIpB,CAAAA,mBAAAA,6BAAAA,OAAQ6B,OAAO,KAAI;;0BAG3D,qBAACR,QAAG;gBAACC,eAAc;gBAASQ,UAAU;0BACnChC,MAAMmB,MAAM,KAAK,kBAChB,qBAACO,SAAI;oBAACG,QAAQ;8BAAC;qBAEfb,aAAaiB,GAAG,CAAC,SAACC,MAAMC;2BACtB,iHAAiH;kCACjH,qBAACT,SAAI;kCAAyBQ,KAAKE,IAAI;uBAA5BnC,eAAekC;;;0BAMhC,sBAACT,SAAI;gBAACG,QAAQ;;oBAAC;oBACNT;oBAAY;oBAAEC;oBAAQ;oBAAKH;oBAAW;oBAAiC7B,QAAQ,SAAS;oBAAM;;;;;AAI7G"}
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text, useStdout } from 'ink';
3
- import { memo, useEffect } from 'react';
3
+ import { memo, useLayoutEffect, useRef } from 'react';
4
4
  const isMac = process.platform === 'darwin';
5
5
  // ANSI escape codes for alternate screen buffer
6
6
  const ENTER_ALT_SCREEN = '\x1b[?1049h';
@@ -11,20 +11,22 @@ const HIDE_CURSOR = '\x1b[?25l';
11
11
  export default /*#__PURE__*/ memo(function FullscreenOverlay({ title, lines, scrollOffset }) {
12
12
  const { stdout } = useStdout();
13
13
  const terminalHeight = (stdout === null || stdout === void 0 ? void 0 : stdout.rows) || 24;
14
+ const enteredRef = useRef(false);
14
15
  // Reserve lines for header (title + divider) and footer (scroll hint)
15
16
  const headerLines = 2;
16
17
  const footerLines = 1;
17
18
  const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);
18
- // Enter alternate screen on mount, exit on unmount
19
- useEffect(()=>{
20
- if (stdout) {
21
- stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
22
- }
19
+ // Enter alternate screen SYNCHRONOUSLY on first render, before ink outputs anything
20
+ if (!enteredRef.current && stdout) {
21
+ stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
22
+ enteredRef.current = true;
23
+ }
24
+ // Exit alternate screen on unmount
25
+ useLayoutEffect(()=>{
23
26
  return ()=>{
24
- if (stdout) {
25
- // Exit alternate screen, then clear main screen and reset cursor
26
- // This helps ink re-render cleanly to a known position
27
- stdout.write(EXIT_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);
27
+ if (stdout && enteredRef.current) {
28
+ stdout.write(EXIT_ALT_SCREEN);
29
+ enteredRef.current = false;
28
30
  }
29
31
  };
30
32
  }, [
@@ -1 +1 @@
1
- {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/FullscreenOverlay.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useEffect } from 'react';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\n// ANSI escape codes for alternate screen buffer\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst CLEAR_SCREEN = '\\x1b[2J';\nconst CURSOR_HOME = '\\x1b[H';\nconst HIDE_CURSOR = '\\x1b[?25l';\n\ntype Props = {\n title: string;\n lines: Line[];\n scrollOffset: number;\n onExit: () => void;\n};\n\nexport default memo(function FullscreenOverlay({ title, lines, scrollOffset }: Props) {\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n\n // Reserve lines for header (title + divider) and footer (scroll hint)\n const headerLines = 2;\n const footerLines = 1;\n const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);\n\n // Enter alternate screen on mount, exit on unmount\n useEffect(() => {\n if (stdout) {\n stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);\n }\n return () => {\n if (stdout) {\n // Exit alternate screen, then clear main screen and reset cursor\n // This helps ink re-render cleanly to a known position\n stdout.write(EXIT_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);\n }\n };\n }, [stdout]);\n\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const totalLines = lines.length;\n const currentLine = scrollOffset + 1;\n const endLine = Math.min(scrollOffset + maxVisible, totalLines);\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n <Text bold color=\"cyan\">\n {title}\n </Text>\n <Text dimColor>{'─'.repeat(Math.min(80, stdout?.columns || 80))}</Text>\n\n {/* Content */}\n <Box flexDirection=\"column\" flexGrow={1}>\n {lines.length === 0 ? (\n <Text dimColor>(no output)</Text>\n ) : (\n visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>{line.text}</Text>\n ))\n )}\n </Box>\n\n {/* Footer */}\n <Text dimColor>\n Lines {currentLine}-{endLine} of {totalLines} | j/k scroll | Tab/⇧Tab page | {isMac ? '⌥↑/↓' : 'g/G'} top/bottom | ↵/q exit\n </Text>\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","useEffect","isMac","process","platform","ENTER_ALT_SCREEN","EXIT_ALT_SCREEN","CLEAR_SCREEN","CURSOR_HOME","HIDE_CURSOR","FullscreenOverlay","title","lines","scrollOffset","stdout","terminalHeight","rows","headerLines","footerLines","maxVisible","Math","max","write","visibleLines","slice","totalLines","length","currentLine","endLine","min","flexDirection","height","bold","color","dimColor","repeat","columns","flexGrow","map","line","i","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,EAAEC,SAAS,QAAQ,QAAQ;AAGxC,MAAMC,QAAQC,QAAQC,QAAQ,KAAK;AAEnC,gDAAgD;AAChD,MAAMC,mBAAmB;AACzB,MAAMC,kBAAkB;AACxB,MAAMC,eAAe;AACrB,MAAMC,cAAc;AACpB,MAAMC,cAAc;AASpB,6BAAeT,KAAK,SAASU,kBAAkB,EAAEC,KAAK,EAAEC,KAAK,EAAEC,YAAY,EAAS;IAClF,MAAM,EAAEC,MAAM,EAAE,GAAGf;IACnB,MAAMgB,iBAAiBD,CAAAA,mBAAAA,6BAAAA,OAAQE,IAAI,KAAI;IAEvC,sEAAsE;IACtE,MAAMC,cAAc;IACpB,MAAMC,cAAc;IACpB,MAAMC,aAAaC,KAAKC,GAAG,CAAC,GAAGN,iBAAiBE,cAAcC;IAE9D,mDAAmD;IACnDjB,UAAU;QACR,IAAIa,QAAQ;YACVA,OAAOQ,KAAK,CAACjB,mBAAmBE,eAAeC,cAAcC;QAC/D;QACA,OAAO;YACL,IAAIK,QAAQ;gBACV,iEAAiE;gBACjE,uDAAuD;gBACvDA,OAAOQ,KAAK,CAAChB,kBAAkBC,eAAeC,cAAcC;YAC9D;QACF;IACF,GAAG;QAACK;KAAO;IAEX,MAAMS,eAAeX,MAAMY,KAAK,CAACX,cAAcA,eAAeM;IAC9D,MAAMM,aAAab,MAAMc,MAAM;IAC/B,MAAMC,cAAcd,eAAe;IACnC,MAAMe,UAAUR,KAAKS,GAAG,CAAChB,eAAeM,YAAYM;IAEpD,qBACE,MAAC5B;QAAIiC,eAAc;QAASC,QAAQhB;;0BAElC,KAACjB;gBAAKkC,IAAI;gBAACC,OAAM;0BACdtB;;0BAEH,KAACb;gBAAKoC,QAAQ;0BAAE,IAAIC,MAAM,CAACf,KAAKS,GAAG,CAAC,IAAIf,CAAAA,mBAAAA,6BAAAA,OAAQsB,OAAO,KAAI;;0BAG3D,KAACvC;gBAAIiC,eAAc;gBAASO,UAAU;0BACnCzB,MAAMc,MAAM,KAAK,kBAChB,KAAC5B;oBAAKoC,QAAQ;8BAAC;qBAEfX,aAAae,GAAG,CAAC,CAACC,MAAMC,IACtB,iHAAiH;kCACjH,KAAC1C;kCAA6ByC,KAAKE,IAAI;uBAA5B5B,eAAe2B;;0BAMhC,MAAC1C;gBAAKoC,QAAQ;;oBAAC;oBACNP;oBAAY;oBAAEC;oBAAQ;oBAAKH;oBAAW;oBAAiCvB,QAAQ,SAAS;oBAAM;;;;;AAI7G,GAAG"}
1
+ {"version":3,"sources":["/Users/kevin/Dev/OpenSource/node/spawn-term/src/components/FullscreenOverlay.tsx"],"sourcesContent":["import { Box, Text, useStdout } from 'ink';\nimport { memo, useLayoutEffect, useRef } from 'react';\nimport type { Line } from '../types.ts';\n\nconst isMac = process.platform === 'darwin';\n\n// ANSI escape codes for alternate screen buffer\nconst ENTER_ALT_SCREEN = '\\x1b[?1049h';\nconst EXIT_ALT_SCREEN = '\\x1b[?1049l';\nconst CLEAR_SCREEN = '\\x1b[2J';\nconst CURSOR_HOME = '\\x1b[H';\nconst HIDE_CURSOR = '\\x1b[?25l';\n\ntype Props = {\n title: string;\n lines: Line[];\n scrollOffset: number;\n onExit: () => void;\n};\n\nexport default memo(function FullscreenOverlay({ title, lines, scrollOffset }: Props) {\n const { stdout } = useStdout();\n const terminalHeight = stdout?.rows || 24;\n const enteredRef = useRef(false);\n\n // Reserve lines for header (title + divider) and footer (scroll hint)\n const headerLines = 2;\n const footerLines = 1;\n const maxVisible = Math.max(1, terminalHeight - headerLines - footerLines);\n\n // Enter alternate screen SYNCHRONOUSLY on first render, before ink outputs anything\n if (!enteredRef.current && stdout) {\n stdout.write(ENTER_ALT_SCREEN + CLEAR_SCREEN + CURSOR_HOME + HIDE_CURSOR);\n enteredRef.current = true;\n }\n\n // Exit alternate screen on unmount\n useLayoutEffect(() => {\n return () => {\n if (stdout && enteredRef.current) {\n stdout.write(EXIT_ALT_SCREEN);\n enteredRef.current = false;\n }\n };\n }, [stdout]);\n\n const visibleLines = lines.slice(scrollOffset, scrollOffset + maxVisible);\n const totalLines = lines.length;\n const currentLine = scrollOffset + 1;\n const endLine = Math.min(scrollOffset + maxVisible, totalLines);\n\n return (\n <Box flexDirection=\"column\" height={terminalHeight}>\n {/* Header */}\n <Text bold color=\"cyan\">\n {title}\n </Text>\n <Text dimColor>{'─'.repeat(Math.min(80, stdout?.columns || 80))}</Text>\n\n {/* Content */}\n <Box flexDirection=\"column\" flexGrow={1}>\n {lines.length === 0 ? (\n <Text dimColor>(no output)</Text>\n ) : (\n visibleLines.map((line, i) => (\n // biome-ignore lint/suspicious/noArrayIndexKey: Lines have no unique ID, index is stable for this scrolling view\n <Text key={scrollOffset + i}>{line.text}</Text>\n ))\n )}\n </Box>\n\n {/* Footer */}\n <Text dimColor>\n Lines {currentLine}-{endLine} of {totalLines} | j/k scroll | Tab/⇧Tab page | {isMac ? '⌥↑/↓' : 'g/G'} top/bottom | ↵/q exit\n </Text>\n </Box>\n );\n});\n"],"names":["Box","Text","useStdout","memo","useLayoutEffect","useRef","isMac","process","platform","ENTER_ALT_SCREEN","EXIT_ALT_SCREEN","CLEAR_SCREEN","CURSOR_HOME","HIDE_CURSOR","FullscreenOverlay","title","lines","scrollOffset","stdout","terminalHeight","rows","enteredRef","headerLines","footerLines","maxVisible","Math","max","current","write","visibleLines","slice","totalLines","length","currentLine","endLine","min","flexDirection","height","bold","color","dimColor","repeat","columns","flexGrow","map","line","i","text"],"mappings":";AAAA,SAASA,GAAG,EAAEC,IAAI,EAAEC,SAAS,QAAQ,MAAM;AAC3C,SAASC,IAAI,EAAEC,eAAe,EAAEC,MAAM,QAAQ,QAAQ;AAGtD,MAAMC,QAAQC,QAAQC,QAAQ,KAAK;AAEnC,gDAAgD;AAChD,MAAMC,mBAAmB;AACzB,MAAMC,kBAAkB;AACxB,MAAMC,eAAe;AACrB,MAAMC,cAAc;AACpB,MAAMC,cAAc;AASpB,6BAAeV,KAAK,SAASW,kBAAkB,EAAEC,KAAK,EAAEC,KAAK,EAAEC,YAAY,EAAS;IAClF,MAAM,EAAEC,MAAM,EAAE,GAAGhB;IACnB,MAAMiB,iBAAiBD,CAAAA,mBAAAA,6BAAAA,OAAQE,IAAI,KAAI;IACvC,MAAMC,aAAahB,OAAO;IAE1B,sEAAsE;IACtE,MAAMiB,cAAc;IACpB,MAAMC,cAAc;IACpB,MAAMC,aAAaC,KAAKC,GAAG,CAAC,GAAGP,iBAAiBG,cAAcC;IAE9D,oFAAoF;IACpF,IAAI,CAACF,WAAWM,OAAO,IAAIT,QAAQ;QACjCA,OAAOU,KAAK,CAACnB,mBAAmBE,eAAeC,cAAcC;QAC7DQ,WAAWM,OAAO,GAAG;IACvB;IAEA,mCAAmC;IACnCvB,gBAAgB;QACd,OAAO;YACL,IAAIc,UAAUG,WAAWM,OAAO,EAAE;gBAChCT,OAAOU,KAAK,CAAClB;gBACbW,WAAWM,OAAO,GAAG;YACvB;QACF;IACF,GAAG;QAACT;KAAO;IAEX,MAAMW,eAAeb,MAAMc,KAAK,CAACb,cAAcA,eAAeO;IAC9D,MAAMO,aAAaf,MAAMgB,MAAM;IAC/B,MAAMC,cAAchB,eAAe;IACnC,MAAMiB,UAAUT,KAAKU,GAAG,CAAClB,eAAeO,YAAYO;IAEpD,qBACE,MAAC/B;QAAIoC,eAAc;QAASC,QAAQlB;;0BAElC,KAAClB;gBAAKqC,IAAI;gBAACC,OAAM;0BACdxB;;0BAEH,KAACd;gBAAKuC,QAAQ;0BAAE,IAAIC,MAAM,CAAChB,KAAKU,GAAG,CAAC,IAAIjB,CAAAA,mBAAAA,6BAAAA,OAAQwB,OAAO,KAAI;;0BAG3D,KAAC1C;gBAAIoC,eAAc;gBAASO,UAAU;0BACnC3B,MAAMgB,MAAM,KAAK,kBAChB,KAAC/B;oBAAKuC,QAAQ;8BAAC;qBAEfX,aAAae,GAAG,CAAC,CAACC,MAAMC,IACtB,iHAAiH;kCACjH,KAAC7C;kCAA6B4C,KAAKE,IAAI;uBAA5B9B,eAAe6B;;0BAMhC,MAAC7C;gBAAKuC,QAAQ;;oBAAC;oBACNP;oBAAY;oBAAEC;oBAAQ;oBAAKH;oBAAW;oBAAiCzB,QAAQ,SAAS;oBAAM;;;;;AAI7G,GAAG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "spawn-term",
3
- "version": "3.2.3",
3
+ "version": "3.2.5",
4
4
  "description": "Formats spawn with for terminal grouping",
5
5
  "keywords": [
6
6
  "spawn",