picosh 0.1.6 → 0.2.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.
package/index.js CHANGED
@@ -6,6 +6,8 @@ const os = require('os');
6
6
  const https = require('https');
7
7
  const http = require('http');
8
8
 
9
+ // ─── image paste ────────────────────────────────────────────────────────────
10
+
9
11
  const SAVE_DIR = path.join(os.tmpdir(), 'picosh');
10
12
  const SAVE_PATH = path.join(SAVE_DIR, 'clip_latest.png');
11
13
 
@@ -21,34 +23,110 @@ function hasImage(clipboard) {
21
23
 
22
24
  function saveBitmap(clipboard) {
23
25
  fs.mkdirSync(SAVE_DIR, {recursive: true});
24
- const filepath = SAVE_PATH;
25
- fs.writeFileSync(filepath, clipboard.readImage().toPNG());
26
- return Promise.resolve(filepath);
26
+ fs.writeFileSync(SAVE_PATH, clipboard.readImage().toPNG());
27
+ return Promise.resolve(SAVE_PATH);
27
28
  }
28
29
 
29
30
  function downloadImage(url) {
30
31
  return new Promise((resolve) => {
31
32
  fs.mkdirSync(SAVE_DIR, {recursive: true});
32
- const filepath = SAVE_PATH;
33
- const file = fs.createWriteStream(filepath);
33
+ const file = fs.createWriteStream(SAVE_PATH);
34
34
  const client = url.startsWith('https') ? https : http;
35
-
36
35
  client.get(url, (res) => {
37
36
  res.pipe(file);
38
- file.on('finish', () => file.close(() => resolve(filepath)));
37
+ file.on('finish', () => file.close(() => resolve(SAVE_PATH)));
39
38
  }).on('error', () => {
40
- fs.unlink(filepath, () => {});
39
+ fs.unlink(SAVE_PATH, () => {});
41
40
  resolve(null);
42
41
  });
43
42
  });
44
43
  }
45
44
 
45
+ // ─── AI waiting indicator ───────────────────────────────────────────────────
46
+
47
+ // ANSIエスケープを除去してプロンプト判定
48
+ const ANSI_RE = /\x1b\[[0-9;]*[mGKHF]/g;
49
+ // Claude Code / 一般的なAI CLIのプロンプトパターン
50
+ const PROMPT_RE = />\s*$|❯\s*$/;
51
+
52
+ const timers = {};
53
+
54
+ exports.middleware = (store) => (next) => (action) => {
55
+ if (action.type === 'SESSION_ADD_DATA') {
56
+ const {uid, data} = action;
57
+ const clean = data.replace(ANSI_RE, '');
58
+
59
+ clearTimeout(timers[uid]);
60
+ // 待機中フラグを一旦解除
61
+ store.dispatch({type: 'PICOSH_AI_WAITING', uid, waiting: false});
62
+
63
+ timers[uid] = setTimeout(() => {
64
+ if (PROMPT_RE.test(clean)) {
65
+ store.dispatch({type: 'PICOSH_AI_WAITING', uid, waiting: true});
66
+ }
67
+ }, 500);
68
+ }
69
+
70
+ if (action.type === 'SESSION_PTY_DATA') {
71
+ const {uid} = action;
72
+ clearTimeout(timers[uid]);
73
+ store.dispatch({type: 'PICOSH_AI_WAITING', uid, waiting: false});
74
+ }
75
+
76
+ return next(action);
77
+ };
78
+
79
+ exports.reduceUI = (state, action) => {
80
+ if (action.type === 'PICOSH_AI_WAITING') {
81
+ const waiting = state.get('picoshWaiting') || {};
82
+ return state.set('picoshWaiting', {...waiting, [action.uid]: action.waiting});
83
+ }
84
+ return state;
85
+ };
86
+
87
+ exports.mapTermsState = (state, map) => {
88
+ return Object.assign(map, {picoshWaiting: state.ui.get('picoshWaiting') || {}});
89
+ };
90
+
91
+ exports.getTabsProps = (parentProps, props) => {
92
+ return Object.assign(props, {picoshWaiting: parentProps.picoshWaiting});
93
+ };
94
+
95
+ exports.getTabProps = (tab, parentProps, props) => {
96
+ return Object.assign(props, {
97
+ isAiWaiting: !!(parentProps.picoshWaiting && parentProps.picoshWaiting[props.uid]),
98
+ });
99
+ };
100
+
101
+ exports.decorateTab = (Tab, {React}) => {
102
+ return function PicoshTab(props) {
103
+ const style = props.isAiWaiting ? {
104
+ boxShadow: '0 0 8px 2px #4a9eff',
105
+ borderRadius: '4px',
106
+ animation: 'picosh-glow 1.5s ease-in-out infinite',
107
+ } : {};
108
+
109
+ return React.createElement(
110
+ 'div',
111
+ {style: {position: 'relative', display: 'contents'}},
112
+ React.createElement(Tab, props),
113
+ props.isAiWaiting && React.createElement('style', {key: 'glow-style'}, `
114
+ @keyframes picosh-glow {
115
+ 0%, 100% { opacity: 1; }
116
+ 50% { opacity: 0.6; }
117
+ }
118
+ `),
119
+ );
120
+ };
121
+ };
122
+
123
+ // ─── term: image paste + glow wrapper ───────────────────────────────────────
124
+
46
125
  exports.decorateTerm = (Term, {React}) => {
47
126
  return class extends React.Component {
48
127
  componentDidMount() {
49
128
  this._onKeyDown = (e) => {
50
129
  if (!((e.ctrlKey || e.metaKey) && e.key === 'v')) return;
51
-
52
130
  try {
53
131
  const {clipboard} = require('electron');
54
132
  const found = hasImage(clipboard);
@@ -57,18 +135,12 @@ exports.decorateTerm = (Term, {React}) => {
57
135
  e.preventDefault();
58
136
  e.stopPropagation();
59
137
 
60
- const save = found.type === 'bitmap'
61
- ? saveBitmap(clipboard)
62
- : downloadImage(found.src);
63
-
138
+ const save = found.type === 'bitmap' ? saveBitmap(clipboard) : downloadImage(found.src);
64
139
  save.then((filepath) => {
65
- if (filepath && this.props.onData) {
66
- this.props.onData(filepath);
67
- }
140
+ if (filepath && this.props.onData) this.props.onData(filepath);
68
141
  });
69
142
  } catch (_) {}
70
143
  };
71
-
72
144
  document.addEventListener('keydown', this._onKeyDown, true);
73
145
  }
74
146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "picosh",
3
- "version": "0.1.6",
3
+ "version": "0.2.0",
4
4
  "description": "Hyper plugin: paste clipboard images as file paths",
5
5
  "main": "index.js",
6
6
  "license": "MIT",
@@ -11,6 +11,11 @@ const HYPER_CLI_PATHS = [
11
11
  '/usr/local/bin/hyper',
12
12
  ];
13
13
 
14
+ const HYPER_CONFIG_PATHS = [
15
+ path.join(os.homedir(), 'AppData', 'Roaming', 'Hyper', 'config.json'),
16
+ path.join(os.homedir(), '.hyper.js'),
17
+ ];
18
+
14
19
  function findHyperCli() {
15
20
  return HYPER_CLI_PATHS.find((p) => fs.existsSync(p));
16
21
  }
@@ -42,8 +47,11 @@ if (!cli) {
42
47
  cli = findHyperCli();
43
48
  }
44
49
 
45
- if (!cli) {
46
- console.error('[picosh] Hyper installed. Run `hyper i picosh` to activate the plugin.');
50
+ const configExists = HYPER_CONFIG_PATHS.some((p) => fs.existsSync(p));
51
+
52
+ if (!cli || !configExists) {
53
+ console.error('[picosh] Hyper installed!');
54
+ console.error('[picosh] Launch Hyper once to initialize, then run: hyper i picosh');
47
55
  process.exit(0);
48
56
  }
49
57