@make-u-free/migi 0.4.2 → 0.4.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/bin/migi.js +32 -28
  2. package/package.json +1 -1
package/bin/migi.js CHANGED
@@ -86,44 +86,48 @@ async function readChatInput() {
86
86
  const lines = ['']
87
87
  let curLine = 0
88
88
  let drawnLines = 0
89
- let lastLineCount = 0
89
+ let cursorLine = 0 // カーソルの物理行(drawn area 先頭からの offset)
90
90
 
91
91
  emitKeypressEvents(process.stdin)
92
92
  if (process.stdin.isTTY) process.stdin.setRawMode(true)
93
93
 
94
94
  function draw() {
95
95
  const w = process.stdout.columns || 80
96
-
97
- // 行数が変わっていない(通常の文字入力)→ 現在行だけ上書き。セパレーター・ガイドは触らない
98
- if (lines.length === lastLineCount && drawnLines > 0) {
99
- const prefix = curLine === 0 ? PFIRST : PCONT
100
- process.stdout.write('\r\x1b[K' + chalk.cyan(prefix) + lines[curLine])
101
- return
102
- }
103
-
104
- // 行数変化 or 初回 → 全体を再描画
105
- lastLineCount = lines.length
106
- const allLines = [
96
+ const newLines = [
107
97
  ...lines.map((l, i) => chalk.cyan(i === 0 ? PFIRST : PCONT) + l),
108
- chalk.dim('─'.repeat(w)),
109
- chalk.dim(` ✦ ${model} · Shift+Enterで改行 / Enterで送信`)
98
+ chalk.dim('─'.repeat(w - 1)), // w-1: 行末での自動折り返し防止
99
+ chalk.dim(` ✦ ${model} · Alt+Enterで改行 / Enterで送信`)
110
100
  ]
101
+ const oldDrawnLines = drawnLines
102
+ drawnLines = newLines.length
111
103
 
112
104
  let buf = ''
113
- if (drawnLines > 0) {
114
- buf += `\x1b[${drawnLines}A\x1b[1G`
115
- for (let i = 0; i < drawnLines; i++) buf += '\x1b[2K\n'
116
- buf += `\x1b[${drawnLines}A\x1b[1G`
105
+
106
+ // drawn area 先頭まで戻る(cursorLine = カーソルが今いる物理行)
107
+ if (cursorLine > 0) buf += `\x1b[${cursorLine}A`
108
+ buf += '\r'
109
+
110
+ // ② 各行を上書き。「先クリア→描画」ではなく「描画→行末クリア」でちらつき防止
111
+ for (let i = 0; i < newLines.length; i++) {
112
+ buf += newLines[i] + '\x1b[K'
113
+ if (i < newLines.length - 1) buf += '\r\n'
114
+ }
115
+
116
+ // ③ 行数が減った場合、余分な古い行をクリア
117
+ for (let i = newLines.length; i < oldDrawnLines; i++) {
118
+ buf += '\r\n\x1b[2K'
117
119
  }
118
120
 
119
- drawnLines = allLines.length
120
- buf += allLines.join('\n') + '\n'
121
+ // curLine の行まで戻る
122
+ const linesFromBottom = drawnLines - 1 - curLine
123
+ if (linesFromBottom > 0) buf += `\x1b[${linesFromBottom}A`
124
+ buf += '\r'
121
125
 
122
- const linesBelow = drawnLines - curLine
123
- if (linesBelow > 0) buf += `\x1b[${linesBelow}A`
124
- const col = (curLine === 0 ? PFIRST : PCONT).length + lines[curLine].length + 1
125
- buf += `\x1b[${col}G`
126
+ // カーソルを入力内容の末尾へ
127
+ const prefix = curLine === 0 ? PFIRST : PCONT
128
+ buf += `\x1b[${prefix.length + lines[curLine].length + 1}G`
126
129
 
130
+ cursorLine = curLine
127
131
  process.stdout.write(buf)
128
132
  }
129
133
 
@@ -136,7 +140,7 @@ async function readChatInput() {
136
140
  }
137
141
 
138
142
  if (key.ctrl && key.name === 'c') {
139
- process.stdout.write(`\x1b[${drawnLines - curLine}B\n`)
143
+ process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
140
144
  process.stdin.removeListener('keypress', onKey)
141
145
  if (process.stdin.isTTY) process.stdin.setRawMode(false)
142
146
  console.log(chalk.cyan('\n お疲れ様でした!またね。\n'))
@@ -144,8 +148,8 @@ async function readChatInput() {
144
148
  }
145
149
 
146
150
  if (key.name === 'return') {
147
- if (key.shift) {
148
- // Shift+Enter 改行
151
+ // Alt+Enter(macOS: Option+Enter)または Shift+Enter → 改行
152
+ if (key.meta || key.shift) {
149
153
  lines.splice(curLine + 1, 0, '')
150
154
  curLine++
151
155
  draw()
@@ -153,7 +157,7 @@ async function readChatInput() {
153
157
  // Enter → 送信
154
158
  const content = lines.join('\n').trim()
155
159
  if (!content) return
156
- process.stdout.write(`\x1b[${drawnLines - curLine}B\n`)
160
+ process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
157
161
  process.stdin.removeListener('keypress', onKey)
158
162
  if (process.stdin.isTTY) process.stdin.setRawMode(false)
159
163
  resolve(content)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@make-u-free/migi",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Your AI right-hand agent. Works anywhere, with any LLM API.",
5
5
  "type": "module",
6
6
  "bin": {