mixdog 0.9.27 → 0.9.29

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.
@@ -102,42 +102,22 @@ function seedBackendChannelIds(entry = {}, backend = 'discord') {
102
102
  return next;
103
103
  }
104
104
 
105
- // Resolve the single-channel entry from the config: prefer the new `channel`
106
- // object; fall back (read-side only, no on-disk migration) to the legacy
107
- // channelsConfig[mainChannel ?? 'main'] entry, then the first entry with an id.
105
+ // Resolve the single-channel entry from the config's `channel` object.
108
106
  function resolveChannelEntry(cfg = {}) {
109
107
  if (cfg.channel && typeof cfg.channel === 'object'
110
108
  && (cfg.channel.channelId || cfg.channel.discordChannelId || cfg.channel.telegramChatId)) {
111
109
  return cfg.channel;
112
110
  }
113
- const legacy = cfg.channelsConfig && typeof cfg.channelsConfig === 'object' ? cfg.channelsConfig : null;
114
- if (legacy) {
115
- const mainName = cfg.mainChannel ?? 'main';
116
- const preferred = legacy[mainName];
117
- if (preferred && typeof preferred === 'object'
118
- && (preferred.channelId || preferred.discordChannelId || preferred.telegramChatId)) {
119
- return preferred;
120
- }
121
- for (const entry of Object.values(legacy)) {
122
- if (entry && typeof entry === 'object'
123
- && (entry.channelId || entry.discordChannelId || entry.telegramChatId)) {
124
- return entry;
125
- }
126
- }
127
- }
128
111
  return cfg.channel && typeof cfg.channel === 'object' ? cfg.channel : {};
129
112
  }
130
113
 
131
114
  function updateChannelsSection(build) {
132
115
  let next;
133
116
  updateSection('channels', (current) => {
134
- // Preserve any legacy channelsConfig/mainChannel that already live on disk
135
- // for read-side compat, but never re-emit them from our writers: strip them
136
- // out of the returned shape so writes converge on the single `channel`.
117
+ // Writes converge on the single `channel` object.
137
118
  const normalized = normalizeChannelsConfig(current);
138
119
  next = build(normalized);
139
- const { channelsConfig: _lc, mainChannel: _lm, ...clean } = normalizeChannelsConfig(next);
140
- return clean;
120
+ return normalizeChannelsConfig(next);
141
121
  });
142
122
  return next;
143
123
  }
@@ -151,8 +131,7 @@ async function updateChannelsSectionAsync(build) {
151
131
  await updateSectionAsync('channels', (current) => {
152
132
  const normalized = normalizeChannelsConfig(current);
153
133
  next = build(normalized);
154
- const { channelsConfig: _lc, mainChannel: _lm, ...clean } = normalizeChannelsConfig(next);
155
- return clean;
134
+ return normalizeChannelsConfig(next);
156
135
  });
157
136
  return next;
158
137
  }
@@ -168,8 +147,8 @@ function listEntryDirs(dir) {
168
147
  }
169
148
  }
170
149
 
171
- // Single-channel read: resolves `cfg.channel` (legacy channelsConfig fallback)
172
- // into the flat shape the settings/TUI layer consumes.
150
+ // Single-channel read: resolves `cfg.channel` into the flat shape the
151
+ // settings/TUI layer consumes.
173
152
  export function getChannel(config = {}) {
174
153
  const cfg = normalizeChannelsConfig(config);
175
154
  const backend = cfg.backend === 'telegram' ? 'telegram' : 'discord';
package/src/tui/App.jsx CHANGED
@@ -21,6 +21,7 @@ import { Box, Text, useApp, useInput, useStdin, useStdout } from 'ink';
21
21
  import { theme, surfaceBackground } from './theme.mjs';
22
22
  import { useEngine } from './hooks/useEngine.mjs';
23
23
  import { classifyToolCategory } from '../runtime/shared/tool-surface.mjs';
24
+ import { localPackageVersion } from '../runtime/shared/update-checker.mjs';
24
25
  import { Spinner } from './components/Spinner.jsx';
25
26
  import { StatusLine } from './components/StatusLine.jsx';
26
27
  import { PromptInput } from './components/PromptInput.jsx';
@@ -3058,7 +3059,7 @@ export function App({ store, initialStatusLine = '', forceOnboarding = false })
3058
3059
  <Text color={theme.logo ?? theme.claude} bold>{centerLine('██║╚██╔╝██║██║ ██╔██╗ ██║ ██║██║ ██║██║ ██║', frameColumns)}</Text>
3059
3060
  <Text color={theme.logo ?? theme.claude} bold>{centerLine('██║ ╚═╝ ██║██║██╔╝ ██╗██████╔╝╚██████╔╝╚██████╔╝', frameColumns)}</Text>
3060
3061
  <Box height={1} flexShrink={0} />
3061
- <Text color={theme.inactive}>{centerLine(`mixdog coding agent · ${state.cwd}`, frameColumns, 4)}</Text>
3062
+ <Text color={theme.inactive}>{centerLine(`mixdog coding agent · v${localPackageVersion()} · ${state.cwd}`, frameColumns, 4)}</Text>
3062
3063
  </Box>
3063
3064
  ) : null}
3064
3065
 
@@ -709,7 +709,7 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
709
709
  return (
710
710
  <Box flexDirection="column" width="100%" height={3} overflow="hidden" justifyContent="flex-start" paddingLeft={2} backgroundColor={surfaceBackground()}>
711
711
  <Box flexDirection="row" width="100%" overflow="hidden">
712
- <Box flexGrow={1} flexShrink={1} overflow="hidden">
712
+ <Box flexGrow={1} flexShrink={1} flexBasis={0} overflow="hidden">
713
713
  <Text wrap="truncate">{lines[0] || ' '}</Text>
714
714
  </Box>
715
715
  <Box flexShrink={0} marginLeft={1} marginRight={1}>
@@ -717,7 +717,7 @@ function StatusLineView({ sessionId, clientHostPid, provider, model, effort, fas
717
717
  </Box>
718
718
  </Box>
719
719
  <Box flexDirection="row" width="100%" overflow="hidden">
720
- <Box flexGrow={1} flexShrink={1} overflow="hidden">
720
+ <Box flexGrow={1} flexShrink={1} flexBasis={0} overflow="hidden">
721
721
  <Text wrap="truncate">{lines[1] || ' '}</Text>
722
722
  </Box>
723
723
  </Box>
@@ -432,7 +432,16 @@ export function ToolExecution({ name, args, result, rawResult, isError, errorCou
432
432
  const markerGlyph = isAgentResponse
433
433
  ? AGENT_RESPONSE_MARKER
434
434
  : (isAgentSurfaceCard ? AGENT_CALL_MARKER : TURN_MARKER);
435
- const dotText = pending && !blinkExpired && !blinkOn ? ' ' : markerGlyph;
435
+ // Directional arrow markers (`←` spawn/send out, `→` response back) render 2
436
+ // cells wide in some terminals (Windows Terminal / Cascadia) while our width
437
+ // math counts them as 1, so the `Box minWidth={2}` gutter padding gets
438
+ // overdrawn and the label glues to the arrow ("←Spawn"). Carry an explicit
439
+ // trailing space in the marker string so the gap is a real character that
440
+ // survives regardless of how wide the terminal actually draws the glyph. The
441
+ // `●` turn marker is a true 1-cell glyph and keeps the padding-only gutter.
442
+ const isDirectionalMarker = isAgentResponse || isAgentSurfaceCard;
443
+ const markerText = isDirectionalMarker ? `${markerGlyph} ` : markerGlyph;
444
+ const dotText = pending && !blinkExpired && !blinkOn ? ' ' : markerText;
436
445
  let labelText;
437
446
  if (isAgentResponse) labelText = agentResponseTitle(parsedArgs);
438
447
  else if (isBackgroundResponse) labelText = backgroundTaskResultTitle(normalizedName, backgroundMeta || parsedArgs);
@@ -38,9 +38,12 @@ const graphemeSegmenter = new Intl.Segmenter(undefined, { granularity: 'grapheme
38
38
  * markers (agent card ←/→, history ↑/↓): WT draws them 1 cell in
39
39
  * Cascadia, and widening them ate the marker's gutter padding space
40
40
  * ("←Spawn" rendered glued / shifted vs the ● rows).
41
+ * Also excludes U+21BB (↻, the statusline quota-reset marker): WT/Cascadia
42
+ * draws it 1 cell, so widening it reserved a phantom cell that shifted the
43
+ * right-aligned workflow label one column left when the usage segment appeared.
41
44
  */
42
45
  export function isProblemCodePoint(cp) {
43
- return (cp >= 0x2460 && cp <= 0x24ff) || (cp >= 0x2194 && cp <= 0x21ff);
46
+ return (cp >= 0x2460 && cp <= 0x24ff) || (cp >= 0x2194 && cp <= 0x21ff && cp !== 0x21bb);
44
47
  }
45
48
 
46
49
  // Fast precheck for the problem ranges above. Lets the hot path bail before