@vitorcen/context-resume 1.0.2 → 1.0.3

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.
Files changed (2) hide show
  1. package/dist/ui/app.js +37 -13
  2. package/package.json +1 -1
package/dist/ui/app.js CHANGED
@@ -3,6 +3,22 @@ import { useState, useEffect } from 'react';
3
3
  import { Box, Text, useInput, useApp } from 'ink';
4
4
  import SelectInput from 'ink-select-input';
5
5
  import { getClaudeSessions, getCodexSessions } from '../adapters/index.js';
6
+ import stringWidth from 'string-width';
7
+ // Truncate by display width (handles CJK characters correctly)
8
+ const truncateByWidth = (str, maxWidth) => {
9
+ if (stringWidth(str) <= maxWidth)
10
+ return str;
11
+ let result = '';
12
+ let width = 0;
13
+ for (const char of str) {
14
+ const charWidth = stringWidth(char);
15
+ if (width + charWidth > maxWidth)
16
+ break;
17
+ result += char;
18
+ width += charWidth;
19
+ }
20
+ return result + '...';
21
+ };
6
22
  const App = ({ cwd, limit = 10, onSubmit }) => {
7
23
  const [claudeItems, setClaudeItems] = useState([]);
8
24
  const [codexItems, setCodexItems] = useState([]);
@@ -33,16 +49,24 @@ const App = ({ cwd, limit = 10, onSubmit }) => {
33
49
  ]);
34
50
  // Sort by timestamp desc
35
51
  const sortFn = (a, b) => b.timestamp - a.timestamp;
36
- const cItems = claudeSessions.sort(sortFn).map(s => ({
37
- label: `${s.title} (${new Date(s.timestamp).toLocaleDateString()})`,
38
- value: s.id,
39
- session: s
40
- }));
41
- const cxItems = codexSessions.sort(sortFn).map(s => ({
42
- label: `${s.title} (${new Date(s.timestamp).toLocaleDateString()})`,
43
- value: s.id,
44
- session: s
45
- }));
52
+ const cItems = claudeSessions.sort(sortFn).map(s => {
53
+ const title = s.title.replace(/\n/g, ' ').trim();
54
+ const truncatedTitle = truncateByWidth(title, 45);
55
+ return {
56
+ label: `${truncatedTitle} (${new Date(s.timestamp).toLocaleDateString()})`,
57
+ value: s.id,
58
+ session: s
59
+ };
60
+ });
61
+ const cxItems = codexSessions.sort(sortFn).map(s => {
62
+ const title = s.title.replace(/\n/g, ' ').trim();
63
+ const truncatedTitle = truncateByWidth(title, 45);
64
+ return {
65
+ label: `${truncatedTitle} (${new Date(s.timestamp).toLocaleDateString()})`,
66
+ value: s.id,
67
+ session: s
68
+ };
69
+ });
46
70
  setClaudeItems(cItems);
47
71
  setCodexItems(cxItems);
48
72
  if (cItems.length > 0)
@@ -66,12 +90,12 @@ const App = ({ cwd, limit = 10, onSubmit }) => {
66
90
  const currentItem = activePanel === 'claude' ? activeClaudeItem : activeCodexItem;
67
91
  // Filter out empty prompts
68
92
  const prompts = (currentItem?.session.userPrompts || []).filter(p => p && p.trim().length > 0);
69
- // Truncate prompts logic
93
+ // Truncate prompts logic (~60 display width: ~120 ASCII chars or ~60 CJK chars)
70
94
  const previewText = prompts.map((p, i) => {
71
95
  const clean = p.replace(/\n/g, ' ').trim();
72
- const truncated = clean.length > 50 ? clean.slice(0, 50) + '...' : clean;
96
+ const truncated = truncateByWidth(clean, 120);
73
97
  return `${i + 1}. ${truncated}`;
74
98
  }).join('\n');
75
- return (_jsxs(Box, { flexDirection: "column", height: 35, children: [_jsxs(Box, { height: 12, borderStyle: "single", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, underline: true, children: "Preview (User Prompts)" }), _jsx(Text, { children: previewText || 'Select a session to view prompts' })] }), _jsxs(Box, { flexDirection: "row", height: 20, children: [_jsxs(Box, { width: "50%", borderStyle: activePanel === 'claude' ? 'double' : 'single', flexDirection: "column", borderColor: activePanel === 'claude' ? 'green' : 'white', children: [_jsx(Text, { bold: true, underline: true, color: activePanel === 'claude' ? 'green' : 'white', children: "Claude Sessions" }), claudeItems.length === 0 ? (_jsx(Text, { children: "No sessions found." })) : (_jsx(SelectInput, { items: claudeItems, onSelect: handleSelect, onHighlight: handleHighlightClaude, isFocused: activePanel === 'claude' }))] }), _jsxs(Box, { width: "50%", borderStyle: activePanel === 'codex' ? 'double' : 'single', flexDirection: "column", borderColor: activePanel === 'codex' ? 'green' : 'white', children: [_jsx(Text, { bold: true, underline: true, color: activePanel === 'codex' ? 'green' : 'white', children: "Codex Sessions" }), codexItems.length === 0 ? (_jsx(Text, { children: "No sessions found." })) : (_jsx(SelectInput, { items: codexItems, onSelect: handleSelect, onHighlight: handleHighlightCodex, isFocused: activePanel === 'codex' }))] })] }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: "TAB/Arrows: Switch Panel | ENTER: Select | ESC: Exit" }) })] }));
99
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { borderStyle: "single", flexDirection: "column", paddingX: 1, children: [_jsx(Text, { bold: true, underline: true, children: "Preview (User Prompts)" }), _jsx(Text, { children: previewText || 'Select a session to view prompts' })] }), _jsxs(Box, { flexDirection: "row", minHeight: 20, children: [_jsxs(Box, { width: "50%", borderStyle: activePanel === 'claude' ? 'double' : 'single', flexDirection: "column", borderColor: activePanel === 'claude' ? 'green' : 'white', children: [_jsx(Text, { bold: true, underline: true, color: activePanel === 'claude' ? 'green' : 'white', children: "Claude Sessions" }), claudeItems.length === 0 ? (_jsx(Text, { children: "No sessions found." })) : (_jsx(SelectInput, { items: claudeItems, onSelect: handleSelect, onHighlight: handleHighlightClaude, isFocused: activePanel === 'claude' }))] }), _jsxs(Box, { width: "50%", borderStyle: activePanel === 'codex' ? 'double' : 'single', flexDirection: "column", borderColor: activePanel === 'codex' ? 'green' : 'white', children: [_jsx(Text, { bold: true, underline: true, color: activePanel === 'codex' ? 'green' : 'white', children: "Codex Sessions" }), codexItems.length === 0 ? (_jsx(Text, { children: "No sessions found." })) : (_jsx(SelectInput, { items: codexItems, onSelect: handleSelect, onHighlight: handleHighlightCodex, isFocused: activePanel === 'codex' }))] })] }), _jsx(Box, { marginTop: 0, children: _jsx(Text, { dimColor: true, children: "TAB/Arrows: Switch Panel | ENTER: Select | ESC: Exit" }) })] }));
76
100
  };
77
101
  export default App;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitorcen/context-resume",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "Context Resume CLI - Browse and mutually restore the conversation history of Claude Code and Codex",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",