git-coco 0.56.0 → 0.57.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/README.md +1 -1
- package/dist/index.d.ts +8 -1
- package/dist/index.esm.mjs +681 -78
- package/dist/index.js +680 -77
- package/package.json +2 -1
package/dist/index.esm.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import ora from 'ora';
|
|
|
22
22
|
import now from 'performance-now';
|
|
23
23
|
import prettyMilliseconds from 'pretty-ms';
|
|
24
24
|
import * as fs$1 from 'node:fs';
|
|
25
|
-
import { existsSync,
|
|
25
|
+
import { existsSync, unlinkSync, statSync, mkdirSync, writeFileSync, renameSync, mkdtempSync, rmSync, readFileSync as readFileSync$1 } from 'node:fs';
|
|
26
26
|
import * as os$1 from 'node:os';
|
|
27
27
|
import { platform, homedir, tmpdir as tmpdir$1 } from 'node:os';
|
|
28
28
|
import * as path$1 from 'node:path';
|
|
@@ -61,7 +61,7 @@ import { pathToFileURL } from 'url';
|
|
|
61
61
|
/**
|
|
62
62
|
* Current build version from package.json
|
|
63
63
|
*/
|
|
64
|
-
const BUILD_VERSION = "0.
|
|
64
|
+
const BUILD_VERSION = "0.57.0";
|
|
65
65
|
|
|
66
66
|
const isInteractive = (config) => {
|
|
67
67
|
return config?.mode === 'interactive' || !!config?.interactive;
|
|
@@ -2139,6 +2139,10 @@ const schema$1 = {
|
|
|
2139
2139
|
"selection": {
|
|
2140
2140
|
"type": "string"
|
|
2141
2141
|
},
|
|
2142
|
+
"selectionForeground": {
|
|
2143
|
+
"type": "string",
|
|
2144
|
+
"description": "Foreground for text sitting on the `selection` background. Derived automatically from `selection` (black on light, white on dark) so the selected row stays readable regardless of the user's terminal default foreground — but can be overridden per theme via `options.colors`."
|
|
2145
|
+
},
|
|
2142
2146
|
"success": {
|
|
2143
2147
|
"type": "string"
|
|
2144
2148
|
},
|
|
@@ -2182,7 +2186,25 @@ const schema$1 = {
|
|
|
2182
2186
|
"vitesse-dark",
|
|
2183
2187
|
"vesper",
|
|
2184
2188
|
"flexoki",
|
|
2185
|
-
"mellow"
|
|
2189
|
+
"mellow",
|
|
2190
|
+
"night-owl",
|
|
2191
|
+
"cobalt2",
|
|
2192
|
+
"oceanic-next",
|
|
2193
|
+
"catppuccin-macchiato",
|
|
2194
|
+
"gruvbox-light",
|
|
2195
|
+
"tokyo-night-day",
|
|
2196
|
+
"one-light",
|
|
2197
|
+
"ayu-light",
|
|
2198
|
+
"rose-pine-dawn",
|
|
2199
|
+
"everforest-light",
|
|
2200
|
+
"vitesse-light",
|
|
2201
|
+
"dayfox",
|
|
2202
|
+
"night-owl-light",
|
|
2203
|
+
"flexoki-light",
|
|
2204
|
+
"material-lighter",
|
|
2205
|
+
"papercolor-light",
|
|
2206
|
+
"modus-operandi",
|
|
2207
|
+
"quiet-light"
|
|
2186
2208
|
]
|
|
2187
2209
|
}
|
|
2188
2210
|
}
|
|
@@ -15212,10 +15234,19 @@ const CommitSplitPlanSchema = objectType({
|
|
|
15212
15234
|
title: stringType().min(1),
|
|
15213
15235
|
body: stringType().optional(),
|
|
15214
15236
|
rationale: stringType().optional(),
|
|
15215
|
-
|
|
15216
|
-
hunks
|
|
15237
|
+
// Both optional: the model legitimately emits a group with *either*
|
|
15238
|
+
// `files` or `hunks` (a file-level vs hunk-level grouping), not always
|
|
15239
|
+
// both. Requiring both made Zod throw "Required" and the whole split
|
|
15240
|
+
// chain failed to parse before the refine could run. The refine below
|
|
15241
|
+
// still enforces "at least one", and every downstream consumer already
|
|
15242
|
+
// reads these as `group.files || []`. (Kept `.optional()` rather than
|
|
15243
|
+
// `.default([])` so the schema's input and output types stay identical
|
|
15244
|
+
// — `executeChainWithSchema` takes a `z.ZodSchema<T>`, which requires
|
|
15245
|
+
// that.)
|
|
15246
|
+
files: arrayType(stringType()).optional(),
|
|
15247
|
+
hunks: arrayType(stringType()).optional(),
|
|
15217
15248
|
})
|
|
15218
|
-
.refine((group) => group.files
|
|
15249
|
+
.refine((group) => (group.files?.length ?? 0) > 0 || (group.hunks?.length ?? 0) > 0, {
|
|
15219
15250
|
message: 'Each group must include at least one file or hunk',
|
|
15220
15251
|
}))
|
|
15221
15252
|
.min(1),
|
|
@@ -32180,6 +32211,10 @@ function renderBranchesSurface(h, components, state, context, contextStatus, bod
|
|
|
32180
32211
|
// row's dim and read as quiet chrome.
|
|
32181
32212
|
h(Text, { color: markerColor, dimColor: markerColor ? false : undefined }, marker.glyph), trailingName, h(Text, { dimColor: true }, timestampPadded), trailingDivergence);
|
|
32182
32213
|
});
|
|
32214
|
+
// Scroll indicators — same "N more above/below" pattern as the
|
|
32215
|
+
// sidebar and help overlay so the user knows the list continues.
|
|
32216
|
+
const branchesHasMoreAbove = startIndex > 0 && localBranches.length > 0;
|
|
32217
|
+
const branchesHasMoreBelow = startIndex + listRows < localBranches.length;
|
|
32183
32218
|
return h(Box, {
|
|
32184
32219
|
borderColor: focusBorderColor(theme, focused),
|
|
32185
32220
|
borderStyle: theme.borderStyle,
|
|
@@ -32187,7 +32222,11 @@ function renderBranchesSurface(h, components, state, context, contextStatus, bod
|
|
|
32187
32222
|
flexShrink: 0,
|
|
32188
32223
|
paddingX: 1,
|
|
32189
32224
|
width,
|
|
32190
|
-
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Branches', focused)), h(Text, { dimColor: true }, headerRight)), ...renderPromotedFilterAffordance(h, Text, state, theme), ...
|
|
32225
|
+
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Branches', focused)), h(Text, { dimColor: true }, headerRight)), ...renderPromotedFilterAffordance(h, Text, state, theme), ...(branchesHasMoreAbove
|
|
32226
|
+
? [h(Text, { key: 'branches-more-above', dimColor: true }, ` ↑ ${startIndex} more above`)]
|
|
32227
|
+
: []), ...lines, ...(branchesHasMoreBelow
|
|
32228
|
+
? [h(Text, { key: 'branches-more-below', dimColor: true }, ` ↓ ${localBranches.length - (startIndex + listRows)} more below`)]
|
|
32229
|
+
: []));
|
|
32191
32230
|
}
|
|
32192
32231
|
|
|
32193
32232
|
/**
|
|
@@ -32960,12 +32999,19 @@ function renderDiffSurface(h, components, state, context, contextStatus, worktre
|
|
|
32960
32999
|
// sees at a glance which file the cursor is inside.
|
|
32961
33000
|
const isActive = absoluteIndex === activeStartLine;
|
|
32962
33001
|
const arrow = theme.ascii ? '> ' : '▾ ';
|
|
33002
|
+
const activeHeader = isActive && focused && !theme.noColor;
|
|
32963
33003
|
return h(Text, {
|
|
32964
33004
|
key: `stash-diff-line-${absoluteIndex}`,
|
|
32965
33005
|
bold: true,
|
|
32966
|
-
|
|
32967
|
-
|
|
32968
|
-
inverse
|
|
33006
|
+
// Active header sits on the selection bar with a
|
|
33007
|
+
// contrast-guaranteed foreground (matches history/status).
|
|
33008
|
+
// The old `inverse` swap turned the accent into the bar and
|
|
33009
|
+
// left the path in the selection color — low-contrast on
|
|
33010
|
+
// light themes (e.g. accent blue bar + light-gray text).
|
|
33011
|
+
color: activeHeader
|
|
33012
|
+
? theme.colors.selectionForeground
|
|
33013
|
+
: (theme.noColor ? undefined : theme.colors.accent),
|
|
33014
|
+
backgroundColor: activeHeader ? theme.colors.selection : undefined,
|
|
32969
33015
|
}, (() => {
|
|
32970
33016
|
// Smart path truncation for the diff file header: keep
|
|
32971
33017
|
// the leading arrow glyph and elide middle path
|
|
@@ -34043,7 +34089,7 @@ function formatHistoryFetchArgs(args) {
|
|
|
34043
34089
|
* Returns the spans flat so the caller can splat them into the row's
|
|
34044
34090
|
* outer Text alongside other segments without an extra wrapper.
|
|
34045
34091
|
*/
|
|
34046
|
-
function renderTypedSubject(h, Text, text, theme, key) {
|
|
34092
|
+
function renderTypedSubject(h, Text, text, theme, key, suppressColor = false) {
|
|
34047
34093
|
const parsed = parseConventionalCommitPrefix(text);
|
|
34048
34094
|
if (!parsed) {
|
|
34049
34095
|
return [h(Text, { key: `${key}-msg` }, text)];
|
|
@@ -34051,7 +34097,9 @@ function renderTypedSubject(h, Text, text, theme, key) {
|
|
|
34051
34097
|
if (text.length < parsed.prefix.length) {
|
|
34052
34098
|
return [h(Text, { key: `${key}-msg` }, text)];
|
|
34053
34099
|
}
|
|
34054
|
-
|
|
34100
|
+
// When the row is selected (inverted), suppress the type color so
|
|
34101
|
+
// text inherits the dark inverted foreground and stays readable.
|
|
34102
|
+
const color = suppressColor ? undefined : getConventionalCommitColor(parsed, theme);
|
|
34055
34103
|
return [
|
|
34056
34104
|
h(Text, { key: `${key}-type`, color, bold: parsed.breaking }, parsed.prefix),
|
|
34057
34105
|
h(Text, { key: `${key}-rest` }, text.slice(parsed.prefix.length)),
|
|
@@ -34072,15 +34120,10 @@ function renderLaneSegmentSpans(h, Text, segments, theme, padTo, keyPrefix, opti
|
|
|
34072
34120
|
const elements = [];
|
|
34073
34121
|
let totalLen = 0;
|
|
34074
34122
|
segments.forEach((seg, idx) => {
|
|
34075
|
-
const laneColor = getLaneColor(seg.laneId, theme);
|
|
34123
|
+
const laneColor = options.suppressColor ? undefined : (getLaneColor(seg.laneId, theme) ?? muted);
|
|
34076
34124
|
elements.push(h(Text, {
|
|
34077
34125
|
key: `${keyPrefix}-${idx}`,
|
|
34078
|
-
color: laneColor
|
|
34079
|
-
// Ink does not cascade dimColor from a parent Text to children,
|
|
34080
|
-
// so the caller's "this whole row should fade" intent has to
|
|
34081
|
-
// travel here as an explicit flag (#831). Used for graph-only
|
|
34082
|
-
// lane-closure rows, where the lane colors otherwise compete
|
|
34083
|
-
// for attention with the commits they connect.
|
|
34126
|
+
color: laneColor,
|
|
34084
34127
|
dimColor: options.forceDim || (theme.noColor && seg.laneId === undefined),
|
|
34085
34128
|
}, seg.text));
|
|
34086
34129
|
totalLen += seg.text.length;
|
|
@@ -34131,18 +34174,26 @@ function renderCommitHistoryRow(h, Text, commit, graph, graphWidth, selected, th
|
|
|
34131
34174
|
const messageRoom = Math.max(8, totalWidth - fixedWidth - cellWidth(refsTrunc));
|
|
34132
34175
|
const message = truncateCells(commit.message, messageRoom);
|
|
34133
34176
|
const selectedBg = selected && !theme.noColor ? theme.colors.selection : undefined;
|
|
34134
|
-
|
|
34135
|
-
|
|
34177
|
+
// Don't use inverse — it makes child colors unreadable. Instead, set a
|
|
34178
|
+
// background on the row AND an explicit, contrast-guaranteed foreground
|
|
34179
|
+
// (`selectionForeground`, derived from the selection bg) on the outer
|
|
34180
|
+
// span. Suppressing each child's own color to `undefined` then lets it
|
|
34181
|
+
// inherit that readable foreground — so the whole selected row stays
|
|
34182
|
+
// legible regardless of the user's terminal default foreground, which
|
|
34183
|
+
// is what the old "rely on the default fg" approach got wrong.
|
|
34184
|
+
const selectedFg = selected && !theme.noColor ? theme.colors.selectionForeground : undefined;
|
|
34185
|
+
const accent = selected ? undefined : (theme.noColor ? undefined : theme.colors.accent);
|
|
34186
|
+
const muted = selected ? undefined : (theme.noColor ? undefined : theme.colors.muted);
|
|
34136
34187
|
// Lane-colored graph spans when full graph mode + non-ASCII rendering
|
|
34137
34188
|
// is in play; otherwise fall back to the legacy single-muted span so
|
|
34138
34189
|
// compact mode and legacy terminals stay visually unchanged.
|
|
34139
34190
|
const graphChildren = laneSegments && !theme.ascii
|
|
34140
|
-
? renderLaneSegmentSpans(h, Text, laneSegments, theme, graphWidth, `c${index}
|
|
34141
|
-
: [h(Text, { color: muted, dimColor: theme.noColor }, substituteGraphChars(graph.padEnd(graphWidth), { ascii: theme.ascii }))];
|
|
34191
|
+
? renderLaneSegmentSpans(h, Text, laneSegments, theme, graphWidth, `c${index}`, { suppressColor: selected })
|
|
34192
|
+
: [h(Text, { color: muted, dimColor: !selected && theme.noColor }, substituteGraphChars(graph.padEnd(graphWidth), { ascii: theme.ascii }))];
|
|
34142
34193
|
return h(Text, {
|
|
34143
34194
|
key: `${commit.hash}-${index}`,
|
|
34144
34195
|
backgroundColor: selectedBg,
|
|
34145
|
-
|
|
34196
|
+
color: selectedFg,
|
|
34146
34197
|
}, ...graphChildren, ' ',
|
|
34147
34198
|
// "Just landed" marker — a single thick vertical bar in the
|
|
34148
34199
|
// accent color before the short hash. Fades when the runtime
|
|
@@ -34164,11 +34215,11 @@ function renderCommitHistoryRow(h, Text, commit, graph, graphWidth, selected, th
|
|
|
34164
34215
|
// Date column drops out entirely at `tight` density — no spacer
|
|
34165
34216
|
// either, so the message column slides left into the freed cells.
|
|
34166
34217
|
dateText
|
|
34167
|
-
? h(Text, { key: `${commit.hash}-${index}-date`, dimColor:
|
|
34218
|
+
? h(Text, { key: `${commit.hash}-${index}-date`, dimColor: !selected }, dateText, ' ')
|
|
34168
34219
|
: null,
|
|
34169
34220
|
// Branch chip prefix (full-graph mode only) lands right before the
|
|
34170
34221
|
// message so the eye reads "branch · subject" as a unit.
|
|
34171
|
-
chip.node, ...renderTypedSubject(h, Text, message, theme, `${commit.hash}-${index}-subj
|
|
34222
|
+
chip.node, ...renderTypedSubject(h, Text, message, theme, `${commit.hash}-${index}-subj`, selected), refsTrunc ? h(Text, { color: accent }, refsTrunc) : null);
|
|
34172
34223
|
}
|
|
34173
34224
|
/**
|
|
34174
34225
|
* Stacked variant used at `rowMode='stacked'` (rail tier). Each
|
|
@@ -34183,9 +34234,13 @@ function renderCommitHistoryRow(h, Text, commit, graph, graphWidth, selected, th
|
|
|
34183
34234
|
*/
|
|
34184
34235
|
function renderStackedCommitHistoryRow(h, Text, Box, commit, graph, graphWidth, selected, theme, index, panelWidth, fullGraph, now, laneSegments, isRecent = false, remoteNames) {
|
|
34185
34236
|
const totalWidth = Math.max(20, panelWidth - 4);
|
|
34186
|
-
|
|
34187
|
-
|
|
34237
|
+
// Suppress child colors on selected rows so each span inherits the
|
|
34238
|
+
// contrast-guaranteed `selectionForeground` set on the line-1 span,
|
|
34239
|
+
// keeping the selected row readable against the selection bg.
|
|
34240
|
+
const accent = selected ? undefined : (theme.noColor ? undefined : theme.colors.accent);
|
|
34241
|
+
const muted = selected ? undefined : (theme.noColor ? undefined : theme.colors.muted);
|
|
34188
34242
|
const selectedBg = selected && !theme.noColor ? theme.colors.selection : undefined;
|
|
34243
|
+
const selectedFg = selected && !theme.noColor ? theme.colors.selectionForeground : undefined;
|
|
34189
34244
|
// Line 1 — subject row. Mostly mirrors the single-line layout but
|
|
34190
34245
|
// skips the date and refs so the message has the whole tail to
|
|
34191
34246
|
// itself. Branch chip rides between the hash and the subject the
|
|
@@ -34197,15 +34252,15 @@ function renderStackedCommitHistoryRow(h, Text, Box, commit, graph, graphWidth,
|
|
|
34197
34252
|
const lineOneFixed = graphWidth + 1 + commit.shortHash.length + 1 + recentMarkerWidth + chip.width;
|
|
34198
34253
|
const subject = truncateCells(commit.message, Math.max(8, totalWidth - lineOneFixed));
|
|
34199
34254
|
const graphChildren = laneSegments && !theme.ascii
|
|
34200
|
-
? renderLaneSegmentSpans(h, Text, laneSegments, theme, graphWidth, `cs${index}
|
|
34201
|
-
: [h(Text, { color: muted, dimColor: theme.noColor }, substituteGraphChars(graph.padEnd(graphWidth), { ascii: theme.ascii }))];
|
|
34255
|
+
? renderLaneSegmentSpans(h, Text, laneSegments, theme, graphWidth, `cs${index}`, { suppressColor: selected })
|
|
34256
|
+
: [h(Text, { color: muted, dimColor: !selected && theme.noColor }, substituteGraphChars(graph.padEnd(graphWidth), { ascii: theme.ascii }))];
|
|
34202
34257
|
const lineOne = h(Text, {
|
|
34203
34258
|
key: `${commit.hash}-${index}-l1`,
|
|
34204
34259
|
backgroundColor: selectedBg,
|
|
34205
|
-
|
|
34260
|
+
color: selectedFg,
|
|
34206
34261
|
}, ...graphChildren, ' ', isRecent
|
|
34207
34262
|
? h(Text, { color: accent, bold: true }, theme.ascii ? '* ' : '▎ ')
|
|
34208
|
-
: null, h(Text, { color: accent, bold: selected || isRecent }, commit.shortHash), ' ', chip.node, ...renderTypedSubject(h, Text, subject, theme, `${commit.hash}-${index}-stk-subj
|
|
34263
|
+
: null, h(Text, { color: accent, bold: selected || isRecent }, commit.shortHash), ' ', chip.node, ...renderTypedSubject(h, Text, subject, theme, `${commit.hash}-${index}-stk-subj`, selected));
|
|
34209
34264
|
// Line 2 — metadata row, padded to align with the start of the
|
|
34210
34265
|
// shortHash on line 1 so the eye still groups them as one commit.
|
|
34211
34266
|
// Selection background does not extend here so we don't get a thick
|
|
@@ -34258,8 +34313,11 @@ function renderPendingCommitRow(h, Text, worktree, selected, theme) {
|
|
|
34258
34313
|
return h(Text, {
|
|
34259
34314
|
key: 'pending-commit-row',
|
|
34260
34315
|
bold: true,
|
|
34261
|
-
|
|
34262
|
-
|
|
34316
|
+
// On selection, swap to the contrast-guaranteed foreground so the
|
|
34317
|
+
// accent label doesn't wash out against the selection bar.
|
|
34318
|
+
color: selected && !theme.noColor
|
|
34319
|
+
? theme.colors.selectionForeground
|
|
34320
|
+
: (theme.noColor ? undefined : theme.colors.accent),
|
|
34263
34321
|
backgroundColor: selected && !theme.noColor ? theme.colors.selection : undefined,
|
|
34264
34322
|
}, truncateCells(label, 140));
|
|
34265
34323
|
}
|
|
@@ -34673,6 +34731,10 @@ function renderCommandPalette(h, components, state, width, theme, focused) {
|
|
|
34673
34731
|
dimColor: !isSelected,
|
|
34674
34732
|
}, truncateCells(line, width - 4));
|
|
34675
34733
|
});
|
|
34734
|
+
// Scroll indicators for the palette list — same pattern as the
|
|
34735
|
+
// sidebar and help overlay so the user knows there's more content.
|
|
34736
|
+
const paletteHasMoreAbove = startIndex > 0 && filtered.length > 0;
|
|
34737
|
+
const paletteHasMoreBelow = startIndex + listRows < filtered.length;
|
|
34676
34738
|
return h(Box, {
|
|
34677
34739
|
borderColor: focusBorderColor(theme, focused),
|
|
34678
34740
|
borderStyle: theme.borderStyle,
|
|
@@ -34681,7 +34743,11 @@ function renderCommandPalette(h, components, state, width, theme, focused) {
|
|
|
34681
34743
|
paddingX: 1,
|
|
34682
34744
|
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Command palette', focused)), h(Text, { dimColor: true }, matchSummary)), h(Text, { color: theme.colors.accent }, truncateCells(inputLine, width - 4)), h(Text, { dimColor: true }, truncateCells(hint, width - 4)), h(Text, undefined, ''), ...(showingRecent
|
|
34683
34745
|
? [h(Text, { key: 'palette-recent-hint', dimColor: true }, '· marks recently-used')]
|
|
34684
|
-
: []), ...
|
|
34746
|
+
: []), ...(paletteHasMoreAbove
|
|
34747
|
+
? [h(Text, { key: 'palette-more-above', dimColor: true }, ` ↑ ${startIndex} more above`)]
|
|
34748
|
+
: []), ...itemLines, ...(paletteHasMoreBelow
|
|
34749
|
+
? [h(Text, { key: 'palette-more-below', dimColor: true }, ` ↓ ${filtered.length - (startIndex + listRows)} more below`)]
|
|
34750
|
+
: []));
|
|
34685
34751
|
}
|
|
34686
34752
|
/**
|
|
34687
34753
|
* Split-plan overlay (#907) — renders the proposed commit groups for
|
|
@@ -35537,6 +35603,8 @@ function renderStashSurface(h, components, state, context, contextStatus, bodyRo
|
|
|
35537
35603
|
dimColor: !isSelected,
|
|
35538
35604
|
}, truncateCells(`${cursor} ${stash.ref.padEnd(12)} ${stash.message}`, 140));
|
|
35539
35605
|
});
|
|
35606
|
+
const stashHasMoreAbove = startIndex > 0 && stashes.length > 0;
|
|
35607
|
+
const stashHasMoreBelow = startIndex + listRows < stashes.length;
|
|
35540
35608
|
return h(Box, {
|
|
35541
35609
|
borderColor: focusBorderColor(theme, focused),
|
|
35542
35610
|
borderStyle: theme.borderStyle,
|
|
@@ -35544,7 +35612,11 @@ function renderStashSurface(h, components, state, context, contextStatus, bodyRo
|
|
|
35544
35612
|
flexShrink: 0,
|
|
35545
35613
|
paddingX: 1,
|
|
35546
35614
|
width,
|
|
35547
|
-
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Stash', focused)), h(Text, { dimColor: true }, headerRight)), ...renderPromotedFilterAffordance(h, Text, state, theme), ...
|
|
35615
|
+
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Stash', focused)), h(Text, { dimColor: true }, headerRight)), ...renderPromotedFilterAffordance(h, Text, state, theme), ...(stashHasMoreAbove
|
|
35616
|
+
? [h(Text, { key: 'stash-more-above', dimColor: true }, ` ↑ ${startIndex} more above`)]
|
|
35617
|
+
: []), ...lines, ...(stashHasMoreBelow
|
|
35618
|
+
? [h(Text, { key: 'stash-more-below', dimColor: true }, ` ↓ ${stashes.length - (startIndex + listRows)} more below`)]
|
|
35619
|
+
: []));
|
|
35548
35620
|
}
|
|
35549
35621
|
|
|
35550
35622
|
/**
|
|
@@ -35640,7 +35712,7 @@ function renderStatusSurface(h, components, state, context, contextStatus, bodyR
|
|
|
35640
35712
|
bold: true,
|
|
35641
35713
|
dimColor: !headerSelected && rowIndex > cursorRowIndex,
|
|
35642
35714
|
backgroundColor: headerSelected && !theme.noColor ? theme.colors.selection : undefined,
|
|
35643
|
-
|
|
35715
|
+
color: headerSelected && !theme.noColor ? theme.colors.selectionForeground : undefined,
|
|
35644
35716
|
}, truncateCells(text, 140));
|
|
35645
35717
|
}
|
|
35646
35718
|
const isSelected = !headerFocused && row.flatIndex === selectedIndex;
|
|
@@ -35660,8 +35732,11 @@ function renderStatusSurface(h, components, state, context, contextStatus, bodyR
|
|
|
35660
35732
|
key: `status-file-${row.flatIndex}-${rowIndex}`,
|
|
35661
35733
|
dimColor: !isSelected && rowIndex > cursorRowIndex,
|
|
35662
35734
|
backgroundColor: isSelected && focused && !theme.noColor ? theme.colors.selection : undefined,
|
|
35663
|
-
|
|
35664
|
-
}, ` ${cursorPart}`,
|
|
35735
|
+
color: isSelected && focused && !theme.noColor ? theme.colors.selectionForeground : undefined,
|
|
35736
|
+
}, ` ${cursorPart}`,
|
|
35737
|
+
// Suppress the dot's own color on selected rows so it inherits the
|
|
35738
|
+
// contrast-guaranteed selection foreground set on the row span.
|
|
35739
|
+
...(useDot ? [h(Text, { color: (isSelected && focused) ? undefined : dotColor }, STAGE_STATUS_DOT), ' '] : []), tailTrunc);
|
|
35665
35740
|
});
|
|
35666
35741
|
// When the mask narrows the list to nothing but the underlying repo
|
|
35667
35742
|
// is non-clean, surface why the panel looks empty so the user can
|
|
@@ -35676,6 +35751,10 @@ function renderStatusSurface(h, components, state, context, contextStatus, bodyR
|
|
|
35676
35751
|
: cleanHint
|
|
35677
35752
|
? [cleanHint]
|
|
35678
35753
|
: ['Worktree clean'];
|
|
35754
|
+
// Scroll indicators for the status file list — same pattern as
|
|
35755
|
+
// branches and the sidebar so the user knows there's more content.
|
|
35756
|
+
const statusHasMoreAbove = windowStart > 0 && surfaceRows.length > 0;
|
|
35757
|
+
const statusHasMoreBelow = windowStart + listRows < surfaceRows.length;
|
|
35679
35758
|
return h(Box, {
|
|
35680
35759
|
borderColor: focusBorderColor(theme, focused),
|
|
35681
35760
|
borderStyle: theme.borderStyle,
|
|
@@ -35691,7 +35770,11 @@ function renderStatusSurface(h, components, state, context, contextStatus, bodyR
|
|
|
35691
35770
|
// never touch the filter.
|
|
35692
35771
|
...(isStatusFilterMaskActive(state.statusFilterMask)
|
|
35693
35772
|
? [h(Text, { key: 'status-mask-indicator', dimColor: true }, `filter: ${formatStatusFilterMask(state.statusFilterMask)} (1/2/3 to toggle)`)]
|
|
35694
|
-
: []), ...
|
|
35773
|
+
: []), ...(statusHasMoreAbove
|
|
35774
|
+
? [h(Text, { key: 'status-more-above', dimColor: true }, ` ↑ ${windowStart} more above`)]
|
|
35775
|
+
: []), ...renderedRows, ...(statusHasMoreBelow
|
|
35776
|
+
? [h(Text, { key: 'status-more-below', dimColor: true }, ` ↓ ${surfaceRows.length - (windowStart + listRows)} more below`)]
|
|
35777
|
+
: []), ...fallbackLines.map((line, index) => h(Text, {
|
|
35695
35778
|
key: `status-surface-fallback-${index}`,
|
|
35696
35779
|
dimColor: index > 0,
|
|
35697
35780
|
}, truncateCells(line, 140))));
|
|
@@ -35921,6 +36004,8 @@ function renderTagsSurface(h, components, state, context, contextStatus, bodyRow
|
|
|
35921
36004
|
dimColor: !isSelected,
|
|
35922
36005
|
}, before, formatHyperlink(namePadded, url), after);
|
|
35923
36006
|
});
|
|
36007
|
+
const tagsHasMoreAbove = startIndex > 0 && tags.length > 0;
|
|
36008
|
+
const tagsHasMoreBelow = startIndex + listRows < tags.length;
|
|
35924
36009
|
return h(Box, {
|
|
35925
36010
|
borderColor: focusBorderColor(theme, focused),
|
|
35926
36011
|
borderStyle: theme.borderStyle,
|
|
@@ -35928,7 +36013,11 @@ function renderTagsSurface(h, components, state, context, contextStatus, bodyRow
|
|
|
35928
36013
|
flexShrink: 0,
|
|
35929
36014
|
paddingX: 1,
|
|
35930
36015
|
width,
|
|
35931
|
-
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Tags', focused)), h(Text, { dimColor: true }, headerRight)), ...renderPromotedFilterAffordance(h, Text, state, theme), ...
|
|
36016
|
+
}, h(Box, { justifyContent: 'space-between' }, h(Text, { bold: true }, panelTitle('Tags', focused)), h(Text, { dimColor: true }, headerRight)), ...renderPromotedFilterAffordance(h, Text, state, theme), ...(tagsHasMoreAbove
|
|
36017
|
+
? [h(Text, { key: 'tags-more-above', dimColor: true }, ` ↑ ${startIndex} more above`)]
|
|
36018
|
+
: []), ...lines, ...(tagsHasMoreBelow
|
|
36019
|
+
? [h(Text, { key: 'tags-more-below', dimColor: true }, ` ↓ ${tags.length - (startIndex + listRows)} more below`)]
|
|
36020
|
+
: []));
|
|
35932
36021
|
}
|
|
35933
36022
|
|
|
35934
36023
|
/**
|
|
@@ -36447,12 +36536,17 @@ function renderInspectorActionsSection(h, Text, context, width, theme, options =
|
|
|
36447
36536
|
h(Text, { key: 'actions-title' }, cursorActive ? '[Actions]' : 'Actions:'),
|
|
36448
36537
|
...actions.map((action, index) => {
|
|
36449
36538
|
const isSelected = cursorActive && index === cursorIndex;
|
|
36539
|
+
// On the selected row, swap every span to the contrast-guaranteed
|
|
36540
|
+
// selection foreground so the key glyph / destructive marker don't
|
|
36541
|
+
// wash out against the selection bar; the row is already highlighted,
|
|
36542
|
+
// and the label text still conveys which actions are destructive.
|
|
36543
|
+
const selectedFg = isSelected && !theme.noColor ? theme.colors.selectionForeground : undefined;
|
|
36450
36544
|
const keyCell = action.key.padEnd(KEY_COLUMN);
|
|
36451
36545
|
const label = truncateCells(action.label, labelBudget);
|
|
36452
36546
|
const children = [
|
|
36453
36547
|
h(Text, {
|
|
36454
36548
|
key: `actions-${index}-key`,
|
|
36455
|
-
color: action.destructive ? theme.colors.danger : theme.colors.accent,
|
|
36549
|
+
color: selectedFg ?? (action.destructive ? theme.colors.danger : theme.colors.accent),
|
|
36456
36550
|
}, keyCell),
|
|
36457
36551
|
GAP,
|
|
36458
36552
|
label,
|
|
@@ -36460,14 +36554,14 @@ function renderInspectorActionsSection(h, Text, context, width, theme, options =
|
|
|
36460
36554
|
if (action.destructive) {
|
|
36461
36555
|
children.push(h(Text, {
|
|
36462
36556
|
key: `actions-${index}-mark`,
|
|
36463
|
-
color: theme.colors.danger,
|
|
36557
|
+
color: selectedFg ?? theme.colors.danger,
|
|
36464
36558
|
dimColor: false,
|
|
36465
36559
|
}, DESTRUCTIVE_SUFFIX));
|
|
36466
36560
|
}
|
|
36467
36561
|
return h(Text, {
|
|
36468
36562
|
key: `actions-${index}`,
|
|
36469
36563
|
backgroundColor: isSelected && !theme.noColor ? theme.colors.selection : undefined,
|
|
36470
|
-
|
|
36564
|
+
color: selectedFg,
|
|
36471
36565
|
}, ...children);
|
|
36472
36566
|
}),
|
|
36473
36567
|
];
|
|
@@ -36544,7 +36638,6 @@ function renderCommitFileList(h, Text, files, selectedIndex, focused, maxRows, w
|
|
|
36544
36638
|
return h(Text, {
|
|
36545
36639
|
key: `commit-file-${index}`,
|
|
36546
36640
|
color: statusCodeColor(file.status, theme),
|
|
36547
|
-
inverse: isSelected && focused && !theme.noColor,
|
|
36548
36641
|
bold: isSelected,
|
|
36549
36642
|
}, label);
|
|
36550
36643
|
});
|
|
@@ -41136,8 +41229,9 @@ function LogInkApp(deps) {
|
|
|
41136
41229
|
if (group.rationale)
|
|
41137
41230
|
lines += 2;
|
|
41138
41231
|
lines += (group.files?.length || 0) + 1;
|
|
41139
|
-
|
|
41140
|
-
|
|
41232
|
+
const hunkCount = group.hunks?.length || 0;
|
|
41233
|
+
if (hunkCount > 0)
|
|
41234
|
+
lines += hunkCount + 1;
|
|
41141
41235
|
return sum + lines;
|
|
41142
41236
|
}, 0)
|
|
41143
41237
|
: undefined,
|
|
@@ -41302,7 +41396,7 @@ function getColorLevel(env = process.env) {
|
|
|
41302
41396
|
return '256';
|
|
41303
41397
|
return '16';
|
|
41304
41398
|
}
|
|
41305
|
-
const TRUECOLOR_PRESETS = new Set(['catppuccin', 'gruvbox', 'dracula', 'nord', 'solarized-dark', 'tokyo-night', 'one-dark', 'rose-pine', 'kanagawa', 'everforest', 'monokai', 'synthwave', 'ayu-dark', 'palenight', 'github-dark', 'horizon', 'nightfox', 'carbonfox', 'tokyonight-storm', 'catppuccin-latte', 'solarized-light', 'github-light', 'iceberg', 'material-ocean', 'moonlight', 'poimandres', 'vitesse-dark', 'vesper', 'flexoki', 'mellow']);
|
|
41399
|
+
const TRUECOLOR_PRESETS = new Set(['catppuccin', 'gruvbox', 'dracula', 'nord', 'solarized-dark', 'tokyo-night', 'one-dark', 'rose-pine', 'kanagawa', 'everforest', 'monokai', 'synthwave', 'ayu-dark', 'palenight', 'github-dark', 'horizon', 'nightfox', 'carbonfox', 'tokyonight-storm', 'catppuccin-latte', 'solarized-light', 'github-light', 'iceberg', 'material-ocean', 'moonlight', 'poimandres', 'vitesse-dark', 'vesper', 'flexoki', 'mellow', 'night-owl', 'cobalt2', 'oceanic-next', 'catppuccin-macchiato', 'gruvbox-light', 'tokyo-night-day', 'one-light', 'ayu-light', 'rose-pine-dawn', 'everforest-light', 'vitesse-light', 'dayfox', 'night-owl-light', 'flexoki-light', 'material-lighter', 'papercolor-light', 'modus-operandi', 'quiet-light']);
|
|
41306
41400
|
/**
|
|
41307
41401
|
* `true` when the named preset relies on hex colors that look best under
|
|
41308
41402
|
* 24-bit RGB. Used by `createLogInkTheme` to decide whether to downgrade
|
|
@@ -41311,6 +41405,45 @@ const TRUECOLOR_PRESETS = new Set(['catppuccin', 'gruvbox', 'dracula', 'nord', '
|
|
|
41311
41405
|
function presetUsesTrueColor(preset) {
|
|
41312
41406
|
return preset !== undefined && TRUECOLOR_PRESETS.has(preset);
|
|
41313
41407
|
}
|
|
41408
|
+
/**
|
|
41409
|
+
* WCAG 2.x relative luminance of a `#rrggbb` color, 0 (black) … 1 (white).
|
|
41410
|
+
* Returns `null` for anything that isn't a 6-digit hex (e.g. ANSI-named
|
|
41411
|
+
* colors), so callers can fall back rather than guess.
|
|
41412
|
+
*/
|
|
41413
|
+
function relativeLuminance(hex) {
|
|
41414
|
+
const match = /^#?([0-9a-f]{6})$/i.exec(hex.trim());
|
|
41415
|
+
if (!match)
|
|
41416
|
+
return null;
|
|
41417
|
+
const int = parseInt(match[1], 16);
|
|
41418
|
+
const channel = (c) => {
|
|
41419
|
+
const x = c / 255;
|
|
41420
|
+
return x <= 0.03928 ? x / 12.92 : ((x + 0.055) / 1.055) ** 2.4;
|
|
41421
|
+
};
|
|
41422
|
+
const r = channel((int >> 16) & 0xff);
|
|
41423
|
+
const g = channel((int >> 8) & 0xff);
|
|
41424
|
+
const b = channel(int & 0xff);
|
|
41425
|
+
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
|
41426
|
+
}
|
|
41427
|
+
/**
|
|
41428
|
+
* Pick a foreground guaranteed to stay readable on `bg` — black for light
|
|
41429
|
+
* backgrounds, white for dark ones. The 0.179 threshold is the luminance
|
|
41430
|
+
* crossover where black and white yield identical contrast, so the choice
|
|
41431
|
+
* always maximizes it; every background clears WCAG AA (≥ 4.5:1).
|
|
41432
|
+
*
|
|
41433
|
+
* This is how the selected-row text stays legible across every theme:
|
|
41434
|
+
* coco controls the selection *background* but not the user's terminal
|
|
41435
|
+
* default foreground, so it must supply its own contrasting foreground
|
|
41436
|
+
* instead of hoping the terminal's happens to contrast. Returns
|
|
41437
|
+
* `undefined` for non-hex backgrounds (let the caller leave color alone).
|
|
41438
|
+
*/
|
|
41439
|
+
function readableForegroundFor(bg) {
|
|
41440
|
+
if (!bg)
|
|
41441
|
+
return undefined;
|
|
41442
|
+
const luminance = relativeLuminance(bg);
|
|
41443
|
+
if (luminance === null)
|
|
41444
|
+
return undefined;
|
|
41445
|
+
return luminance > 0.179 ? '#000000' : '#ffffff';
|
|
41446
|
+
}
|
|
41314
41447
|
|
|
41315
41448
|
const THEME_PRESET_COLORS = {
|
|
41316
41449
|
default: {
|
|
@@ -41323,7 +41456,7 @@ const THEME_PRESET_COLORS = {
|
|
|
41323
41456
|
gitModified: 'yellow',
|
|
41324
41457
|
info: 'blue',
|
|
41325
41458
|
muted: 'gray',
|
|
41326
|
-
selection: '
|
|
41459
|
+
selection: '#1a3a4a',
|
|
41327
41460
|
success: 'green',
|
|
41328
41461
|
warning: 'yellow',
|
|
41329
41462
|
},
|
|
@@ -41747,6 +41880,258 @@ const THEME_PRESET_COLORS = {
|
|
|
41747
41880
|
success: '#a3d4a0',
|
|
41748
41881
|
warning: '#f0c674',
|
|
41749
41882
|
},
|
|
41883
|
+
'night-owl': {
|
|
41884
|
+
accent: '#82aaff',
|
|
41885
|
+
border: '#1d3b53',
|
|
41886
|
+
danger: '#ef5350',
|
|
41887
|
+
focusBorder: '#7fdbca',
|
|
41888
|
+
gitAdded: '#addb67',
|
|
41889
|
+
gitDeleted: '#ef5350',
|
|
41890
|
+
gitModified: '#ecc48d',
|
|
41891
|
+
info: '#82aaff',
|
|
41892
|
+
muted: '#637777',
|
|
41893
|
+
selection: '#1d3b53',
|
|
41894
|
+
success: '#addb67',
|
|
41895
|
+
warning: '#ecc48d',
|
|
41896
|
+
},
|
|
41897
|
+
cobalt2: {
|
|
41898
|
+
accent: '#ffc600',
|
|
41899
|
+
border: '#234e6d',
|
|
41900
|
+
danger: '#ff628c',
|
|
41901
|
+
focusBorder: '#9effff',
|
|
41902
|
+
gitAdded: '#3ad900',
|
|
41903
|
+
gitDeleted: '#ff628c',
|
|
41904
|
+
gitModified: '#ffc600',
|
|
41905
|
+
info: '#9effff',
|
|
41906
|
+
muted: '#627e99',
|
|
41907
|
+
selection: '#0d3a58',
|
|
41908
|
+
success: '#3ad900',
|
|
41909
|
+
warning: '#ffc600',
|
|
41910
|
+
},
|
|
41911
|
+
'oceanic-next': {
|
|
41912
|
+
accent: '#6699cc',
|
|
41913
|
+
border: '#343d46',
|
|
41914
|
+
danger: '#ec5f67',
|
|
41915
|
+
focusBorder: '#5fb3b3',
|
|
41916
|
+
gitAdded: '#99c794',
|
|
41917
|
+
gitDeleted: '#ec5f67',
|
|
41918
|
+
gitModified: '#fac863',
|
|
41919
|
+
info: '#6699cc',
|
|
41920
|
+
muted: '#65737e',
|
|
41921
|
+
selection: '#4f5b66',
|
|
41922
|
+
success: '#99c794',
|
|
41923
|
+
warning: '#fac863',
|
|
41924
|
+
},
|
|
41925
|
+
'catppuccin-macchiato': {
|
|
41926
|
+
accent: '#8aadf4',
|
|
41927
|
+
border: '#494d64',
|
|
41928
|
+
danger: '#ed8796',
|
|
41929
|
+
focusBorder: '#91d7e3',
|
|
41930
|
+
gitAdded: '#a6da95',
|
|
41931
|
+
gitDeleted: '#ed8796',
|
|
41932
|
+
gitModified: '#eed49f',
|
|
41933
|
+
info: '#8aadf4',
|
|
41934
|
+
muted: '#6e738d',
|
|
41935
|
+
selection: '#363a4f',
|
|
41936
|
+
success: '#a6da95',
|
|
41937
|
+
warning: '#eed49f',
|
|
41938
|
+
},
|
|
41939
|
+
'gruvbox-light': {
|
|
41940
|
+
accent: '#076678',
|
|
41941
|
+
border: '#bdae93',
|
|
41942
|
+
danger: '#9d0006',
|
|
41943
|
+
focusBorder: '#427b58',
|
|
41944
|
+
gitAdded: '#79740e',
|
|
41945
|
+
gitDeleted: '#9d0006',
|
|
41946
|
+
gitModified: '#b57614',
|
|
41947
|
+
info: '#076678',
|
|
41948
|
+
muted: '#7c6f64',
|
|
41949
|
+
selection: '#ebdbb2',
|
|
41950
|
+
success: '#79740e',
|
|
41951
|
+
warning: '#b57614',
|
|
41952
|
+
},
|
|
41953
|
+
'tokyo-night-day': {
|
|
41954
|
+
accent: '#2e7de9',
|
|
41955
|
+
border: '#b7c1e3',
|
|
41956
|
+
danger: '#f52a65',
|
|
41957
|
+
focusBorder: '#007197',
|
|
41958
|
+
gitAdded: '#587539',
|
|
41959
|
+
gitDeleted: '#f52a65',
|
|
41960
|
+
gitModified: '#8c6c3e',
|
|
41961
|
+
info: '#2e7de9',
|
|
41962
|
+
muted: '#848cb5',
|
|
41963
|
+
selection: '#b7c1e3',
|
|
41964
|
+
success: '#587539',
|
|
41965
|
+
warning: '#8c6c3e',
|
|
41966
|
+
},
|
|
41967
|
+
'one-light': {
|
|
41968
|
+
accent: '#4078f2',
|
|
41969
|
+
border: '#d4d4d4',
|
|
41970
|
+
danger: '#e45649',
|
|
41971
|
+
focusBorder: '#0184bc',
|
|
41972
|
+
gitAdded: '#50a14f',
|
|
41973
|
+
gitDeleted: '#e45649',
|
|
41974
|
+
gitModified: '#c18401',
|
|
41975
|
+
info: '#4078f2',
|
|
41976
|
+
muted: '#a0a1a7',
|
|
41977
|
+
selection: '#e5e5e6',
|
|
41978
|
+
success: '#50a14f',
|
|
41979
|
+
warning: '#c18401',
|
|
41980
|
+
},
|
|
41981
|
+
'ayu-light': {
|
|
41982
|
+
accent: '#fa8d3e',
|
|
41983
|
+
border: '#e6e6e6',
|
|
41984
|
+
danger: '#e65050',
|
|
41985
|
+
focusBorder: '#4cbf99',
|
|
41986
|
+
gitAdded: '#6cbf43',
|
|
41987
|
+
gitDeleted: '#e65050',
|
|
41988
|
+
gitModified: '#f2ae49',
|
|
41989
|
+
info: '#399ee6',
|
|
41990
|
+
muted: '#abb0b6',
|
|
41991
|
+
selection: '#d1e4f4',
|
|
41992
|
+
success: '#6cbf43',
|
|
41993
|
+
warning: '#f2ae49',
|
|
41994
|
+
},
|
|
41995
|
+
'rose-pine-dawn': {
|
|
41996
|
+
accent: '#907aa9',
|
|
41997
|
+
border: '#dfdad9',
|
|
41998
|
+
danger: '#b4637a',
|
|
41999
|
+
focusBorder: '#56949f',
|
|
42000
|
+
gitAdded: '#286983',
|
|
42001
|
+
gitDeleted: '#b4637a',
|
|
42002
|
+
gitModified: '#ea9d34',
|
|
42003
|
+
info: '#56949f',
|
|
42004
|
+
muted: '#9893a5',
|
|
42005
|
+
selection: '#dfdad9',
|
|
42006
|
+
success: '#286983',
|
|
42007
|
+
warning: '#ea9d34',
|
|
42008
|
+
},
|
|
42009
|
+
'everforest-light': {
|
|
42010
|
+
accent: '#8da101',
|
|
42011
|
+
border: '#ddd8be',
|
|
42012
|
+
danger: '#f85552',
|
|
42013
|
+
focusBorder: '#35a77c',
|
|
42014
|
+
gitAdded: '#8da101',
|
|
42015
|
+
gitDeleted: '#f85552',
|
|
42016
|
+
gitModified: '#dfa000',
|
|
42017
|
+
info: '#3a94c5',
|
|
42018
|
+
muted: '#939f91',
|
|
42019
|
+
selection: '#edeada',
|
|
42020
|
+
success: '#8da101',
|
|
42021
|
+
warning: '#dfa000',
|
|
42022
|
+
},
|
|
42023
|
+
'vitesse-light': {
|
|
42024
|
+
accent: '#1e754f',
|
|
42025
|
+
border: '#e0e0e0',
|
|
42026
|
+
danger: '#ab5959',
|
|
42027
|
+
focusBorder: '#2993a3',
|
|
42028
|
+
gitAdded: '#1e754f',
|
|
42029
|
+
gitDeleted: '#ab5959',
|
|
42030
|
+
gitModified: '#b07d48',
|
|
42031
|
+
info: '#296aa3',
|
|
42032
|
+
muted: '#999fa6',
|
|
42033
|
+
selection: '#eaeaeb',
|
|
42034
|
+
success: '#1e754f',
|
|
42035
|
+
warning: '#b07d48',
|
|
42036
|
+
},
|
|
42037
|
+
dayfox: {
|
|
42038
|
+
accent: '#2848a9',
|
|
42039
|
+
border: '#e4dcd4',
|
|
42040
|
+
danger: '#a5222f',
|
|
42041
|
+
focusBorder: '#287980',
|
|
42042
|
+
gitAdded: '#396847',
|
|
42043
|
+
gitDeleted: '#a5222f',
|
|
42044
|
+
gitModified: '#ac5402',
|
|
42045
|
+
info: '#2848a9',
|
|
42046
|
+
muted: '#908479',
|
|
42047
|
+
selection: '#e7d2be',
|
|
42048
|
+
success: '#396847',
|
|
42049
|
+
warning: '#ac5402',
|
|
42050
|
+
},
|
|
42051
|
+
'night-owl-light': {
|
|
42052
|
+
accent: '#288ed7',
|
|
42053
|
+
border: '#d9d9d9',
|
|
42054
|
+
danger: '#d3423e',
|
|
42055
|
+
focusBorder: '#2aa298',
|
|
42056
|
+
gitAdded: '#08916a',
|
|
42057
|
+
gitDeleted: '#d3423e',
|
|
42058
|
+
gitModified: '#daaa01',
|
|
42059
|
+
info: '#288ed7',
|
|
42060
|
+
muted: '#989fb1',
|
|
42061
|
+
selection: '#e4e8f0',
|
|
42062
|
+
success: '#08916a',
|
|
42063
|
+
warning: '#daaa01',
|
|
42064
|
+
},
|
|
42065
|
+
'flexoki-light': {
|
|
42066
|
+
accent: '#205ea6',
|
|
42067
|
+
border: '#cecdc3',
|
|
42068
|
+
danger: '#af3029',
|
|
42069
|
+
focusBorder: '#24837b',
|
|
42070
|
+
gitAdded: '#66800b',
|
|
42071
|
+
gitDeleted: '#af3029',
|
|
42072
|
+
gitModified: '#ad8301',
|
|
42073
|
+
info: '#205ea6',
|
|
42074
|
+
muted: '#6f6e69',
|
|
42075
|
+
selection: '#e6e4d9',
|
|
42076
|
+
success: '#66800b',
|
|
42077
|
+
warning: '#ad8301',
|
|
42078
|
+
},
|
|
42079
|
+
'material-lighter': {
|
|
42080
|
+
accent: '#39adb5',
|
|
42081
|
+
border: '#e7eaec',
|
|
42082
|
+
danger: '#e53935',
|
|
42083
|
+
focusBorder: '#39adb5',
|
|
42084
|
+
gitAdded: '#91b859',
|
|
42085
|
+
gitDeleted: '#e53935',
|
|
42086
|
+
gitModified: '#f6a434',
|
|
42087
|
+
info: '#6182b8',
|
|
42088
|
+
muted: '#90a4ae',
|
|
42089
|
+
selection: '#d3e1e8',
|
|
42090
|
+
success: '#91b859',
|
|
42091
|
+
warning: '#f6a434',
|
|
42092
|
+
},
|
|
42093
|
+
'papercolor-light': {
|
|
42094
|
+
accent: '#0087af',
|
|
42095
|
+
border: '#d7d7d7',
|
|
42096
|
+
danger: '#af0000',
|
|
42097
|
+
focusBorder: '#005f87',
|
|
42098
|
+
gitAdded: '#008700',
|
|
42099
|
+
gitDeleted: '#af0000',
|
|
42100
|
+
gitModified: '#d75f00',
|
|
42101
|
+
info: '#0087af',
|
|
42102
|
+
muted: '#878787',
|
|
42103
|
+
selection: '#d0d0d0',
|
|
42104
|
+
success: '#008700',
|
|
42105
|
+
warning: '#d75f00',
|
|
42106
|
+
},
|
|
42107
|
+
'modus-operandi': {
|
|
42108
|
+
accent: '#0031a9',
|
|
42109
|
+
border: '#d7d7d7',
|
|
42110
|
+
danger: '#a60000',
|
|
42111
|
+
focusBorder: '#005e8b',
|
|
42112
|
+
gitAdded: '#006800',
|
|
42113
|
+
gitDeleted: '#a60000',
|
|
42114
|
+
gitModified: '#6f5500',
|
|
42115
|
+
info: '#0031a9',
|
|
42116
|
+
muted: '#595959',
|
|
42117
|
+
selection: '#c0deff',
|
|
42118
|
+
success: '#006800',
|
|
42119
|
+
warning: '#6f5500',
|
|
42120
|
+
},
|
|
42121
|
+
'quiet-light': {
|
|
42122
|
+
accent: '#4b83cd',
|
|
42123
|
+
border: '#e0e0e0',
|
|
42124
|
+
danger: '#aa3731',
|
|
42125
|
+
focusBorder: '#4b83cd',
|
|
42126
|
+
gitAdded: '#448c27',
|
|
42127
|
+
gitDeleted: '#aa3731',
|
|
42128
|
+
gitModified: '#a67d00',
|
|
42129
|
+
info: '#4b83cd',
|
|
42130
|
+
muted: '#a3a6ad',
|
|
42131
|
+
selection: '#c9d0d9',
|
|
42132
|
+
success: '#448c27',
|
|
42133
|
+
warning: '#a67d00',
|
|
42134
|
+
},
|
|
41750
42135
|
};
|
|
41751
42136
|
function shouldUseAscii(term) {
|
|
41752
42137
|
if (!term) {
|
|
@@ -41771,8 +42156,28 @@ function createLogInkTheme(options = {}) {
|
|
|
41771
42156
|
? {}
|
|
41772
42157
|
: {
|
|
41773
42158
|
...THEME_PRESET_COLORS[preset],
|
|
42159
|
+
// Preserve the requested theme's selection background even when the
|
|
42160
|
+
// rest of the palette downgrades to `default`. The selection is a
|
|
42161
|
+
// single background color the terminal can approximate; without this,
|
|
42162
|
+
// a light theme inherits `default`'s dark selection (#1a3a4a) and the
|
|
42163
|
+
// selected row renders as a dark bar on a light background.
|
|
42164
|
+
...(preset !== requestedPreset && THEME_PRESET_COLORS[requestedPreset]?.selection
|
|
42165
|
+
? { selection: THEME_PRESET_COLORS[requestedPreset].selection }
|
|
42166
|
+
: {}),
|
|
41774
42167
|
...options.colors,
|
|
41775
42168
|
};
|
|
42169
|
+
// Derive a contrasting foreground for the selected row from its own
|
|
42170
|
+
// selection background, unless the caller supplied one explicitly. coco
|
|
42171
|
+
// owns the selection background but not the terminal's default foreground,
|
|
42172
|
+
// so without this the selected row's text falls back to whatever the
|
|
42173
|
+
// user's terminal foreground is — which may not contrast with the bar at
|
|
42174
|
+
// all (the bug behind unreadable selected rows on many themes).
|
|
42175
|
+
if (!noColor && colors.selection && !colors.selectionForeground) {
|
|
42176
|
+
const selectionForeground = readableForegroundFor(colors.selection);
|
|
42177
|
+
if (selectionForeground) {
|
|
42178
|
+
colors.selectionForeground = selectionForeground;
|
|
42179
|
+
}
|
|
42180
|
+
}
|
|
41776
42181
|
return {
|
|
41777
42182
|
noColor,
|
|
41778
42183
|
ascii,
|
|
@@ -43401,7 +43806,7 @@ const options$1 = {
|
|
|
43401
43806
|
},
|
|
43402
43807
|
theme: {
|
|
43403
43808
|
description: 'TUI theme preset',
|
|
43404
|
-
choices: ['default', 'monochrome', 'catppuccin', 'gruvbox', 'dracula', 'nord', 'solarized-dark', 'tokyo-night', 'one-dark', 'rose-pine', 'kanagawa', 'everforest', 'monokai', 'synthwave', 'ayu-dark', 'palenight', 'github-dark', 'horizon', 'nightfox', 'carbonfox', 'tokyonight-storm', 'catppuccin-latte', 'solarized-light', 'github-light', 'iceberg', 'material-ocean', 'moonlight', 'poimandres', 'vitesse-dark', 'vesper', 'flexoki', 'mellow'],
|
|
43809
|
+
choices: ['default', 'monochrome', 'catppuccin', 'gruvbox', 'dracula', 'nord', 'solarized-dark', 'tokyo-night', 'one-dark', 'rose-pine', 'kanagawa', 'everforest', 'monokai', 'synthwave', 'ayu-dark', 'palenight', 'github-dark', 'horizon', 'nightfox', 'carbonfox', 'tokyonight-storm', 'catppuccin-latte', 'solarized-light', 'github-light', 'iceberg', 'material-ocean', 'moonlight', 'poimandres', 'vitesse-dark', 'vesper', 'flexoki', 'mellow', 'night-owl', 'cobalt2', 'oceanic-next', 'catppuccin-macchiato', 'gruvbox-light', 'tokyo-night-day', 'one-light', 'ayu-light', 'rose-pine-dawn', 'everforest-light', 'vitesse-light', 'dayfox', 'night-owl-light', 'flexoki-light', 'material-lighter', 'papercolor-light', 'modus-operandi', 'quiet-light'],
|
|
43405
43810
|
},
|
|
43406
43811
|
};
|
|
43407
43812
|
const builder$1 = (yargs) => {
|
|
@@ -43429,7 +43834,7 @@ const options = {
|
|
|
43429
43834
|
},
|
|
43430
43835
|
theme: {
|
|
43431
43836
|
description: 'TUI theme preset',
|
|
43432
|
-
choices: ['default', 'monochrome', 'catppuccin', 'gruvbox', 'dracula', 'nord', 'solarized-dark', 'tokyo-night', 'one-dark', 'rose-pine', 'kanagawa', 'everforest', 'monokai', 'synthwave', 'ayu-dark', 'palenight', 'github-dark', 'horizon', 'nightfox', 'carbonfox', 'tokyonight-storm', 'catppuccin-latte', 'solarized-light', 'github-light', 'iceberg', 'material-ocean', 'moonlight', 'poimandres', 'vitesse-dark', 'vesper', 'flexoki', 'mellow'],
|
|
43837
|
+
choices: ['default', 'monochrome', 'catppuccin', 'gruvbox', 'dracula', 'nord', 'solarized-dark', 'tokyo-night', 'one-dark', 'rose-pine', 'kanagawa', 'everforest', 'monokai', 'synthwave', 'ayu-dark', 'palenight', 'github-dark', 'horizon', 'nightfox', 'carbonfox', 'tokyonight-storm', 'catppuccin-latte', 'solarized-light', 'github-light', 'iceberg', 'material-ocean', 'moonlight', 'poimandres', 'vitesse-dark', 'vesper', 'flexoki', 'mellow', 'night-owl', 'cobalt2', 'oceanic-next', 'catppuccin-macchiato', 'gruvbox-light', 'tokyo-night-day', 'one-light', 'ayu-light', 'rose-pine-dawn', 'everforest-light', 'vitesse-light', 'dayfox', 'night-owl-light', 'flexoki-light', 'material-lighter', 'papercolor-light', 'modus-operandi', 'quiet-light'],
|
|
43433
43838
|
},
|
|
43434
43839
|
};
|
|
43435
43840
|
const builder = (yargs) => {
|
|
@@ -44713,33 +45118,48 @@ function buildWorkspaceHeaderChips(state, options = { focusLabel: 'List' }) {
|
|
|
44713
45118
|
});
|
|
44714
45119
|
return chips;
|
|
44715
45120
|
}
|
|
44716
|
-
// Footer hints
|
|
44717
|
-
//
|
|
44718
|
-
//
|
|
44719
|
-
//
|
|
44720
|
-
|
|
44721
|
-
|
|
44722
|
-
|
|
44723
|
-
const
|
|
44724
|
-
const
|
|
44725
|
-
|
|
45121
|
+
// Footer hints are split into two slots — same pattern as `coco ui`:
|
|
45122
|
+
// - contextual : per-mode actions (sort, filter, refresh, add/remove)
|
|
45123
|
+
// - global : always-on essentials (help, quit) — never crowded out
|
|
45124
|
+
//
|
|
45125
|
+
// The contextual slot drops bindings users can find via the help
|
|
45126
|
+
// overlay (arrow keys, tab); the global slot is the safety net so
|
|
45127
|
+
// `? help` and `q quit` never disappear.
|
|
45128
|
+
const LIST_CONTEXTUAL = ['s sort', '/ filter', 'r/R refresh', 'a add', 'd remove'];
|
|
45129
|
+
const SIDEBAR_CONTEXTUAL = ['↑/↓ cycle tab', 'enter open'];
|
|
45130
|
+
const FILTER_CONTEXTUAL = ['type to filter', 'enter apply', 'esc cancel'];
|
|
45131
|
+
const ADD_REPO_CONTEXTUAL = ['type path', 'tab to complete', 'enter to add', 'esc to cancel'];
|
|
45132
|
+
const CONFIRM_DELETE_CONTEXTUAL = ['y confirm', 'any other key cancels'];
|
|
45133
|
+
const GLOBAL_HINTS = ['? help', 'q quit'];
|
|
45134
|
+
function contextualHintsFor(focus) {
|
|
44726
45135
|
switch (focus) {
|
|
44727
45136
|
case 'sidebar':
|
|
44728
|
-
return
|
|
45137
|
+
return SIDEBAR_CONTEXTUAL;
|
|
44729
45138
|
case 'filter':
|
|
44730
|
-
return
|
|
45139
|
+
return FILTER_CONTEXTUAL;
|
|
44731
45140
|
case 'add-repo':
|
|
44732
|
-
return
|
|
45141
|
+
return ADD_REPO_CONTEXTUAL;
|
|
44733
45142
|
case 'confirm-delete':
|
|
44734
|
-
return
|
|
45143
|
+
return CONFIRM_DELETE_CONTEXTUAL;
|
|
44735
45144
|
case 'list':
|
|
44736
45145
|
default:
|
|
44737
|
-
return
|
|
45146
|
+
return LIST_CONTEXTUAL;
|
|
44738
45147
|
}
|
|
44739
45148
|
}
|
|
44740
45149
|
function buildWorkspaceFooter(state) {
|
|
45150
|
+
const contextual = contextualHintsFor(state.focus);
|
|
45151
|
+
// Modal modes (filter / add-repo / confirm-delete) suppress the
|
|
45152
|
+
// global hints — those bindings are not reachable while a prompt
|
|
45153
|
+
// is open and showing them would be misleading.
|
|
45154
|
+
const isModal = state.focus === 'filter' ||
|
|
45155
|
+
state.focus === 'add-repo' ||
|
|
45156
|
+
state.focus === 'confirm-delete';
|
|
45157
|
+
const global = isModal ? [] : GLOBAL_HINTS;
|
|
45158
|
+
const allHints = [...contextual, ...global];
|
|
44741
45159
|
return {
|
|
44742
|
-
hint:
|
|
45160
|
+
hint: allHints.join(' · '),
|
|
45161
|
+
contextual,
|
|
45162
|
+
global,
|
|
44743
45163
|
status: state.status,
|
|
44744
45164
|
filterMode: state.focus === 'filter',
|
|
44745
45165
|
};
|
|
@@ -44749,11 +45169,25 @@ function buildWorkspaceFooter(state) {
|
|
|
44749
45169
|
* sections so users can scan by intent ("how do I navigate?" "how
|
|
44750
45170
|
* do I act?") rather than reading a flat alphabetized list.
|
|
44751
45171
|
*
|
|
45172
|
+
* Section order mirrors `coco ui`'s help convention — Essentials
|
|
45173
|
+
* first so newcomers see `?`/`esc`/`q` immediately, then move outward
|
|
45174
|
+
* to navigation, modification, and the destructive verbs last.
|
|
45175
|
+
*
|
|
44752
45176
|
* The view layer composes these into a panel with section titles,
|
|
44753
45177
|
* a leading app/title bar, and a closing hint at the bottom.
|
|
44754
45178
|
*/
|
|
44755
45179
|
function buildWorkspaceHelpSections() {
|
|
44756
45180
|
return [
|
|
45181
|
+
{
|
|
45182
|
+
title: 'Essentials',
|
|
45183
|
+
subtitle: 'The keys you reach for most often.',
|
|
45184
|
+
rows: [
|
|
45185
|
+
{ glyph: '?', keys: '?', description: 'Toggle this help overlay' },
|
|
45186
|
+
{ glyph: '⎋', keys: 'esc', description: 'Clear filter / close overlay / cancel prompt' },
|
|
45187
|
+
{ glyph: '◴', keys: 'q · ctrl+c', description: 'Quit the workspace surface' },
|
|
45188
|
+
{ glyph: '↵', keys: 'enter', description: 'Drill into the cursored repo (coco ui)' },
|
|
45189
|
+
],
|
|
45190
|
+
},
|
|
44757
45191
|
{
|
|
44758
45192
|
title: 'Navigate',
|
|
44759
45193
|
subtitle: 'Move the cursor and switch focus between panels.',
|
|
@@ -44763,7 +45197,6 @@ function buildWorkspaceHelpSections() {
|
|
|
44763
45197
|
{ glyph: '←', keys: 'h', description: 'Jump focus to the sidebar' },
|
|
44764
45198
|
{ glyph: '→', keys: 'l', description: 'Jump focus to the list' },
|
|
44765
45199
|
{ glyph: '⤒', keys: 'g / G', description: 'Jump to top / bottom of the list' },
|
|
44766
|
-
{ glyph: '↵', keys: 'enter', description: 'Drill into the cursored repo (coco ui)' },
|
|
44767
45200
|
],
|
|
44768
45201
|
},
|
|
44769
45202
|
{
|
|
@@ -44783,14 +45216,6 @@ function buildWorkspaceHelpSections() {
|
|
|
44783
45216
|
{ glyph: '✕', keys: 'd', description: 'Remove the cursored repo from the known-repos store' },
|
|
44784
45217
|
],
|
|
44785
45218
|
},
|
|
44786
|
-
{
|
|
44787
|
-
title: 'General',
|
|
44788
|
-
rows: [
|
|
44789
|
-
{ glyph: '?', keys: '?', description: 'Toggle this help overlay' },
|
|
44790
|
-
{ glyph: '⎋', keys: 'esc', description: 'Clear filter / close overlay / cancel prompt' },
|
|
44791
|
-
{ glyph: '◴', keys: 'q · ctrl+c', description: 'Quit the workspace surface' },
|
|
44792
|
-
],
|
|
44793
|
-
},
|
|
44794
45219
|
];
|
|
44795
45220
|
}
|
|
44796
45221
|
function buildWorkspaceOnboarding(state) {
|
|
@@ -45237,13 +45662,19 @@ function renderFooter(deps) {
|
|
|
45237
45662
|
// height shifted by a row every time a status banner came and went,
|
|
45238
45663
|
// forcing the panel chrome to reflow.
|
|
45239
45664
|
const statusContent = model.status ?? '';
|
|
45665
|
+
const contextualText = model.contextual.join(' ');
|
|
45666
|
+
const globalText = model.global.join(' · ');
|
|
45240
45667
|
return React.createElement(Box, {
|
|
45241
45668
|
borderColor: focusBorderColor(theme, false),
|
|
45242
45669
|
borderStyle: theme.borderStyle,
|
|
45243
45670
|
paddingX: 1,
|
|
45244
45671
|
flexDirection: 'column',
|
|
45245
45672
|
height: FOOTER_HEIGHT,
|
|
45246
|
-
},
|
|
45673
|
+
},
|
|
45674
|
+
// Row 1: contextual ↔ global hints. justifyContent pushes them
|
|
45675
|
+
// to opposite edges so the eye scans each cluster as one block —
|
|
45676
|
+
// same shape as `coco ui`'s footer post-0.54.2 redesign.
|
|
45677
|
+
React.createElement(Box, { flexDirection: 'row', justifyContent: 'space-between' }, React.createElement(Text, { dimColor: true }, contextualText), React.createElement(Text, { dimColor: true }, globalText)), React.createElement(Text, {
|
|
45247
45678
|
color: model.status ? toneColor('warn', theme) : undefined,
|
|
45248
45679
|
dimColor: !model.status,
|
|
45249
45680
|
}, statusContent || ' '));
|
|
@@ -46546,6 +46977,148 @@ var workspace = {
|
|
|
46546
46977
|
options,
|
|
46547
46978
|
};
|
|
46548
46979
|
|
|
46980
|
+
/**
|
|
46981
|
+
* Default-command router for `coco` invoked with no positional
|
|
46982
|
+
* arguments. Decides where to send the user based on the state of
|
|
46983
|
+
* their machine:
|
|
46984
|
+
*
|
|
46985
|
+
* ┌─────────────────────────┬─────────────────────┬──────────────┐
|
|
46986
|
+
* │ Config present? │ In a git repo? │ Action │
|
|
46987
|
+
* ├─────────────────────────┼─────────────────────┼──────────────┤
|
|
46988
|
+
* │ No (default-only) │ — │ run `init` │
|
|
46989
|
+
* │ Yes │ Yes (worktree) │ run `ui` │
|
|
46990
|
+
* │ Yes │ No │ run `ws` │
|
|
46991
|
+
* └─────────────────────────┴─────────────────────┴──────────────┘
|
|
46992
|
+
*
|
|
46993
|
+
* The pre-existing default — fall through to `commit` — was hostile
|
|
46994
|
+
* to first-time users: a fresh install with no config landed
|
|
46995
|
+
* straight in the API-key error path, with no hint that `coco init`
|
|
46996
|
+
* was the right next step. Routing fresh installs to `init` and
|
|
46997
|
+
* configured users to the workstation/UI matches what every other
|
|
46998
|
+
* git-aware CLI does (lazygit, tig, gitui all open their TUI on bare
|
|
46999
|
+
* invocation).
|
|
47000
|
+
*
|
|
47001
|
+
* `coco commit` keeps its dedicated subcommand entry so existing
|
|
47002
|
+
* scripts (`git aliases`, hook integrations, CI jobs) that call
|
|
47003
|
+
* `coco commit` continue to work unchanged.
|
|
47004
|
+
*
|
|
47005
|
+
* The router is a thin shim — it forwards to the existing handlers
|
|
47006
|
+
* via their public exports rather than re-implementing the logic.
|
|
47007
|
+
*/
|
|
47008
|
+
/**
|
|
47009
|
+
* Pure decision function — given probed signals (whether config
|
|
47010
|
+
* exists, whether the current directory is a git repo, whether the
|
|
47011
|
+
* user opted into legacy commit-by-default), decides which command
|
|
47012
|
+
* to invoke. Kept pure so unit tests can cover every quadrant of
|
|
47013
|
+
* the router table without spawning processes.
|
|
47014
|
+
*
|
|
47015
|
+
* "Config exists" is defined as: the loader detected at least one
|
|
47016
|
+
* source beyond `default` — i.e., the user has either a project
|
|
47017
|
+
* config, a git config `[coco]` section, an env var, or an XDG
|
|
47018
|
+
* config. A pure-defaults run is treated as "never been configured"
|
|
47019
|
+
* because `coco init` is the only way to populate any of those
|
|
47020
|
+
* sources.
|
|
47021
|
+
*/
|
|
47022
|
+
function decideDefaultRoute(input) {
|
|
47023
|
+
if (input.envOverride === 'commit' || input.explicitCommit) {
|
|
47024
|
+
return {
|
|
47025
|
+
kind: 'commit',
|
|
47026
|
+
reason: input.envOverride === 'commit' ? 'env-override' : 'explicit-flag',
|
|
47027
|
+
};
|
|
47028
|
+
}
|
|
47029
|
+
if (!input.hasConfigSource) {
|
|
47030
|
+
return { kind: 'init', reason: 'no-config' };
|
|
47031
|
+
}
|
|
47032
|
+
if (input.isGitRepo) {
|
|
47033
|
+
return { kind: 'ui', reason: 'config-and-repo' };
|
|
47034
|
+
}
|
|
47035
|
+
return { kind: 'workspace', reason: 'config-no-repo' };
|
|
47036
|
+
}
|
|
47037
|
+
/**
|
|
47038
|
+
* Probe whether the cwd (after `--repo` is honored) is inside a git
|
|
47039
|
+
* worktree. Tolerant of every error class — a thrown simple-git
|
|
47040
|
+
* call should never block the router; it should fall back to
|
|
47041
|
+
* "not a repo" so the user lands somewhere sensible (workspace
|
|
47042
|
+
* surface) rather than crashing on an empty machine.
|
|
47043
|
+
*/
|
|
47044
|
+
async function probeIsGitRepo() {
|
|
47045
|
+
try {
|
|
47046
|
+
// Lazy-import simple-git so the cold-start path stays fast for
|
|
47047
|
+
// users running `coco --help` / `coco doctor` etc.
|
|
47048
|
+
const { default: simpleGit } = await import('simple-git');
|
|
47049
|
+
const git = simpleGit();
|
|
47050
|
+
return await git.checkIsRepo();
|
|
47051
|
+
}
|
|
47052
|
+
catch {
|
|
47053
|
+
return false;
|
|
47054
|
+
}
|
|
47055
|
+
}
|
|
47056
|
+
/**
|
|
47057
|
+
* Build a synthetic argv for one of the targeted handlers. Each
|
|
47058
|
+
* handler reads its own typed argv contract (`CommitArgv`,
|
|
47059
|
+
* `InitArgv`, `UiArgv`, `WorkspaceArgv`) so we can't just spread the
|
|
47060
|
+
* raw default argv — we have to project the shared fields and let
|
|
47061
|
+
* the handler fill in command-specific defaults.
|
|
47062
|
+
*/
|
|
47063
|
+
function buildSyntheticArgv(argv) {
|
|
47064
|
+
return {
|
|
47065
|
+
_: ['$0'],
|
|
47066
|
+
$0: argv.$0,
|
|
47067
|
+
repo: argv.repo,
|
|
47068
|
+
cwd: argv.cwd,
|
|
47069
|
+
verbose: argv.verbose ?? false,
|
|
47070
|
+
interactive: true,
|
|
47071
|
+
version: false,
|
|
47072
|
+
help: false,
|
|
47073
|
+
};
|
|
47074
|
+
}
|
|
47075
|
+
/**
|
|
47076
|
+
* Default-command handler installed under yargs's `$0` slot. Probes
|
|
47077
|
+
* the environment, computes the right route, and forwards to the
|
|
47078
|
+
* matching command handler. Falls through to commit if any
|
|
47079
|
+
* unexpected error blocks routing — preserves backwards-compat
|
|
47080
|
+
* for users on weird setups while still giving newcomers the
|
|
47081
|
+
* onboarding path they deserve.
|
|
47082
|
+
*/
|
|
47083
|
+
const defaultRouteHandler = async (argv, logger) => {
|
|
47084
|
+
// The `--repo` flag has to land before any probe runs — otherwise
|
|
47085
|
+
// we'd sniff the launcher's cwd instead of the targeted repo.
|
|
47086
|
+
applyRepoCwd(argv);
|
|
47087
|
+
// Trigger a config load so `getConfigSources()` returns the active
|
|
47088
|
+
// source list. We discard the config object — the decision only
|
|
47089
|
+
// cares about which sources contributed.
|
|
47090
|
+
void loadConfig(argv);
|
|
47091
|
+
const sources = getConfigSources();
|
|
47092
|
+
const hasConfigSource = sources.some((source) => source.source !== 'default');
|
|
47093
|
+
const isGitRepo = await probeIsGitRepo();
|
|
47094
|
+
const decision = decideDefaultRoute({
|
|
47095
|
+
hasConfigSource,
|
|
47096
|
+
isGitRepo,
|
|
47097
|
+
explicitCommit: Boolean(argv.commit),
|
|
47098
|
+
envOverride: process.env.COCO_DEFAULT,
|
|
47099
|
+
});
|
|
47100
|
+
switch (decision.kind) {
|
|
47101
|
+
case 'init':
|
|
47102
|
+
// Friendly hint before the wizard kicks in — sets expectations
|
|
47103
|
+
// that the user is being walked through setup, not silently
|
|
47104
|
+
// routed to a different command.
|
|
47105
|
+
logger.log('No coco config detected — running `coco init` to set up your provider + key.', { color: 'cyan' });
|
|
47106
|
+
logger.log('');
|
|
47107
|
+
await handler$7(buildSyntheticArgv(argv), logger);
|
|
47108
|
+
return;
|
|
47109
|
+
case 'ui':
|
|
47110
|
+
await handler$5(buildSyntheticArgv(argv));
|
|
47111
|
+
return;
|
|
47112
|
+
case 'workspace':
|
|
47113
|
+
await handler(buildSyntheticArgv(argv));
|
|
47114
|
+
return;
|
|
47115
|
+
case 'commit':
|
|
47116
|
+
default:
|
|
47117
|
+
await handler$9(buildSyntheticArgv(argv), logger);
|
|
47118
|
+
return;
|
|
47119
|
+
}
|
|
47120
|
+
};
|
|
47121
|
+
|
|
46549
47122
|
var types = /*#__PURE__*/Object.freeze({
|
|
46550
47123
|
__proto__: null
|
|
46551
47124
|
});
|
|
@@ -46564,7 +47137,37 @@ y.option('repo', {
|
|
|
46564
47137
|
description: 'Target a specific repository directory instead of the current working directory.',
|
|
46565
47138
|
global: true,
|
|
46566
47139
|
});
|
|
46567
|
-
|
|
47140
|
+
// Global `--verbose` (alias `-v`) — every subcommand inherits it.
|
|
47141
|
+
// Flips `argv.verbose: true` so `commandExecutor` and `Logger` print
|
|
47142
|
+
// stack traces / debug spans. Previously only settable via the
|
|
47143
|
+
// `COCO_VERBOSE=true` env var or `coco.verbose` git/json config —
|
|
47144
|
+
// `BaseArgvOptions.verbose` was typed but never declared as a yargs
|
|
47145
|
+
// option, so passing `--verbose` from the CLI was a silent no-op.
|
|
47146
|
+
y.option('verbose', {
|
|
47147
|
+
type: 'boolean',
|
|
47148
|
+
alias: 'v',
|
|
47149
|
+
description: 'Print verbose diagnostic output (stack traces on errors, debug spans).',
|
|
47150
|
+
default: false,
|
|
47151
|
+
global: true,
|
|
47152
|
+
});
|
|
47153
|
+
// `$0` (no positional args) routes through the smart default router
|
|
47154
|
+
// rather than aliasing directly to `coco commit`. The router probes
|
|
47155
|
+
// the user's environment (config presence, git-repo presence) and
|
|
47156
|
+
// forwards to `init` / `ui` / `workspace` / `commit` based on which
|
|
47157
|
+
// of those is most likely to be helpful. Mirrors what other modern
|
|
47158
|
+
// git-aware CLIs do (lazygit / tig / gitui) — fresh installs land in
|
|
47159
|
+
// a setup wizard, configured users land in the TUI, scripts that
|
|
47160
|
+
// rely on `coco commit` keep their dedicated subcommand entry.
|
|
47161
|
+
y.command('$0', 'Smart entry point — routes to init / ui / workspace / commit based on your environment.', (yargs) => yargs.option('commit', {
|
|
47162
|
+
type: 'boolean',
|
|
47163
|
+
description: 'Force the legacy default — run `coco commit` regardless of routing.',
|
|
47164
|
+
default: false,
|
|
47165
|
+
}),
|
|
47166
|
+
// `commandExecutor` wraps every command with config loading, error
|
|
47167
|
+
// formatting, and exit-code handling. The router is a regular
|
|
47168
|
+
// command so it lights up the same plumbing for free.
|
|
47169
|
+
commandExecutor(defaultRouteHandler));
|
|
47170
|
+
y.command(commit.command, commit.desc, commit.builder, commit.handler);
|
|
46568
47171
|
y.command(changelog.command, changelog.desc, changelog.builder, changelog.handler);
|
|
46569
47172
|
y.command(recap.command, recap.desc, recap.builder, recap.handler);
|
|
46570
47173
|
y.command(review.command, review.desc, review.builder, review.handler);
|