@pokit/tabs-ink 0.0.31 → 0.0.32

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.
@@ -0,0 +1,18 @@
1
+ export type CopyModeState = {
2
+ active: boolean;
3
+ cursorLine: number;
4
+ cursorCol: number;
5
+ selectStart: {
6
+ line: number;
7
+ col: number;
8
+ } | null;
9
+ };
10
+ export declare function useCopyMode(lines: string[], viewHeight: number, scrollOffset: number): {
11
+ state: CopyModeState;
12
+ enterCopyMode: () => void;
13
+ exitCopyMode: () => void;
14
+ navigate: (direction: "up" | "down" | "left" | "right") => void;
15
+ toggleSelect: () => Promise<void>;
16
+ getSelectedText: () => string | null;
17
+ };
18
+ //# sourceMappingURL=use-copy-mode.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-copy-mode.d.ts","sourceRoot":"","sources":["../src/use-copy-mode.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,aAAa,GAAG;IAC1B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;CACnD,CAAC;AAEF,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,UAAU,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM;;;;0BA2BrE,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO;;2BAoCN,MAAM,GAAG,IAAI;EAsFtD"}
@@ -0,0 +1,138 @@
1
+ import { useState, useCallback } from 'react';
2
+ import { spawn } from 'node:child_process';
3
+ export function useCopyMode(lines, viewHeight, scrollOffset) {
4
+ const [state, setState] = useState({
5
+ active: false,
6
+ cursorLine: 0,
7
+ cursorCol: 0,
8
+ selectStart: null,
9
+ });
10
+ const enterCopyMode = useCallback(() => {
11
+ setState({
12
+ active: true,
13
+ cursorLine: 0,
14
+ cursorCol: 0,
15
+ selectStart: null,
16
+ });
17
+ }, []);
18
+ const exitCopyMode = useCallback(() => {
19
+ setState({
20
+ active: false,
21
+ cursorLine: 0,
22
+ cursorCol: 0,
23
+ selectStart: null,
24
+ });
25
+ }, []);
26
+ const navigate = useCallback((direction) => {
27
+ setState((prev) => {
28
+ if (!prev.active)
29
+ return prev;
30
+ let { cursorLine, cursorCol } = prev;
31
+ const visibleStart = scrollOffset;
32
+ const visibleEnd = Math.min(scrollOffset + viewHeight, lines.length);
33
+ const maxLine = lines.length - 1;
34
+ switch (direction) {
35
+ case 'up':
36
+ cursorLine = Math.max(0, cursorLine - 1);
37
+ break;
38
+ case 'down':
39
+ cursorLine = Math.min(maxLine, cursorLine + 1);
40
+ break;
41
+ case 'left':
42
+ cursorCol = Math.max(0, cursorCol - 1);
43
+ break;
44
+ case 'right': {
45
+ const currentLine = lines[cursorLine] || '';
46
+ cursorCol = Math.min(currentLine.length, cursorCol + 1);
47
+ break;
48
+ }
49
+ }
50
+ // Clamp column to current line length
51
+ const currentLine = lines[cursorLine] || '';
52
+ cursorCol = Math.min(cursorCol, currentLine.length);
53
+ return { ...prev, cursorLine, cursorCol };
54
+ });
55
+ }, [lines, scrollOffset, viewHeight]);
56
+ const getSelectedText = useCallback(() => {
57
+ if (!state.selectStart)
58
+ return null;
59
+ const start = state.selectStart;
60
+ const end = { line: state.cursorLine, col: state.cursorCol };
61
+ // Normalize selection (ensure start is before end)
62
+ let startLine = Math.min(start.line, end.line);
63
+ let endLine = Math.max(start.line, end.line);
64
+ let startCol = start.line < end.line ? start.col : Math.min(start.col, end.col);
65
+ let endCol = start.line < end.line ? end.col : Math.max(start.col, end.col);
66
+ if (startLine === endLine) {
67
+ startCol = Math.min(start.col, end.col);
68
+ endCol = Math.max(start.col, end.col);
69
+ }
70
+ const selectedLines = [];
71
+ for (let i = startLine; i <= endLine; i++) {
72
+ const line = lines[i] || '';
73
+ if (i === startLine && i === endLine) {
74
+ selectedLines.push(line.slice(startCol, endCol));
75
+ }
76
+ else if (i === startLine) {
77
+ selectedLines.push(line.slice(startCol));
78
+ }
79
+ else if (i === endLine) {
80
+ selectedLines.push(line.slice(0, endCol));
81
+ }
82
+ else {
83
+ selectedLines.push(line);
84
+ }
85
+ }
86
+ return selectedLines.join('\n');
87
+ }, [state, lines]);
88
+ const copyToClipboard = useCallback(async (text) => {
89
+ try {
90
+ // Use pbcopy on macOS, xclip/xsel on Linux
91
+ let copyCommand = 'pbcopy';
92
+ if (process.platform === 'linux') {
93
+ copyCommand = 'xclip -selection clipboard || xsel --clipboard --input';
94
+ }
95
+ else if (process.platform === 'win32') {
96
+ copyCommand = 'clip';
97
+ }
98
+ const proc = spawn('sh', ['-c', copyCommand], {
99
+ stdio: ['pipe', 'ignore', 'ignore'],
100
+ });
101
+ proc.stdin?.write(text);
102
+ proc.stdin?.end();
103
+ await new Promise((resolve) => {
104
+ proc.on('close', resolve);
105
+ });
106
+ }
107
+ catch (error) {
108
+ console.error('Failed to copy to clipboard:', error);
109
+ }
110
+ }, []);
111
+ const toggleSelect = useCallback(async () => {
112
+ const hasSelection = state.selectStart !== null;
113
+ if (!hasSelection) {
114
+ // Start selection
115
+ setState((prev) => {
116
+ if (!prev.active)
117
+ return prev;
118
+ return { ...prev, selectStart: { line: prev.cursorLine, col: prev.cursorCol } };
119
+ });
120
+ }
121
+ else {
122
+ // End selection - copy and exit
123
+ const text = getSelectedText();
124
+ if (text) {
125
+ await copyToClipboard(text);
126
+ }
127
+ exitCopyMode();
128
+ }
129
+ }, [state.selectStart, getSelectedText, copyToClipboard, exitCopyMode]);
130
+ return {
131
+ state,
132
+ enterCopyMode,
133
+ exitCopyMode,
134
+ navigate,
135
+ toggleSelect,
136
+ getSelectedText,
137
+ };
138
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pokit/tabs-ink",
3
- "version": "0.0.31",
3
+ "version": "0.0.32",
4
4
  "description": "Ink-based tab renderer for pok CLI applications",
5
5
  "keywords": [
6
6
  "cli",
@@ -51,12 +51,12 @@
51
51
  "devDependencies": {
52
52
  "@types/bun": "latest",
53
53
  "@types/react": "^19.2.0",
54
- "@pokit/core": "0.0.31",
55
- "@pokit/tabs-core": "0.0.31"
54
+ "@pokit/core": "0.0.32",
55
+ "@pokit/tabs-core": "0.0.32"
56
56
  },
57
57
  "peerDependencies": {
58
- "@pokit/core": "0.0.31",
59
- "@pokit/tabs-core": "0.0.31"
58
+ "@pokit/core": "0.0.32",
59
+ "@pokit/tabs-core": "0.0.32"
60
60
  },
61
61
  "engines": {
62
62
  "bun": ">=1.0.0"