@mobius-os/mobius 0.2.6 → 0.2.8
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/package.json +1 -1
- package/src/components/Chat.tsx +28 -12
- package/src/components/PrepScreen.tsx +12 -18
package/package.json
CHANGED
package/src/components/Chat.tsx
CHANGED
|
@@ -35,8 +35,7 @@ interface TerminalSize {
|
|
|
35
35
|
isTty: boolean
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
const VERSION = '0.2.
|
|
39
|
-
const WELCOME_ROWS = 12
|
|
38
|
+
const VERSION = '0.2.8'
|
|
40
39
|
const CHROME_ROWS = 11
|
|
41
40
|
|
|
42
41
|
const SLASH_COMMANDS = [
|
|
@@ -50,6 +49,7 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
50
49
|
const chat = useChat({ client, ready, resumeSessionId })
|
|
51
50
|
const [showHelp, setShowHelp] = useState(false)
|
|
52
51
|
const [scrollBack, setScrollBack] = useState(0)
|
|
52
|
+
const [modelLabel, setModelLabel] = useState<string | null>(null)
|
|
53
53
|
const terminal = useTerminalSize()
|
|
54
54
|
|
|
55
55
|
const runSlash = useCallback((raw: string) => {
|
|
@@ -80,11 +80,12 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
80
80
|
() => fitTranscript(chat.entries, transcriptRows, terminal.columns, scrollBack),
|
|
81
81
|
[chat.entries, transcriptRows, terminal.columns, scrollBack],
|
|
82
82
|
)
|
|
83
|
-
// Welcome card is for
|
|
84
|
-
//
|
|
85
|
-
// header +
|
|
86
|
-
// recent messages
|
|
87
|
-
|
|
83
|
+
// Welcome card is for a truly fresh session only. Once there's any
|
|
84
|
+
// conversation (or an in-flight user message) it disappears, handing the
|
|
85
|
+
// compact header + bottom-anchored transcript the full height. Keeping it
|
|
86
|
+
// while chatting left recent messages stranded mid-screen with a blank gap
|
|
87
|
+
// above the composer.
|
|
88
|
+
const showWelcome = chat.entries.length === 0 && chat.pendingUser === null && scrollBack === 0
|
|
88
89
|
|
|
89
90
|
// In-app history pager. Ink redraws only the live frame, so the terminal's
|
|
90
91
|
// own scrollback holds no past turns — older entries are unreachable unless we
|
|
@@ -100,6 +101,19 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
100
101
|
if (cur > prev && scrollBack > 0) setScrollBack(s => s + (cur - prev))
|
|
101
102
|
}, [chat.entries.length, scrollBack])
|
|
102
103
|
|
|
104
|
+
// Show the model's friendly label (e.g. "GPT-5.6-Sol") in the header/status
|
|
105
|
+
// instead of its opaque key (e.g. "codex:mobiusdefaultaabb").
|
|
106
|
+
useEffect(() => {
|
|
107
|
+
const key = ready.prefs.model
|
|
108
|
+
if (!key) { setModelLabel(null); return }
|
|
109
|
+
let cancelled = false
|
|
110
|
+
client.modelOptions()
|
|
111
|
+
.then(opts => { if (!cancelled) setModelLabel(opts.find(o => o.key === key)?.label ?? null) })
|
|
112
|
+
.catch(() => { if (!cancelled) setModelLabel(null) })
|
|
113
|
+
return () => { cancelled = true }
|
|
114
|
+
}, [client, ready.prefs.model])
|
|
115
|
+
const modelDisplay = modelLabel ?? ready.prefs.model ?? 'default'
|
|
116
|
+
|
|
103
117
|
useInput((_input, key) => {
|
|
104
118
|
// The composer ignores pageUp/pageDown, so binding them here can't clash
|
|
105
119
|
// with text entry, history navigation, or the slash-command popup.
|
|
@@ -118,7 +132,7 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
118
132
|
>
|
|
119
133
|
<Box flexDirection="column" flexGrow={1} overflowY="hidden">
|
|
120
134
|
{showWelcome
|
|
121
|
-
? <WelcomeCard ready={ready} columns={terminal.columns} resumed={Boolean(resumeSessionId)} />
|
|
135
|
+
? <WelcomeCard ready={ready} columns={terminal.columns} resumed={Boolean(resumeSessionId)} modelDisplay={modelDisplay} />
|
|
122
136
|
: <CompactHeader ready={ready} sessionId={chat.sessionId} />}
|
|
123
137
|
|
|
124
138
|
<Box flexGrow={1} flexDirection="column" justifyContent={showWelcome ? 'flex-start' : 'flex-end'} overflowY="hidden">
|
|
@@ -157,6 +171,7 @@ export function ChatScreen({ client, ready, webUserId, resumeSessionId, onClear,
|
|
|
157
171
|
columns={terminal.columns}
|
|
158
172
|
webUrl={buildWebUrl(client.server, webUserId, ready, chat.sessionId)}
|
|
159
173
|
aimuxStatus={aimuxStatus}
|
|
174
|
+
modelDisplay={modelDisplay}
|
|
160
175
|
/>
|
|
161
176
|
</Box>
|
|
162
177
|
)
|
|
@@ -180,7 +195,7 @@ function useTerminalSize(): TerminalSize {
|
|
|
180
195
|
return size
|
|
181
196
|
}
|
|
182
197
|
|
|
183
|
-
function WelcomeCard({ ready, columns, resumed }: { ready: ReadyState; columns: number; resumed: boolean }) {
|
|
198
|
+
function WelcomeCard({ ready, columns, resumed, modelDisplay }: { ready: ReadyState; columns: number; resumed: boolean; modelDisplay: string }) {
|
|
184
199
|
const cwd = compactPath(process.cwd())
|
|
185
200
|
const width = Math.max(38, Math.min(68, columns - 4))
|
|
186
201
|
const labelWidth = 11
|
|
@@ -193,7 +208,7 @@ function WelcomeCard({ ready, columns, resumed }: { ready: ReadyState; columns:
|
|
|
193
208
|
<Text dimColor> (v{VERSION})</Text>
|
|
194
209
|
</Text>
|
|
195
210
|
<Text> </Text>
|
|
196
|
-
<MetaRow label="model:" value={
|
|
211
|
+
<MetaRow label="model:" value={modelDisplay} hint="/help 查看命令" labelWidth={labelWidth} />
|
|
197
212
|
<MetaRow label="project:" value={ready.project.name} labelWidth={labelWidth} />
|
|
198
213
|
<MetaRow label="task:" value={ready.issue.title} labelWidth={labelWidth} />
|
|
199
214
|
<MetaRow label="directory:" value={cwd} labelWidth={labelWidth} />
|
|
@@ -461,14 +476,15 @@ function Composer({ onSubmit, onStop, onQuit, typing, commands }: ComposerProps)
|
|
|
461
476
|
)
|
|
462
477
|
}
|
|
463
478
|
|
|
464
|
-
function StatusArea({ ready, sessionId, columns, webUrl, aimuxStatus }: {
|
|
479
|
+
function StatusArea({ ready, sessionId, columns, webUrl, aimuxStatus, modelDisplay }: {
|
|
465
480
|
ready: ReadyState
|
|
466
481
|
sessionId: string | null
|
|
467
482
|
columns: number
|
|
468
483
|
webUrl: string
|
|
469
484
|
aimuxStatus?: AimuxStatus
|
|
485
|
+
modelDisplay: string
|
|
470
486
|
}) {
|
|
471
|
-
const model =
|
|
487
|
+
const model = modelDisplay
|
|
472
488
|
const language = ready.prefs.language === 'en' ? 'English' : '中文'
|
|
473
489
|
const cwd = compactPath(process.cwd())
|
|
474
490
|
const leftRaw = columns >= 100
|
|
@@ -131,11 +131,13 @@ export function PrepScreen({ client, onReady, onQuit }: {
|
|
|
131
131
|
setStep(next)
|
|
132
132
|
if (!next) finish(iss, p)
|
|
133
133
|
}
|
|
134
|
-
async function createIssue(name: string
|
|
134
|
+
async function createIssue(name: string) {
|
|
135
135
|
if (!project) return
|
|
136
136
|
setStatusMsg('创建任务…')
|
|
137
137
|
try {
|
|
138
|
-
|
|
138
|
+
// git worktree is disabled by default and not offered as a choice — TUI
|
|
139
|
+
// tasks always run in the bound working tree.
|
|
140
|
+
const iss = await client.createIssue(project.id, { title: name || '命令行任务', description: '由 TUI 创建', use_worktree: false })
|
|
139
141
|
setIssues(await client.listIssues(project.id, 'active'))
|
|
140
142
|
await pickIssue(iss)
|
|
141
143
|
} catch (e: any) { setStatusMsg(`创建任务失败: ${e?.message ?? e}`) }
|
|
@@ -278,29 +280,21 @@ function ProjectPicker({ cwd, projects, statusMsg, onPick, onCreate, onQuit }: {
|
|
|
278
280
|
function IssuePicker({ issues, onPick, onCreate }: {
|
|
279
281
|
issues: Issue[]
|
|
280
282
|
onPick: (i: Issue) => void
|
|
281
|
-
onCreate: (name: string
|
|
283
|
+
onCreate: (name: string) => void
|
|
282
284
|
}) {
|
|
283
|
-
const [mode, setMode] = useState<'list' | 'create-name'
|
|
285
|
+
const [mode, setMode] = useState<'list' | 'create-name'>('list')
|
|
284
286
|
const [name, setName] = useState('')
|
|
285
287
|
|
|
286
288
|
if (mode === 'create-name') {
|
|
289
|
+
// git worktree is disabled by default and the option is intentionally not
|
|
290
|
+
// offered — TUI-created tasks always run in the bound working tree.
|
|
287
291
|
return (
|
|
288
292
|
<Box flexDirection="column" paddingX={2}>
|
|
289
|
-
<Text bold color="cyan"
|
|
293
|
+
<Text bold color="cyan">创建新任务</Text>
|
|
294
|
+
<Text color="gray">输入任务名称(不使用 git worktree)</Text>
|
|
290
295
|
<TextInput value={name} onChange={setName} focused placeholder="命令行任务"
|
|
291
|
-
onSubmit={() =>
|
|
292
|
-
<Text color="gray"
|
|
293
|
-
</Box>
|
|
294
|
-
)
|
|
295
|
-
}
|
|
296
|
-
if (mode === 'create-wt') {
|
|
297
|
-
return (
|
|
298
|
-
<Box flexDirection="column" paddingX={2}>
|
|
299
|
-
<Text bold color="cyan">创建新任务 · 第 2 步:是否使用 git worktree?</Text>
|
|
300
|
-
<Select
|
|
301
|
-
items={[{ label: '否(默认)', value: 'no' }, { label: '是', value: 'yes' }]}
|
|
302
|
-
onSelect={v => onCreate(name, v === 'yes')} />
|
|
303
|
-
<Text color="gray">回车确认 · Esc 返回</Text>
|
|
296
|
+
onSubmit={() => onCreate(name)} />
|
|
297
|
+
<Text color="gray">回车创建 · Esc 返回</Text>
|
|
304
298
|
</Box>
|
|
305
299
|
)
|
|
306
300
|
}
|