@litfamily/litopencode 1.0.31 → 1.0.32
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/CHANGELOG.md +24 -0
- package/README-Ko-KR.md +6 -6
- package/README.md +6 -6
- package/dist/activation-probe.js +1 -0
- package/dist/activation-prompt-utils.d.ts +1 -1
- package/dist/activation-reentry.d.ts +20 -0
- package/dist/activation-reentry.js +110 -1
- package/dist/activation-routing.d.ts +2 -1
- package/dist/activation-routing.js +100 -11
- package/dist/activation-workflow-prompts.d.ts +4 -0
- package/dist/activation-workflow-prompts.js +30 -0
- package/dist/activation.d.ts +1 -1
- package/dist/activation.js +44 -5
- package/dist/adhd-next.d.ts +51 -0
- package/dist/adhd-next.js +150 -0
- package/dist/cli/managed-skill-assets.d.ts +1 -1
- package/dist/cli/managed-skill-assets.js +7 -1
- package/dist/cli/native-skill-integrity.js +1 -1
- package/dist/cli.js +7 -1
- package/dist/commands.d.ts +28 -1
- package/dist/commands.js +48 -1
- package/dist/features.d.ts +80 -1
- package/dist/features.js +105 -1
- package/dist/ledger.js +12 -1
- package/dist/secret-shapes.js +5 -5
- package/dist/skills.d.ts +22 -1
- package/dist/skills.js +36 -0
- package/dist/start-work-typed.d.ts +34 -0
- package/dist/start-work-typed.js +156 -0
- package/dist/state.d.ts +2 -0
- package/dist/state.js +2 -0
- package/dist/tools.js +22 -2
- package/dist/tui/mission-band.d.ts +20 -1
- package/dist/tui/mission-band.js +73 -16
- package/dist/tui/mission-looks.d.ts +38 -0
- package/dist/tui/mission-looks.js +73 -0
- package/dist/tui/mission-model.d.ts +2 -0
- package/dist/tui/mission-model.js +3 -1
- package/dist/tui/mission-rainbow.d.ts +33 -0
- package/dist/tui/mission-rainbow.js +90 -0
- package/dist/tui/mission-source.d.ts +48 -0
- package/dist/tui/mission-source.js +184 -2
- package/dist/tui/mission-text.d.ts +2 -0
- package/dist/tui/mission-text.js +9 -1
- package/dist/tui/mission-threads.d.ts +50 -0
- package/dist/tui/mission-threads.js +119 -0
- package/dist/tui.js +84 -6
- package/dist/uiux-visual-catalog.js +8 -1
- package/dist/v2/session-hooks.js +38 -5
- package/dist/workflow-stack.d.ts +53 -0
- package/dist/workflow-stack.js +196 -0
- package/docs/assets/readme/badge-version.svg +1 -1
- package/docs/assets/skills/lit-schematic.webp +0 -0
- package/docs/reference-Ko-KR.md +30 -2
- package/docs/reference.md +34 -2
- package/package.json +1 -1
- package/skills/frontend-ui-ux/SKILL.md +3 -2
- package/skills/frontend-ui-ux/kit/components.css +414 -0
- package/skills/frontend-ui-ux/kit/fonts.json +22 -0
- package/skills/frontend-ui-ux/kit/layouts/landing.html +164 -0
- package/skills/frontend-ui-ux/kit/layouts/settings.html +114 -0
- package/skills/frontend-ui-ux/kit/layouts/workspace.html +115 -0
- package/skills/frontend-ui-ux/kit/styles/computational-brutalism.json +227 -0
- package/skills/frontend-ui-ux/kit/styles/field-notes.json +205 -0
- package/skills/frontend-ui-ux/kit/styles/graphite.json +200 -0
- package/skills/frontend-ui-ux/kit/styles/meadow.json +199 -0
- package/skills/frontend-ui-ux/kit/styles/neo-brutalism.json +222 -0
- package/skills/frontend-ui-ux/kit/ui.js +226 -0
- package/skills/frontend-ui-ux/references/brand-and-imagery.md +2 -2
- package/skills/frontend-ui-ux/references/complete-contract.md +2 -1
- package/skills/frontend-ui-ux/references/craft-floor.md +6 -2
- package/skills/frontend-ui-ux/references/default-editorial-pixel.json +3 -0
- package/skills/frontend-ui-ux/references/house-styles.md +236 -0
- package/skills/frontend-ui-ux/references/motion-guide.md +2 -2
- package/skills/frontend-ui-ux/references/production.md +3 -1
- package/skills/frontend-ui-ux/references/slop-register.md +4 -4
- package/skills/frontend-ui-ux/scripts/design-lock.mjs +400 -0
- package/skills/frontend-ui-ux/scripts/pixel-art.mjs +60 -0
- package/skills/frontend-ui-ux/scripts/probe-lock.mjs +235 -0
- package/skills/frontend-ui-ux/scripts/probe-page.mjs +34 -12
- package/skills/frontend-ui-ux/scripts/probe-static.mjs +16 -3
- package/skills/frontend-ui-ux/scripts/probe-thresholds.json +8 -2
- package/skills/frontend-ui-ux/scripts/probe.mjs +35 -7
- package/skills/lit-adhd/SKILL.md +208 -0
- package/skills/lit-adhd/references/brief-format.md +43 -0
- package/skills/lit-adhd/references/card-examples.md +82 -0
- package/skills/lit-adhd/references/status-taxonomy.md +29 -0
- package/skills/lit-adhd/scripts/rewind.mjs +835 -0
- package/skills/lit-diagram-drawer/SKILL.md +4 -3
- package/skills/lit-diagram-drawer/references/style-guide.md +20 -11
- package/skills/lit-diagram-drawer/references/type-architecture.md +3 -3
- package/skills/lit-diagram-drawer/references/type-dependency.md +1 -1
- package/skills/lit-diagram-drawer/references/type-tree.md +3 -3
- package/skills/lit-diagram-drawer/references/type-wardley.md +1 -1
- package/skills/lit-diagram-drawer/references/verifier-guide.md +1 -0
- package/skills/lit-diagram-drawer/scripts/craft-thresholds.json +1 -2
- package/skills/lit-diagram-drawer/scripts/visual-quality.mjs +1 -2
- package/skills/lit-docx/SKILL.md +2 -1
- package/skills/lit-handoff/SKILL.md +2 -0
- package/skills/lit-hijack/SKILL.md +422 -0
- package/skills/lit-hijack/references/capture-and-provenance.md +87 -0
- package/skills/lit-hijack/references/clean-room-build.md +111 -0
- package/skills/lit-hijack/references/distil-and-licence.md +100 -0
- package/skills/lit-hijack/references/gap-and-port-design.md +126 -0
- package/skills/lit-hijack/references/parity-and-report.md +110 -0
- package/skills/lit-hijack/scripts/hijack.mjs +993 -0
- package/skills/lit-plan/SKILL.md +2 -0
- package/skills/lit-pptx/SKILL.md +27 -11
- package/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Bold.ttf +0 -0
- package/skills/lit-pptx/fonts/jetbrains-mono/JetBrainsMono-Regular.ttf +0 -0
- package/skills/lit-pptx/fonts/jetbrains-mono/OFL.txt +93 -0
- package/skills/lit-pptx/fonts/jetbrains-mono/SOURCE.md +17 -0
- package/skills/lit-pptx/references/density-and-fill.md +9 -3
- package/skills/lit-pptx/references/direction-step.md +21 -6
- package/skills/lit-pptx/references/examples/04-lecture-chalk-ko.md +3 -6
- package/skills/lit-pptx/references/examples/05-research-talk-paper-en.md +1 -5
- package/skills/lit-pptx/references/examples/08-reliability-keynote-night-en.md +0 -4
- package/skills/lit-pptx/references/examples/09-status-update-ledger-en.md +2 -2
- package/skills/lit-pptx/references/examples/11-forecast-review-night-ko.md +4 -7
- package/skills/lit-pptx/references/examples/13-engineering-review-computational-brutalism-ko.md +173 -0
- package/skills/lit-pptx/references/examples/14-launch-pitch-neo-brutalism-ko.md +154 -0
- package/skills/lit-pptx/references/examples/15-design-lecture-bauhaus-ko.md +139 -0
- package/skills/lit-pptx/references/examples/16-community-programme-riso-ko.md +158 -0
- package/skills/lit-pptx/references/examples/17-public-data-briefing-newsroom-ko.md +179 -0
- package/skills/lit-pptx/references/layout-families.md +65 -47
- package/skills/lit-pptx/references/title-treatments.md +83 -134
- package/skills/lit-pptx/references/tonalities/atlas.md +3 -3
- package/skills/lit-pptx/references/tonalities/bauhaus.md +140 -0
- package/skills/lit-pptx/references/tonalities/chalk.md +7 -6
- package/skills/lit-pptx/references/tonalities/computational-brutalism.md +150 -0
- package/skills/lit-pptx/references/tonalities/gazette.md +1 -1
- package/skills/lit-pptx/references/tonalities/ledger.md +1 -1
- package/skills/lit-pptx/references/tonalities/neo-brutalism.md +161 -0
- package/skills/lit-pptx/references/tonalities/newsroom.md +144 -0
- package/skills/lit-pptx/references/tonalities/night.md +1 -1
- package/skills/lit-pptx/references/tonalities/paper.md +2 -2
- package/skills/lit-pptx/references/tonalities/riso.md +137 -0
- package/skills/lit-pptx/references/tonalities/signal.md +3 -3
- package/skills/lit-pptx/references/tonalities/studio.md +7 -6
- package/skills/lit-pptx/run.mjs +2 -1
- package/skills/lit-pptx/scripts/compile-deck.js +1 -1
- package/skills/lit-pptx/scripts/craft-thresholds.json +4 -8
- package/skills/lit-pptx/scripts/craft_extras.py +56 -3
- package/skills/lit-pptx/scripts/deck_output.py +75 -90
- package/skills/lit-pptx/scripts/embed_fonts.py +13 -1
- package/skills/lit-pptx/scripts/layout_inventory.py +12 -5
- package/skills/lit-pptx/scripts/lib/grid-resolver.js +101 -110
- package/skills/lit-pptx/scripts/lib/layout-resolver.js +9 -15
- package/skills/lit-pptx/scripts/lib/render-pack.js +373 -566
- package/skills/lit-pptx/scripts/lib/slide-grid.js +104 -0
- package/skills/lit-pptx/scripts/lib/template-registry.js +57 -6
- package/skills/lit-pptx/scripts/qa_deck.py +10 -11
- package/skills/lit-pptx/scripts/render_pages.py +118 -0
- package/skills/lit-pptx/templates/tonalities/atlas/pack.yaml +1 -1
- package/skills/lit-pptx/templates/tonalities/bauhaus/pack.yaml +98 -0
- package/skills/lit-pptx/templates/tonalities/chalk/pack.yaml +1 -1
- package/skills/lit-pptx/templates/tonalities/computational-brutalism/pack.yaml +99 -0
- package/skills/lit-pptx/templates/tonalities/gazette/pack.yaml +1 -1
- package/skills/lit-pptx/templates/tonalities/ledger/pack.yaml +1 -1
- package/skills/lit-pptx/templates/tonalities/neo-brutalism/pack.yaml +107 -0
- package/skills/lit-pptx/templates/tonalities/newsroom/pack.yaml +99 -0
- package/skills/lit-pptx/templates/tonalities/night/pack.yaml +1 -1
- package/skills/lit-pptx/templates/tonalities/paper/pack.yaml +1 -1
- package/skills/lit-pptx/templates/tonalities/riso/pack.yaml +99 -0
- package/skills/lit-pptx/templates/tonalities/signal/pack.yaml +2 -2
- package/skills/lit-pptx/templates/tonalities/studio/pack.yaml +1 -1
- package/skills/lit-recap/SKILL.md +4 -0
- package/skills/lit-schematic/SKILL.md +118 -0
- package/skills/lit-schematic/examples/iso-heat-exchanger/build.mjs +38 -0
- package/skills/lit-schematic/examples/iso-heat-exchanger/scene.mjs +133 -0
- package/skills/lit-schematic/examples/journal-adsorption-rig/build.mjs +87 -0
- package/skills/lit-schematic/examples/pid-dosing-skid/build.mjs +85 -0
- package/skills/lit-schematic/references/cutaway-outputs.md +70 -0
- package/skills/lit-schematic/references/isometric-craft.md +101 -0
- package/skills/lit-schematic/references/journal-figures.md +72 -0
- package/skills/lit-schematic/references/kit-api.md +134 -0
- package/skills/lit-schematic/references/pfd-and-layout.md +53 -0
- package/skills/lit-schematic/references/pid-drafting.md +103 -0
- package/skills/lit-schematic/references/pid-symbols.md +163 -0
- package/skills/lit-schematic/references/verification.md +100 -0
- package/skills/lit-schematic/scripts/doctor.mjs +57 -0
- package/skills/lit-schematic/scripts/lib/figure.mjs +81 -0
- package/skills/lit-schematic/scripts/lib/fonts.mjs +25 -0
- package/skills/lit-schematic/scripts/lib/geom.mjs +113 -0
- package/skills/lit-schematic/scripts/lib/interactive.mjs +292 -0
- package/skills/lit-schematic/scripts/lib/isa.mjs +115 -0
- package/skills/lit-schematic/scripts/lib/iso.mjs +719 -0
- package/skills/lit-schematic/scripts/lib/legend-sheet.mjs +162 -0
- package/skills/lit-schematic/scripts/lib/pid.mjs +937 -0
- package/skills/lit-schematic/scripts/lib/png.mjs +132 -0
- package/skills/lit-schematic/scripts/lib/qa-core.mjs +658 -0
- package/skills/lit-schematic/scripts/lib/route.mjs +218 -0
- package/skills/lit-schematic/scripts/lib/sheet.mjs +890 -0
- package/skills/lit-schematic/scripts/lib/svg.mjs +82 -0
- package/skills/lit-schematic/scripts/lib/theme.mjs +115 -0
- package/skills/lit-schematic/scripts/motion.mjs +129 -0
- package/skills/lit-schematic/scripts/qa.mjs +92 -0
- package/skills/lit-schematic/scripts/render.mjs +233 -0
- package/skills/lit-schematic/scripts/scaffold.mjs +60 -0
- package/skills/lit-scientific-visualization/SKILL.md +27 -4
- package/skills/lit-typographic-motion/SKILL.md +19 -17
- package/skills/lit-typographic-motion/THIRD_PARTY_NOTICES +9 -0
- package/skills/lit-typographic-motion/engine/accents.mjs +6 -0
- package/skills/lit-typographic-motion/engine/assets.mjs +282 -0
- package/skills/lit-typographic-motion/engine/blender/runner.py +228 -0
- package/skills/lit-typographic-motion/engine/blender.mjs +126 -0
- package/skills/lit-typographic-motion/engine/board.mjs +138 -0
- package/skills/lit-typographic-motion/engine/cover/assets.mjs +11 -8
- package/skills/lit-typographic-motion/engine/cover/capture.mjs +6 -6
- package/skills/lit-typographic-motion/engine/cover/render.mjs +20 -7
- package/skills/lit-typographic-motion/engine/cover/treatment.mjs +30 -3
- package/skills/lit-typographic-motion/engine/fill.mjs +54 -0
- package/skills/lit-typographic-motion/engine/gallery.mjs +118 -0
- package/skills/lit-typographic-motion/engine/house.mjs +42 -0
- package/skills/lit-typographic-motion/engine/look.mjs +65 -8
- package/skills/lit-typographic-motion/engine/loudness.mjs +101 -0
- package/skills/lit-typographic-motion/engine/scaffold.mjs +219 -0
- package/skills/lit-typographic-motion/engine/score.mjs +594 -0
- package/skills/lit-typographic-motion/engine/sound.mjs +9 -57
- package/skills/lit-typographic-motion/engine/stage/capture.mjs +23 -9
- package/skills/lit-typographic-motion/engine/stage/clock.js +8 -2
- package/skills/lit-typographic-motion/engine/stage/cover-init.js +12 -1
- package/skills/lit-typographic-motion/engine/stage/frame-worker.mjs +15 -7
- package/skills/lit-typographic-motion/engine/stage/gate.mjs +48 -4
- package/skills/lit-typographic-motion/engine/stage/qa-page.js +16 -5
- package/skills/lit-typographic-motion/engine/stage/qa.mjs +148 -7
- package/skills/lit-typographic-motion/engine/stage/render.mjs +69 -7
- package/skills/lit-typographic-motion/engine/stage/scan.mjs +20 -1
- package/skills/lit-typographic-motion/engine/stage/stage-kit.js +85 -4
- package/skills/lit-typographic-motion/engine/stills.mjs +1 -1
- package/skills/lit-typographic-motion/engine/styles.mjs +221 -0
- package/skills/lit-typographic-motion/engine/text.mjs +1 -1
- package/skills/lit-typographic-motion/engine/treatment.mjs +190 -6
- package/skills/lit-typographic-motion/engine/vendor/three/LICENSE +21 -0
- package/skills/lit-typographic-motion/engine/vendor/three/addons/environments/RoomEnvironment.js +144 -0
- package/skills/lit-typographic-motion/engine/vendor/three/addons/geometries/RoundedBoxGeometry.js +155 -0
- package/skills/lit-typographic-motion/engine/vendor/three/three.module.js +54571 -0
- package/skills/lit-typographic-motion/gate.mjs +18 -2
- package/skills/lit-typographic-motion/probe.mjs +13 -1
- package/skills/lit-typographic-motion/recipes/hero-3d/expected.json +64 -0
- package/skills/lit-typographic-motion/recipes/hero-3d/index.html +132 -0
- package/skills/lit-typographic-motion/recipes/orbit-loop/expected.json +2 -0
- package/skills/lit-typographic-motion/recipes/painted-light/expected.json +2 -0
- package/skills/lit-typographic-motion/recipes/weight-tide/expected.json +2 -0
- package/skills/lit-typographic-motion/references/cover.md +56 -13
- package/skills/lit-typographic-motion/references/craft-loop.md +20 -7
- package/skills/lit-typographic-motion/references/depth.md +112 -0
- package/skills/lit-typographic-motion/references/runtime.md +21 -6
- package/skills/lit-typographic-motion/references/sound.md +76 -0
- package/skills/lit-typographic-motion/references/stage.md +50 -7
- package/skills/lit-typographic-motion/references/studio.md +162 -0
- package/skills/lit-typographic-motion/references/style-bibles.md +18 -0
- package/skills/lit-typographic-motion/references/treatment.md +108 -9
- package/skills/lit-typographic-motion/render.mjs +60 -3
- package/skills/lit-typographic-motion/styles/paper.json +185 -0
- package/skills/lit-typographic-motion/styles/swiss-signal.json +188 -0
- package/skills/lit-typographic-motion/styles/terminalcore.json +182 -0
- package/skills/lit-typographic-motion/styles/tidal.json +187 -0
- package/skills/litresearch/SKILL.md +2 -0
- package/skills/managed-skill-manifest.json +517 -88
- package/skills/readme-studio/SKILL.md +1 -1
- package/skills/review-work/SKILL.md +2 -0
- package/skills/start-work/SKILL.md +8 -0
- package/skills/workflow-loop/SKILL.md +4 -0
- package/tools/check-pack-payload.mjs +4 -2
- package/tools/payload-substance-parity.json +4 -3
- package/tools/qa-cover-recipes.mjs +4 -3
package/dist/tui/mission-band.js
CHANGED
|
@@ -5,6 +5,9 @@
|
|
|
5
5
|
// the band always move together.
|
|
6
6
|
import { ease, mixHex, motionTiming, seg } from "./mission-motion.js";
|
|
7
7
|
import { contextWarnPercent, currentStepIndex, missionStatus, stepName } from "./mission-model.js";
|
|
8
|
+
import { laneColor, reviewLanes, reviewMark, turnColor, turnMark } from "./mission-looks.js";
|
|
9
|
+
import { isRainbowSkill, rainbowColor, rainbowPill, rainbowText, rewindMark } from "./mission-rainbow.js";
|
|
10
|
+
import { threadLines } from "./mission-threads.js";
|
|
8
11
|
import { cellWidth, chipSparkCells, chipSparkWidth, crumb, fade, lineWidth, linkColor, liveSegments, missionPanel, progressBar, shimmerAt, shine, stageFlash, statusSegments, truncateCells } from "./mission-text.js";
|
|
9
12
|
/**
|
|
10
13
|
* Reads the band switches from the plugin's tui.json options: `band: false` turns the band off,
|
|
@@ -34,6 +37,7 @@ export function readNotifyOptions(options) {
|
|
|
34
37
|
return { enabled: desktop || sound, desktop, sound };
|
|
35
38
|
}
|
|
36
39
|
const minimumText = 12;
|
|
40
|
+
const idleHint = "lit adhd to pick up where you left off";
|
|
37
41
|
function elapsedText(minutes) {
|
|
38
42
|
const total = Math.max(0, minutes ?? 0);
|
|
39
43
|
return total < 60 ? `${total}m` : `${Math.floor(total / 60)}h${String(total % 60).padStart(2, "0")}`;
|
|
@@ -129,10 +133,10 @@ function phaseLabel(view, frame, palette, rest) {
|
|
|
129
133
|
return { text: `⚑ ${status.text}`, fg: mixHex(palette.info, palette.warning, Math.max(frame.warn, 0.5)), bold: true };
|
|
130
134
|
return { text: `● ${stepName(view.phase, view).toUpperCase()}`, fg: palette.info, bold: true };
|
|
131
135
|
}
|
|
132
|
-
function topLine(view, frame, palette, width, border, rest) {
|
|
136
|
+
function topLine(view, frame, palette, width, border, rest, mark) {
|
|
133
137
|
const brand = [{ text: "🔥 ", fg: palette.text }, { text: "LIT", fg: shine(palette.secondary, palette, 0.5, shimmerAt(frame), 0.6), bold: true }];
|
|
134
138
|
const name = view.active ? view.discipline : "ready";
|
|
135
|
-
const right = [phaseLabel(view, frame, palette, rest)];
|
|
139
|
+
const right = mark ?? [phaseLabel(view, frame, palette, rest)];
|
|
136
140
|
const withName = (room) => [...brand, { text: " · ", fg: palette.muted }, { text: truncateCells(name, room), fg: view.active ? palette.text : palette.muted }];
|
|
137
141
|
const nameRoom = width - cellWidth("╭─") - lineWidth(withName(0)) - 2 - (lineWidth(right) + 2) - cellWidth("─╮") - 1;
|
|
138
142
|
const attempts = [];
|
|
@@ -190,12 +194,16 @@ function trackPills(view, frame, palette, tight) {
|
|
|
190
194
|
return out;
|
|
191
195
|
}
|
|
192
196
|
/** The text after the track in an idle session: the project's last goal, or how to start. */
|
|
193
|
-
function idleText(last, palette, room) {
|
|
194
|
-
if (last === undefined || last.title === "")
|
|
195
|
-
return [{ text: " ", fg: palette.muted }, { text: truncateCells("add lit to a prompt to start", room - 2), fg: palette.muted }];
|
|
197
|
+
function idleText(last, palette, room, earlier) {
|
|
198
|
+
if (last === undefined || last.title === "") {
|
|
199
|
+
return [{ text: " ", fg: palette.muted }, { text: truncateCells(earlier ? idleHint : "add lit to a prompt to start", room - 2), fg: earlier ? palette.accent : palette.muted }];
|
|
200
|
+
}
|
|
196
201
|
const mark = last.complete ? { text: ` ✓ ${last.passed}/${last.total}`, fg: palette.success } : { text: ` ● ${last.passed}/${last.total}`, fg: palette.info };
|
|
197
202
|
const head = { text: " last: ", fg: palette.muted };
|
|
198
|
-
|
|
203
|
+
const hint = earlier ? [{ text: ` ${idleHint}`, fg: palette.accent }] : [];
|
|
204
|
+
const fixed = cellWidth(head.text) + cellWidth(mark.text);
|
|
205
|
+
const withHint = hint.length > 0 && room - fixed - lineWidth(hint) >= 12 ? hint : [];
|
|
206
|
+
return [head, { text: truncateCells(last.title, room - fixed - lineWidth(withHint)), fg: palette.text }, mark, ...withHint];
|
|
199
207
|
}
|
|
200
208
|
// Shedding order as the row narrows: the next-task text, bar width, the bar, the criteria count and
|
|
201
209
|
// then the step labels. Each plan is tried in turn and the first that fits wins.
|
|
@@ -207,7 +215,7 @@ const trackPlans = [
|
|
|
207
215
|
{ tight: false, bar: 0, criteria: false, text: false },
|
|
208
216
|
{ tight: true, bar: 0, criteria: false, text: false }
|
|
209
217
|
];
|
|
210
|
-
function trackRow(view, frame, palette, width, last) {
|
|
218
|
+
function trackRow(view, frame, palette, width, last, earlier) {
|
|
211
219
|
const sweep = shimmerAt(frame);
|
|
212
220
|
const gap = { text: " ", fg: palette.muted };
|
|
213
221
|
for (const plan of trackPlans) {
|
|
@@ -220,7 +228,7 @@ function trackRow(view, frame, palette, width, last) {
|
|
|
220
228
|
if (fixed > width)
|
|
221
229
|
continue;
|
|
222
230
|
const room = width - fixed;
|
|
223
|
-
const after = plan.text ? (view.active ? trailing(view, frame, palette, room) : idleText(last, palette, room)) : [];
|
|
231
|
+
const after = plan.text ? (view.active ? trailing(view, frame, palette, room) : idleText(last, palette, room, earlier)) : [];
|
|
224
232
|
const keepText = plan.text && after.length > 0 && (view.complete ? lineWidth(after) <= room : room >= minimumText);
|
|
225
233
|
if (plan.text && after.length > 0 && !keepText)
|
|
226
234
|
continue;
|
|
@@ -231,8 +239,10 @@ function trackRow(view, frame, palette, width, last) {
|
|
|
231
239
|
}
|
|
232
240
|
return fitLine(trackPills(view, frame, palette, true), width, palette);
|
|
233
241
|
}
|
|
234
|
-
function chipSegments(chip, shimmer, palette) {
|
|
242
|
+
function chipSegments(chip, shimmer, palette, rainbow) {
|
|
235
243
|
const text = ` ${chip.name} `;
|
|
244
|
+
if (rainbow !== undefined && chip.current && isRainbowSkill(chip.name))
|
|
245
|
+
return rainbowPill(text, rainbow, 180);
|
|
236
246
|
if (!chip.current)
|
|
237
247
|
return [{ text, fg: palette.muted, ...(palette.element === undefined ? {} : { bg: palette.element }) }];
|
|
238
248
|
const sweep = shimmer?.names.includes(chip.name) === true ? shimmer.sweep : -1;
|
|
@@ -245,11 +255,11 @@ function chipSegments(chip, shimmer, palette) {
|
|
|
245
255
|
* The skill chips row, exactly `width` cells: bright LIT accent pills for the current request first, then
|
|
246
256
|
* dim recent names, and `+N` for the ones that do not fit. The label goes before any chip does.
|
|
247
257
|
*/
|
|
248
|
-
export function skillRow(chips, shimmer, palette, width) {
|
|
258
|
+
export function skillRow(chips, shimmer, palette, width, rainbow) {
|
|
249
259
|
const label = { text: "skills ", fg: palette.muted };
|
|
250
260
|
if (chips.length === 0)
|
|
251
261
|
return fitLine([label, { text: " none yet", fg: palette.muted }], width, palette);
|
|
252
|
-
const pills = chips.map((chip) => chipSegments(chip, shimmer, palette));
|
|
262
|
+
const pills = chips.map((chip) => chipSegments(chip, shimmer, palette, rainbow));
|
|
253
263
|
const space = { text: " ", fg: palette.muted };
|
|
254
264
|
for (const lead of [[label], []]) {
|
|
255
265
|
const all = lineWidth(lead) + pills.reduce((total, pill, index) => total + lineWidth(pill) + (index === 0 ? 0 : 1), 0);
|
|
@@ -275,6 +285,24 @@ export function skillRow(chips, shimmer, palette, width) {
|
|
|
275
285
|
}
|
|
276
286
|
return fitLine([{ text: `+${pills.length}`, fg: palette.muted }], width, palette);
|
|
277
287
|
}
|
|
288
|
+
/** The longest run of rule in the top border, split into the five review lanes, each in its lane's colour. */
|
|
289
|
+
function laneRule(top, reports, palette) {
|
|
290
|
+
let at = -1;
|
|
291
|
+
top.forEach((segment, index) => {
|
|
292
|
+
if (/^─+$/u.test(segment.text) && (at < 0 || segment.text.length > (top[at]?.text.length ?? 0)))
|
|
293
|
+
at = index;
|
|
294
|
+
});
|
|
295
|
+
const rule = top[at];
|
|
296
|
+
if (rule === undefined || rule.text.length < reviewLanes.length)
|
|
297
|
+
return top;
|
|
298
|
+
const size = rule.text.length;
|
|
299
|
+
const parts = reviewLanes.map((lane, index) => {
|
|
300
|
+
const from = Math.floor((index * size) / reviewLanes.length);
|
|
301
|
+
const to = Math.floor(((index + 1) * size) / reviewLanes.length);
|
|
302
|
+
return { text: "─".repeat(to - from), fg: laneColor(lane, reports, palette), ...(reports[lane] === undefined ? {} : { bold: true }) };
|
|
303
|
+
});
|
|
304
|
+
return [...top.slice(0, at), ...parts, ...top.slice(at + 1)];
|
|
305
|
+
}
|
|
278
306
|
/**
|
|
279
307
|
* The status card: four rows of exactly `width` cells whether the session is idle or working, so
|
|
280
308
|
* the prompt never moves. Each row sheds its own low-priority parts as the terminal narrows.
|
|
@@ -284,14 +312,43 @@ export function statusCard(input, palette, width) {
|
|
|
284
312
|
const rest = resting(input.view, input.busy);
|
|
285
313
|
// At rest the track keeps what is done and unlights the step that was running.
|
|
286
314
|
const view = rest ? { ...input.view, steps: input.view.steps.map((step) => (step.state === "now" ? { ...step, state: "todo" } : step)) } : input.view;
|
|
287
|
-
|
|
315
|
+
// One look at a time: waiting on the user first, then the rewind look, then the review lanes.
|
|
316
|
+
const turn = input.turn;
|
|
317
|
+
const rainbow = turn === undefined ? input.rainbow : undefined;
|
|
318
|
+
const lanes = turn === undefined && rainbow === undefined ? input.lanes : undefined;
|
|
319
|
+
const border = turn !== undefined ? turnColor(palette, turn.glow) : rainbow === undefined ? borderColor(view, frame, palette, rest) : rainbowColor(rainbow.hue);
|
|
320
|
+
const trackPalette = rainbow === undefined ? palette : { ...palette, primary: rainbowColor(rainbow.hue + 40), accent: rainbowColor(rainbow.hue + 160) };
|
|
288
321
|
const inner = width - 4;
|
|
289
322
|
const row = (content) => (inner < 0 ? fitLine([], width, palette) : [{ text: "│ ", fg: border }, ...fitLine(content, inner, palette), { text: " │", fg: border }]);
|
|
323
|
+
const mark = turn !== undefined ? turnMark(palette, turn.glow) : rainbow !== undefined ? rewindMark(rainbow) : lanes !== undefined ? reviewMark(lanes, palette) : undefined;
|
|
324
|
+
const ruledTop = topLine(view, frame, palette, width, border, rest, mark);
|
|
325
|
+
const top = lanes === undefined ? ruledTop : laneRule(ruledTop, lanes, palette);
|
|
326
|
+
const threadRows = input.threads === undefined ? [] : threadLines(input.threads, palette, Math.max(0, inner), {
|
|
327
|
+
now: frame.now ?? 0,
|
|
328
|
+
motion: input.motion ?? true,
|
|
329
|
+
...(rainbow === undefined ? {} : { gaugeColor: (index) => rainbowColor(rainbow.hue + 60 + index * 50) })
|
|
330
|
+
});
|
|
331
|
+
while (threadRows.length < (input.reserve ?? 0))
|
|
332
|
+
threadRows.push([]);
|
|
333
|
+
const track = trackRow(view, frame, trackPalette, Math.max(0, inner), input.last, input.earlier === true);
|
|
334
|
+
const bottom = bottomLine(view, input.figures, palette, width, border);
|
|
335
|
+
if (rainbow === undefined)
|
|
336
|
+
return [top, row(track), row(skillRow(input.chips, input.shimmer, palette, Math.max(0, inner))), ...threadRows.map(row), bottom];
|
|
337
|
+
// The rewind look reaches every pill cell by cell: LIT, the running phase, the lit-adhd chip; the phase
|
|
338
|
+
// labels and the context figure turn on the card's own ground.
|
|
339
|
+
const names = view.steps.map((step) => stepName(step.id, view).toLowerCase());
|
|
340
|
+
const label = (text) => names.findIndex((name) => text.trim().toLowerCase().startsWith(name));
|
|
290
341
|
return [
|
|
291
|
-
|
|
292
|
-
row(
|
|
293
|
-
|
|
294
|
-
|
|
342
|
+
top.flatMap((segment) => (segment.text === "LIT" ? rainbowPill("LIT", rainbow, 90) : [segment])),
|
|
343
|
+
row(track.flatMap((segment) => {
|
|
344
|
+
if (segment.bg !== undefined && segment.bold === true && /\S/u.test(segment.text))
|
|
345
|
+
return rainbowPill(segment.text, rainbow, 160);
|
|
346
|
+
const at = segment.bg === undefined ? label(segment.text) : -1;
|
|
347
|
+
return at >= 0 ? [{ ...segment, fg: rainbowColor(rainbow.hue + 200 + at * 50), bold: true }] : [segment];
|
|
348
|
+
})),
|
|
349
|
+
row(skillRow(input.chips, input.shimmer, palette, Math.max(0, inner), rainbow)),
|
|
350
|
+
...threadRows.map(row),
|
|
351
|
+
bottom.flatMap((segment) => (segment.text.startsWith("ctx ") ? rainbowText(segment.text, rainbow, 120) : [segment]))
|
|
295
352
|
];
|
|
296
353
|
}
|
|
297
354
|
const cardRows = 4;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { MissionPalette, Segment } from "./mission-text.ts";
|
|
2
|
+
import type { MissionView } from "./mission-model.ts";
|
|
3
|
+
import type { SkillChip } from "./mission-skills.ts";
|
|
4
|
+
export type SkillLook = "rewind" | "review";
|
|
5
|
+
/** The look a skill name carries, if any. */
|
|
6
|
+
export declare function lookOfSkill(name: string): SkillLook | undefined;
|
|
7
|
+
/** The skill look while a turn runs: the session's discipline first, then a chip this turn lit. */
|
|
8
|
+
export declare function activeSkillLook(view: Pick<MissionView, "active" | "discipline">, chips: readonly SkillChip[], busy: boolean | undefined): SkillLook | undefined;
|
|
9
|
+
/** What the session is waiting on the user for, read from records; every field is a real signal. */
|
|
10
|
+
export type WaitSignals = {
|
|
11
|
+
/** OpenCode holds a permission request or a question for this session. */
|
|
12
|
+
readonly asked: boolean;
|
|
13
|
+
/** The last message is the user's: they have answered, so nothing waits on them now. */
|
|
14
|
+
readonly userSpokeLast: boolean;
|
|
15
|
+
/** The last reply of the turn opens with `BLOCKED:`. */
|
|
16
|
+
readonly blockedReply: boolean;
|
|
17
|
+
/** A typed start-work run is paused on a boundary only the user can approve. */
|
|
18
|
+
readonly boundary: boolean;
|
|
19
|
+
};
|
|
20
|
+
/** True while the session waits on the user: asked now, or idle on a decision, a BLOCKED reply or a boundary. */
|
|
21
|
+
export declare function waitingOnYou(view: Pick<MissionView, "active" | "warnings">, busy: boolean | undefined, signals: WaitSignals): boolean;
|
|
22
|
+
export declare const turnBreathMs = 3000;
|
|
23
|
+
/** How far the amber has eased toward the border at `now`, 0 to 1; a 3 s sine, 0 under reduced motion. */
|
|
24
|
+
export declare function turnGlow(now: number, motion: boolean): number;
|
|
25
|
+
/** The amber of the waiting look: the warning colour eased at most 25 % toward the card's border. */
|
|
26
|
+
export declare function turnColor(palette: MissionPalette, glow: number): string;
|
|
27
|
+
export declare function turnMark(palette: MissionPalette, glow: number): Segment[];
|
|
28
|
+
export declare const reviewLanes: readonly ["scope", "tests", "package", "security", "surface"];
|
|
29
|
+
export type ReviewLane = (typeof reviewLanes)[number];
|
|
30
|
+
export type LaneResult = "pass" | "fail" | "blocked";
|
|
31
|
+
export type LaneReports = Partial<Readonly<Record<ReviewLane, LaneResult>>>;
|
|
32
|
+
export declare const laneResults: readonly LaneResult[];
|
|
33
|
+
export declare function isReviewLane(value: unknown): value is ReviewLane;
|
|
34
|
+
export declare function isLaneResult(value: unknown): value is LaneResult;
|
|
35
|
+
/** One lane's colour on the border: its own hue once it reports a pass, error once it fails, dim until then. */
|
|
36
|
+
export declare function laneColor(lane: ReviewLane, reports: LaneReports, palette: MissionPalette): string;
|
|
37
|
+
/** The corner label of the review look: how many of the five lanes have reported, never a guess. */
|
|
38
|
+
export declare function reviewMark(reports: LaneReports, palette: MissionPalette): Segment[];
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// The looks the status card can wear, chosen in one place. A skill look belongs to the discipline at work
|
|
2
|
+
// or to a chip lit by this turn: lit-adhd and its alias lit-rewind wear the rewind look, review-work wears
|
|
3
|
+
// its five lanes. A state look belongs to the session: `YOUR TURN` while it waits on the user, and the
|
|
4
|
+
// done burst (the completion sparks of mission-motion) when a goal reaches done. Each look reads only
|
|
5
|
+
// records OpenCode or LitOpenCode already keep, so a lane without a report stays dim and nothing is
|
|
6
|
+
// guessed. Motion is slow and small: the amber breath is one hue held while its mix eases over 3 s.
|
|
7
|
+
import { mixHex } from "./mission-motion.js";
|
|
8
|
+
const skillLooks = Object.freeze({ "lit-adhd": "rewind", "lit-rewind": "rewind", "review-work": "review" });
|
|
9
|
+
/** The look a skill name carries, if any. */
|
|
10
|
+
export function lookOfSkill(name) {
|
|
11
|
+
return Object.hasOwn(skillLooks, name) ? skillLooks[name] : undefined;
|
|
12
|
+
}
|
|
13
|
+
/** The skill look while a turn runs: the session's discipline first, then a chip this turn lit. */
|
|
14
|
+
export function activeSkillLook(view, chips, busy) {
|
|
15
|
+
if (busy === false)
|
|
16
|
+
return undefined;
|
|
17
|
+
const own = view.active ? lookOfSkill(view.discipline) : undefined;
|
|
18
|
+
if (own !== undefined)
|
|
19
|
+
return own;
|
|
20
|
+
for (const chip of chips) {
|
|
21
|
+
const look = chip.current ? lookOfSkill(chip.name) : undefined;
|
|
22
|
+
if (look !== undefined)
|
|
23
|
+
return look;
|
|
24
|
+
}
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
/** True while the session waits on the user: asked now, or idle on a decision, a BLOCKED reply or a boundary. */
|
|
28
|
+
export function waitingOnYou(view, busy, signals) {
|
|
29
|
+
if (signals.asked)
|
|
30
|
+
return true;
|
|
31
|
+
if (signals.userSpokeLast || busy === true)
|
|
32
|
+
return false;
|
|
33
|
+
const decision = view.active && view.warnings.some((warning) => warning.kind === "decision");
|
|
34
|
+
return decision || signals.blockedReply || signals.boundary;
|
|
35
|
+
}
|
|
36
|
+
export const turnBreathMs = 3000;
|
|
37
|
+
/** How far the amber has eased toward the border at `now`, 0 to 1; a 3 s sine, 0 under reduced motion. */
|
|
38
|
+
export function turnGlow(now, motion) {
|
|
39
|
+
if (!motion)
|
|
40
|
+
return 0;
|
|
41
|
+
return (1 - Math.cos((2 * Math.PI * (now % turnBreathMs)) / turnBreathMs)) / 2;
|
|
42
|
+
}
|
|
43
|
+
/** The amber of the waiting look: the warning colour eased at most 25 % toward the card's border. */
|
|
44
|
+
export function turnColor(palette, glow) {
|
|
45
|
+
return mixHex(palette.warning, palette.borderActive, Math.min(1, Math.max(0, glow)) * 0.25);
|
|
46
|
+
}
|
|
47
|
+
export function turnMark(palette, glow) {
|
|
48
|
+
return [{ text: "◆ YOUR TURN", fg: turnColor(palette, glow), bold: true }];
|
|
49
|
+
}
|
|
50
|
+
export const reviewLanes = ["scope", "tests", "package", "security", "surface"];
|
|
51
|
+
export const laneResults = ["pass", "fail", "blocked"];
|
|
52
|
+
export function isReviewLane(value) {
|
|
53
|
+
return typeof value === "string" && reviewLanes.includes(value);
|
|
54
|
+
}
|
|
55
|
+
export function isLaneResult(value) {
|
|
56
|
+
return typeof value === "string" && laneResults.includes(value);
|
|
57
|
+
}
|
|
58
|
+
/** One lane's colour on the border: its own hue once it reports a pass, error once it fails, dim until then. */
|
|
59
|
+
export function laneColor(lane, reports, palette) {
|
|
60
|
+
const result = reports[lane];
|
|
61
|
+
if (result === undefined)
|
|
62
|
+
return palette.borderSubtle;
|
|
63
|
+
if (result !== "pass")
|
|
64
|
+
return palette.error;
|
|
65
|
+
const hues = [palette.primary, palette.info, palette.accent, palette.secondary, palette.success];
|
|
66
|
+
return hues[reviewLanes.indexOf(lane)] ?? palette.success;
|
|
67
|
+
}
|
|
68
|
+
/** The corner label of the review look: how many of the five lanes have reported, never a guess. */
|
|
69
|
+
export function reviewMark(reports, palette) {
|
|
70
|
+
const reported = reviewLanes.filter((lane) => reports[lane] !== undefined).length;
|
|
71
|
+
const failed = reviewLanes.some((lane) => reports[lane] !== undefined && reports[lane] !== "pass");
|
|
72
|
+
return [{ text: `◇ REVIEW ${reported}/${reviewLanes.length}`, fg: failed ? palette.error : palette.info, bold: true }];
|
|
73
|
+
}
|
|
@@ -20,6 +20,7 @@ export type WorkInput = {
|
|
|
20
20
|
readonly status: string;
|
|
21
21
|
readonly iterations: number;
|
|
22
22
|
readonly plan?: PlanChecklist;
|
|
23
|
+
readonly boundary?: boolean;
|
|
23
24
|
};
|
|
24
25
|
export type ResearchStage = "decompose" | "wave" | "expand" | "verify" | "synthesis";
|
|
25
26
|
/** The latest litresearch run folder: what its journal, claim graph and report say so far. */
|
|
@@ -143,6 +144,7 @@ export declare function readWorkState(raw: unknown, sessionID: string): {
|
|
|
143
144
|
readonly status: string;
|
|
144
145
|
readonly iterations: number;
|
|
145
146
|
readonly planPath: string;
|
|
147
|
+
readonly boundary?: true;
|
|
146
148
|
} | undefined;
|
|
147
149
|
type LedgerCriterion = {
|
|
148
150
|
readonly id: string;
|
|
@@ -109,7 +109,9 @@ export function readWorkState(raw, sessionID) {
|
|
|
109
109
|
return undefined;
|
|
110
110
|
const history = Array.isArray(raw.history) ? raw.history.filter(isRecord) : [];
|
|
111
111
|
const plan = isRecord(raw.plan) ? text(raw.plan.path) : "";
|
|
112
|
-
|
|
112
|
+
// A paused run holding a boundary waits for the user's typed approval.
|
|
113
|
+
const boundary = status === "paused" && isRecord(raw.pendingBoundary);
|
|
114
|
+
return { status, iterations: history.filter((entry) => entry.type === "work.progressed").length, planPath: plan, ...(boundary ? { boundary: true } : {}) };
|
|
113
115
|
}
|
|
114
116
|
// The ledger CLI accepts `passed`, `failed` and `completed` as spellings of its own statuses; a goals.json
|
|
115
117
|
// the agent writes by hand also marks a finished criterion `completed` or `done`.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { SkillChip } from "./mission-skills.ts";
|
|
2
|
+
import type { Line, Segment } from "./mission-text.ts";
|
|
3
|
+
import type { MissionView } from "./mission-model.ts";
|
|
4
|
+
export declare const rainbowTickMs = 250;
|
|
5
|
+
export type Rainbow = {
|
|
6
|
+
readonly hue: number;
|
|
7
|
+
};
|
|
8
|
+
/** True while the session's current discipline or a chip lit by this turn is lit-adhd or lit-rewind. */
|
|
9
|
+
export declare function rainbowActive(view: Pick<MissionView, "active" | "discipline">, chips: readonly SkillChip[], busy: boolean | undefined): boolean;
|
|
10
|
+
/** The rewind look outlasts its turn so the band stays lit while the user reads the brief. */
|
|
11
|
+
export declare const rainbowLingerMs: number;
|
|
12
|
+
/** Shown while lit-adhd runs, then until the next prompt, for at most rainbowLingerMs after it was last seen. */
|
|
13
|
+
export declare function rainbowShows(active: boolean, seenAt: number | undefined, lastPromptAt: number | undefined, now: number): boolean;
|
|
14
|
+
/** The top-border mark of the rewind look: REWIND « with a hue per letter, then a four-cell sparkle trail. */
|
|
15
|
+
export declare function rewindMark(rainbow: Rainbow): Line;
|
|
16
|
+
/** True for the skill names that carry the rewind look. */
|
|
17
|
+
export declare const isRainbowSkill: (name: string) => boolean;
|
|
18
|
+
/** A pill cell's fill at this hue: the rainbow colour lifted toward white, so the dark ink keeps 4.5:1. */
|
|
19
|
+
export declare function rainbowCell(hue: number): string;
|
|
20
|
+
/**
|
|
21
|
+
* A pill drawn cell by cell through the rainbow: each cell a step further round the wheel, dark bold ink
|
|
22
|
+
* on top. Only the colours of fixed cells move, so the text and width never change and nothing flashes.
|
|
23
|
+
*/
|
|
24
|
+
export declare function rainbowPill(text: string, rainbow: Rainbow, offset: number): Segment[];
|
|
25
|
+
/** Plain text with each character a step round the wheel, for labels that sit on the card's ground. */
|
|
26
|
+
export declare function rainbowText(text: string, rainbow: Rainbow, offset: number): Segment[];
|
|
27
|
+
/** Whether the rainbow may play at all: tui.json motion is not off and NO_COLOR is unset or empty. */
|
|
28
|
+
export declare function rainbowAllowed(motion: boolean, noColor: string | undefined): boolean;
|
|
29
|
+
export declare function rainbowAt(now: number): Rainbow;
|
|
30
|
+
/** An OKLCH colour at the rainbow's fixed lightness and chroma, as `#rrggbb`. */
|
|
31
|
+
export declare function rainbowColor(hue: number): string;
|
|
32
|
+
/** WCAG relative luminance of a `#rrggbb` colour, 0 to 1. */
|
|
33
|
+
export declare function relativeLuminance(hex: string): number;
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// The look of the band while lit-adhd (alias lit-rewind) is the discipline at work: the border, the LIT
|
|
2
|
+
// word and the gauges take a colour that walks round the hue wheel. Every colour is drawn at one fixed
|
|
3
|
+
// OKLCH lightness, so the brightness of the band never swings; only the hue moves, a few degrees per
|
|
4
|
+
// 250 ms tick, which is four small colour steps a second and no flash at all. It is off under reduced
|
|
5
|
+
// motion (tui.json `motion: false`) and NO_COLOR, and it never reaches a session that is not rewinding.
|
|
6
|
+
import { activeSkillLook, lookOfSkill } from "./mission-looks.js";
|
|
7
|
+
import { mixHex } from "./mission-motion.js";
|
|
8
|
+
export const rainbowTickMs = 250;
|
|
9
|
+
/** Degrees of hue per second: a full turn every 12 s, 7.5 degrees per tick. */
|
|
10
|
+
const degreesPerSecond = 30;
|
|
11
|
+
const lightness = 0.8;
|
|
12
|
+
const chroma = 0.115;
|
|
13
|
+
/** True while the session's current discipline or a chip lit by this turn is lit-adhd or lit-rewind. */
|
|
14
|
+
export function rainbowActive(view, chips, busy) {
|
|
15
|
+
return activeSkillLook(view, chips, busy) === "rewind";
|
|
16
|
+
}
|
|
17
|
+
/** The rewind look outlasts its turn so the band stays lit while the user reads the brief. */
|
|
18
|
+
export const rainbowLingerMs = 15 * 60_000;
|
|
19
|
+
/** Shown while lit-adhd runs, then until the next prompt, for at most rainbowLingerMs after it was last seen. */
|
|
20
|
+
export function rainbowShows(active, seenAt, lastPromptAt, now) {
|
|
21
|
+
if (active)
|
|
22
|
+
return true;
|
|
23
|
+
if (seenAt === undefined || now - seenAt > rainbowLingerMs)
|
|
24
|
+
return false;
|
|
25
|
+
return lastPromptAt === undefined || lastPromptAt <= seenAt;
|
|
26
|
+
}
|
|
27
|
+
const sparkleFrames = ["✦·⋆·", "·✦·⋆", "⋆·✦·", "·⋆·✧"];
|
|
28
|
+
/** The top-border mark of the rewind look: REWIND « with a hue per letter, then a four-cell sparkle trail. */
|
|
29
|
+
export function rewindMark(rainbow) {
|
|
30
|
+
const letters = [..."REWIND"].map((letter, index) => ({ text: letter, fg: rainbowColor(rainbow.hue + index * 45), bold: true }));
|
|
31
|
+
// The hue advances one step per tick, so it also clocks the trail.
|
|
32
|
+
const tick = Math.floor(rainbow.hue / ((degreesPerSecond * rainbowTickMs) / 1000));
|
|
33
|
+
const trail = sparkleFrames[tick % sparkleFrames.length] ?? sparkleFrames[0];
|
|
34
|
+
return [...letters, { text: " «", fg: rainbowColor(rainbow.hue + 270) }, { text: " ", fg: rainbowColor(rainbow.hue) }, ...[...trail].map((glyph, index) => ({ text: glyph, fg: rainbowColor(rainbow.hue + 300 + index * 20) }))];
|
|
35
|
+
}
|
|
36
|
+
/** True for the skill names that carry the rewind look. */
|
|
37
|
+
export const isRainbowSkill = (name) => lookOfSkill(name) === "rewind";
|
|
38
|
+
/** Degrees of hue one pill spans from its first cell to its last. */
|
|
39
|
+
const pillSpread = 126;
|
|
40
|
+
const pillInk = "#14161c";
|
|
41
|
+
/** A pill cell's fill at this hue: the rainbow colour lifted toward white, so the dark ink keeps 4.5:1. */
|
|
42
|
+
export function rainbowCell(hue) {
|
|
43
|
+
return mixHex(rainbowColor(hue), "#ffffff", 0.3);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A pill drawn cell by cell through the rainbow: each cell a step further round the wheel, dark bold ink
|
|
47
|
+
* on top. Only the colours of fixed cells move, so the text and width never change and nothing flashes.
|
|
48
|
+
*/
|
|
49
|
+
export function rainbowPill(text, rainbow, offset) {
|
|
50
|
+
const chars = [...text];
|
|
51
|
+
const step = chars.length <= 1 ? 0 : pillSpread / (chars.length - 1);
|
|
52
|
+
return chars.map((char, index) => ({ text: char, fg: pillInk, bg: rainbowCell(rainbow.hue + offset + index * step), bold: true }));
|
|
53
|
+
}
|
|
54
|
+
/** Plain text with each character a step round the wheel, for labels that sit on the card's ground. */
|
|
55
|
+
export function rainbowText(text, rainbow, offset) {
|
|
56
|
+
return [...text].map((char, index) => ({ text: char, fg: rainbowColor(rainbow.hue + offset + index * 24), bold: true }));
|
|
57
|
+
}
|
|
58
|
+
/** Whether the rainbow may play at all: tui.json motion is not off and NO_COLOR is unset or empty. */
|
|
59
|
+
export function rainbowAllowed(motion, noColor) {
|
|
60
|
+
return motion && (noColor === undefined || noColor === "");
|
|
61
|
+
}
|
|
62
|
+
export function rainbowAt(now) {
|
|
63
|
+
return { hue: ((now / 1000) * degreesPerSecond) % 360 };
|
|
64
|
+
}
|
|
65
|
+
function encode(linear) {
|
|
66
|
+
const clamped = Math.min(1, Math.max(0, linear));
|
|
67
|
+
return clamped <= 0.0031308 ? 12.92 * clamped : 1.055 * clamped ** (1 / 2.4) - 0.055;
|
|
68
|
+
}
|
|
69
|
+
/** An OKLCH colour at the rainbow's fixed lightness and chroma, as `#rrggbb`. */
|
|
70
|
+
export function rainbowColor(hue) {
|
|
71
|
+
const radians = (((hue % 360) + 360) % 360) * (Math.PI / 180);
|
|
72
|
+
const a = chroma * Math.cos(radians);
|
|
73
|
+
const b = chroma * Math.sin(radians);
|
|
74
|
+
const l = (lightness + 0.3963377774 * a + 0.2158037573 * b) ** 3;
|
|
75
|
+
const m = (lightness - 0.1055613458 * a - 0.0638541728 * b) ** 3;
|
|
76
|
+
const s = (lightness - 0.0894841775 * a - 1.291485548 * b) ** 3;
|
|
77
|
+
const red = 4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s;
|
|
78
|
+
const green = -1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s;
|
|
79
|
+
const blue = -0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s;
|
|
80
|
+
return "#" + [red, green, blue].map((channel) => Math.round(encode(channel) * 255).toString(16).padStart(2, "0")).join("");
|
|
81
|
+
}
|
|
82
|
+
function linear(channel) {
|
|
83
|
+
const value = channel / 255;
|
|
84
|
+
return value <= 0.04045 ? value / 12.92 : ((value + 0.055) / 1.055) ** 2.4;
|
|
85
|
+
}
|
|
86
|
+
/** WCAG relative luminance of a `#rrggbb` colour, 0 to 1. */
|
|
87
|
+
export function relativeLuminance(hex) {
|
|
88
|
+
const channels = [1, 3, 5].map((index) => linear(Number.parseInt(hex.slice(index, index + 2), 16)));
|
|
89
|
+
return 0.2126 * channels[0] + 0.7152 * channels[1] + 0.0722 * channels[2];
|
|
90
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { type LiveStep, type MissionInput, type ResearchInput, type TodoInput, type WorkInput } from "./mission-model.ts";
|
|
2
|
+
import { type LaneReports, type ReviewLane, type LaneResult } from "./mission-looks.ts";
|
|
2
3
|
import { type SkillChip } from "./mission-skills.ts";
|
|
4
|
+
import { type ThreadRow, type ThreadSource } from "./mission-threads.ts";
|
|
3
5
|
type MessageLike = {
|
|
4
6
|
readonly id: string;
|
|
5
7
|
readonly role: string;
|
|
@@ -88,11 +90,57 @@ export type CardInput = {
|
|
|
88
90
|
readonly busy: boolean;
|
|
89
91
|
readonly contextPercent: number | null;
|
|
90
92
|
readonly branch?: string;
|
|
93
|
+
/** Two or more lanes of work, from the stack, `task` calls and sibling sessions. */
|
|
94
|
+
readonly threads?: readonly ThreadRow[];
|
|
95
|
+
/** Another session here moved lately, so an idle card can name `lit adhd`. */
|
|
96
|
+
readonly earlier?: boolean;
|
|
97
|
+
/** The lanes review-work has reported since its latest start; present once a review ran here. */
|
|
98
|
+
readonly lanes?: LaneReports;
|
|
99
|
+
/** OpenCode holds a permission request or a question for this session. */
|
|
100
|
+
readonly asked?: boolean;
|
|
101
|
+
/** The last message is the user's. */
|
|
102
|
+
readonly userSpokeLast?: boolean;
|
|
103
|
+
/** The last reply opens with `BLOCKED:`. */
|
|
104
|
+
readonly blockedReply?: boolean;
|
|
91
105
|
};
|
|
106
|
+
/** A completed `review-work` call with action `lane`: one lane's reported result. */
|
|
107
|
+
export declare function readLaneReport(part: unknown): {
|
|
108
|
+
readonly lane: ReviewLane;
|
|
109
|
+
readonly result: LaneResult;
|
|
110
|
+
} | undefined;
|
|
111
|
+
/**
|
|
112
|
+
* True when the reply's first line of prose opens with `BLOCKED:`. The ignition mark comes first on the
|
|
113
|
+
* host (a fenced LIT block from experimental.text.complete, and the model's `LIT IGNITED` line), so those
|
|
114
|
+
* lines and blank ones are passed over; anything else ends the search.
|
|
115
|
+
*/
|
|
116
|
+
export declare function opensBlocked(parts: readonly unknown[]): boolean;
|
|
92
117
|
/**
|
|
93
118
|
* What the band card shows beside the mission: the session's skill chips, whether a turn is running,
|
|
94
119
|
* context use and git branch. OpenCode keeps each session's last `session.status` ({ type: "busy" },
|
|
95
120
|
* "retry" or "idle"); a session it has not seen run has none, which counts as not running.
|
|
96
121
|
*/
|
|
97
122
|
export declare function readCardInput(state: MissionStateSource, sessionID: string): CardInput;
|
|
123
|
+
export declare const earlierWorkWindowMs: number;
|
|
124
|
+
/**
|
|
125
|
+
* The lanes beside this session's own mission: its workflow stack frames, its running `task` calls and
|
|
126
|
+
* the stacks of other sessions in the workspace that moved in the last fifteen minutes. Only counts
|
|
127
|
+
* that are real totals are carried; the input of a tool call is read for its description alone.
|
|
128
|
+
*/
|
|
129
|
+
export declare function readThreadSources(state: MissionStateSource, sessionID: string, root: string | undefined, now: number): ThreadSource[];
|
|
130
|
+
/**
|
|
131
|
+
* Whether earlier work exists for the band's idle hint and the one suggestion: another session here
|
|
132
|
+
* touched its workflow stack within `withinMs`.
|
|
133
|
+
*/
|
|
134
|
+
/** When the session's last message and its last user prompt were created, from the reactive store. */
|
|
135
|
+
export declare function messageTimes(state: MissionStateSource, sessionID: string): {
|
|
136
|
+
readonly lastAt?: number;
|
|
137
|
+
readonly lastPromptAt?: number;
|
|
138
|
+
};
|
|
139
|
+
/** The way back in is offered after this long without a message in the session. */
|
|
140
|
+
export declare const adhdBreakMs: number;
|
|
141
|
+
/** The idle card names lit adhd in a fresh session or after a break, only when earlier work exists. */
|
|
142
|
+
export declare function wayBackDue(lastActivityAt: number | undefined, now: number, earlier: boolean): boolean;
|
|
143
|
+
/** At most one start suggestion per workspace per local day; records the day when it answers true. */
|
|
144
|
+
export declare function adhdSuggestDue(days: Map<string, string>, root: string, now: number): boolean;
|
|
145
|
+
export declare function hasEarlierWork(root: string | undefined, sessionID: string, now: number, withinMs: number): boolean;
|
|
98
146
|
export {};
|