storymode-cli 1.3.2 → 1.3.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.mjs +41 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storymode-cli",
3
- "version": "1.3.2",
3
+ "version": "1.3.4",
4
4
  "description": "Play AI-animated pixel art characters in your terminal",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.mjs CHANGED
@@ -199,12 +199,11 @@ export async function run(args) {
199
199
  const reactive = !flags['no-reactive'];
200
200
 
201
201
  // Auto-detect graphics protocol for HD rendering
202
- // iTerm2 inside tmux can't clear previous inline images (no equivalent of
203
- // Kitty's a=d,d=a — \x1b[2J is intercepted by tmux, not forwarded to iTerm2).
204
- // So HD only works for: Kitty/Ghostty (any context) or iTerm2 (direct, no tmux).
202
+ // iTerm2 inside tmux can't clear previous inline images (Kitty has a=d,d=a
203
+ // but iTerm2's \x1b[2J is intercepted by tmux). So for iTerm2 we use native
204
+ // splits (AppleScript) instead of tmux, keeping the companion process direct.
205
205
  const renderMode = detectGraphicsProtocol();
206
- const useHd = renderMode.protocol === 'kitty'
207
- || (renderMode.protocol === 'iterm2' && !renderMode.inTmux);
206
+ const useHd = !!renderMode.protocol;
208
207
  const paneWidth = useHd && size === 'full' ? 80 : size === 'full' ? 62 : size === 'tiny' ? 14 : 22;
209
208
 
210
209
  // If we're already the companion pane (spawned by tmux split), play inline
@@ -241,21 +240,50 @@ export async function run(args) {
241
240
  installHooks({ global: true });
242
241
  }
243
242
 
244
- // Open tmux side pane — install tmux if needed
245
- if (!await ensureTmux()) process.exit(1);
246
- const inTmux = !!process.env.TMUX;
243
+ // Build companion command flags
247
244
  const sextantFlag = flags.sextant ? ' --sextant' : '';
248
245
  const reactiveFlag = reactive ? ' --reactive' : '';
249
246
  const noAiFlag = noAi ? ' --no-ai' : '';
250
247
  const modelFlag = model ? ` --model=${model}` : '';
251
- // Enable tmux passthrough for graphics protocols when HD is active
252
- const passthroughPrefix = useHd ? 'tmux set allow-passthrough on; ' : '';
253
- const companionCmd = `${passthroughPrefix}STORYMODE_COMPANION=1 npx storymode-cli play ${id} --size=${size}${sextantFlag}${reactiveFlag}${noAiFlag}${modelFlag}`;
248
+ const companionCmd = `STORYMODE_COMPANION=1 npx --yes storymode-cli play ${id} --size=${size}${sextantFlag}${reactiveFlag}${noAiFlag}${modelFlag}`;
249
+
250
+ // iTerm2 (not in tmux): use native split to keep HD working.
251
+ // iTerm2 inline images can't be cleared through tmux passthrough,
252
+ // so we avoid tmux entirely and use AppleScript to split the pane.
253
+ if (renderMode.protocol === 'iterm2' && !renderMode.inTmux) {
254
+ try {
255
+ // Wrap command so errors stay visible (iTerm2 closes pane on exit)
256
+ const wrappedCmd = `${companionCmd} || { echo ""; echo " Companion crashed. Press enter to close."; read; }`;
257
+ const escapedCmd = wrappedCmd.replace(/"/g, '\\"');
258
+ execSync(`osascript -e '
259
+ tell application "iTerm2"
260
+ tell current session of current tab of current window
261
+ set newSession to split vertically with default profile command "${escapedCmd}"
262
+ end tell
263
+ end tell
264
+ '`, { stdio: 'ignore' });
265
+ process.stderr.write(` Companion opened in iTerm2 pane (${size} HD/${renderMode.protocol}).\n`);
266
+ process.stderr.write(' Close the pane or press q in it to stop.\n');
267
+ } catch (err) {
268
+ console.error(` iTerm2 split failed: ${err.message}`);
269
+ console.error(' Falling back to tmux...');
270
+ // Fall through to tmux path below
271
+ renderMode.protocol = null;
272
+ }
273
+ if (renderMode.protocol) break;
274
+ }
275
+
276
+ // Open tmux side pane — install tmux if needed
277
+ if (!await ensureTmux()) process.exit(1);
278
+ const inTmux = !!process.env.TMUX;
279
+ // Enable tmux passthrough for Kitty/Ghostty graphics protocols
280
+ const passthroughPrefix = renderMode.protocol === 'kitty' ? 'tmux set allow-passthrough on; ' : '';
281
+ const tmuxCompanionCmd = `${passthroughPrefix}${companionCmd}`;
254
282
 
255
283
  try {
256
284
  if (inTmux) {
257
285
  execSync(
258
- `tmux split-window -h -l ${paneWidth} '${companionCmd}'`,
286
+ `tmux split-window -h -l ${paneWidth} '${tmuxCompanionCmd}'`,
259
287
  { stdio: 'ignore' }
260
288
  );
261
289
  if (!detach) {
@@ -280,7 +308,7 @@ export async function run(args) {
280
308
  process.stderr.write(' Starting tmux session with companion pane...\n');
281
309
  execSync(
282
310
  `tmux new-session -d -s storymode -x 100 -y 30 \\; ` +
283
- `split-window -h -l ${paneWidth} '${companionCmd}' \\; ` +
311
+ `split-window -h -l ${paneWidth} '${tmuxCompanionCmd}' \\; ` +
284
312
  `select-pane -L`,
285
313
  { stdio: 'ignore' }
286
314
  );