jig-ui 0.15.0 → 0.16.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/CHANGELOG.md +31 -0
- package/README.md +6 -1
- package/dist/index.js +61 -9
- package/package.json +1 -1
- package/rules/02-tokens.md +16 -8
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,36 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.16.0
|
|
4
|
+
|
|
5
|
+
With more than one mode, every barrel names its mode, and none sits in the
|
|
6
|
+
global stylesheet.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- **`theme.<mode>.css` for every mode, once there are two.** `init` kept
|
|
11
|
+
`theme.css` for the first mode and left it wired into the global stylesheet,
|
|
12
|
+
so a second mode's routes loaded the first mode's tokens globally and their
|
|
13
|
+
own from their layout, and got whichever came last. Now each barrel names its
|
|
14
|
+
mode, the global stylesheet keeps only what every route shares (Tailwind and
|
|
15
|
+
`utilities.css`), and each route's layout imports its own barrel. A project
|
|
16
|
+
with one mode is unchanged: `theme.css`, wired globally.
|
|
17
|
+
- **A project gaining a second mode is migrated.** An unedited `theme.css`
|
|
18
|
+
becomes `theme.<first mode>.css`, and every stylesheet import that resolves
|
|
19
|
+
to it is removed, found by where it points rather than by guessing the file.
|
|
20
|
+
An edited one is left, with a note on where its edits belong. `check` names a
|
|
21
|
+
`theme.css` left beside two modes.
|
|
22
|
+
|
|
23
|
+
## 0.15.1
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- **A question anywhere in the message is a question.** 0.15.0 let an owner
|
|
28
|
+
pause through only when the message's last line ended in `?`. `decide` asks
|
|
29
|
+
its round, then shows an example answer, then says "answer for your own
|
|
30
|
+
project", so the message ends on a full stop, and the gate refused the first
|
|
31
|
+
live `/jig decide` three times. Any sentence ending in `?` now counts; a `?`
|
|
32
|
+
in a URL or a code span does not.
|
|
33
|
+
|
|
3
34
|
## 0.15.0
|
|
4
35
|
|
|
5
36
|
The gate holds an agent on warnings, waits while it asks the owner a question,
|
package/README.md
CHANGED
|
@@ -123,6 +123,11 @@ jig.config.json route → mode map
|
|
|
123
123
|
.jig/state.json bookkeeping — version, modes, checksums
|
|
124
124
|
```
|
|
125
125
|
|
|
126
|
+
Declare a second mode and every barrel names its mode instead —
|
|
127
|
+
`theme.editorial.css`, `theme.operator.css` — and none sits in the global
|
|
128
|
+
stylesheet. Each route's layout imports the barrel for its mode; `init` removes
|
|
129
|
+
the import it added and prints which barrel each surface takes.
|
|
130
|
+
|
|
126
131
|
Nothing you wrote is touched beyond that one import line. Re-running `init`
|
|
127
132
|
never overwrites a config or brand file you have edited.
|
|
128
133
|
|
|
@@ -604,7 +609,7 @@ treatment.
|
|
|
604
609
|
| `rules/05-copy.md` | Interface text rules |
|
|
605
610
|
| `<css dir>/jig/brand.*.css` | Identity. One per project. |
|
|
606
611
|
| `<css dir>/jig/mode.*.css` | Density, scale, rhythm, motion |
|
|
607
|
-
| `<css dir>/jig/theme.css` | The barrel — brand + mode. This is what you import. |
|
|
612
|
+
| `<css dir>/jig/theme.css` | The barrel — brand + mode. This is what you import. With more than one mode, `theme.<mode>.css`, one per mode, each imported by its routes' layout. |
|
|
608
613
|
| `.jig/state.json` | What `init` wrote, with checksums. `update` reads it to leave your edits alone. |
|
|
609
614
|
| `.jig/specs/`, `.jig/mockups/`, `.jig/critique/` | The design loop's record: what was agreed, what was drawn, what the review found. |
|
|
610
615
|
|
package/dist/index.js
CHANGED
|
@@ -3911,10 +3911,20 @@ function modeWiringProblems(projectRoot) {
|
|
|
3911
3911
|
if (modes.length === 0) return [];
|
|
3912
3912
|
const dir = dirname6(config.brand);
|
|
3913
3913
|
const rel = (f) => dir === "." ? f : `${dir}/${f}`;
|
|
3914
|
-
|
|
3914
|
+
const multi = modes.length > 1;
|
|
3915
|
+
const barrelOf = (mode) => multi ? `theme.${mode}.css` : "theme.css";
|
|
3916
|
+
const anyBarrel = ["theme.css", ...modes.map((m) => `theme.${m}.css`)].some((f) => existsSync12(join22(projectRoot, rel(f))));
|
|
3917
|
+
if (!anyBarrel) return [];
|
|
3915
3918
|
const problems = [];
|
|
3916
|
-
|
|
3917
|
-
|
|
3919
|
+
if (multi && existsSync12(join22(projectRoot, rel("theme.css")))) {
|
|
3920
|
+
problems.push({
|
|
3921
|
+
mode: modes[0],
|
|
3922
|
+
barrel: rel("theme.css"),
|
|
3923
|
+
message: `jig.config.json declares ${modes.length} modes, so each barrel names its mode: ${rel("theme.css")} should be ${rel(barrelOf(modes[0]))}, imported by the layouts that serve it and not by the global stylesheet`
|
|
3924
|
+
});
|
|
3925
|
+
}
|
|
3926
|
+
modes.forEach((mode) => {
|
|
3927
|
+
const barrel = rel(barrelOf(mode));
|
|
3918
3928
|
const modeFile = `mode.${mode}.css`;
|
|
3919
3929
|
let body;
|
|
3920
3930
|
try {
|
|
@@ -5084,7 +5094,8 @@ ${configRelPath} already exists and was not created by 'jig init' (or has been e
|
|
|
5084
5094
|
}
|
|
5085
5095
|
const modeAbsPath = modeAbsPaths[primaryMode];
|
|
5086
5096
|
const brandFileOnly = brandRelPath.split("/").pop();
|
|
5087
|
-
const
|
|
5097
|
+
const multiMode = declaredModes.length > 1;
|
|
5098
|
+
const barrelFor = (mode) => multiMode ? `theme.${mode}.css` : "theme.css";
|
|
5088
5099
|
for (const mode of declaredModes) {
|
|
5089
5100
|
const rel = relKey(...tokensRelDir, barrelFor(mode));
|
|
5090
5101
|
const abs = join28(opts.projectRoot, ...tokensRelDir, barrelFor(mode));
|
|
@@ -5098,6 +5109,20 @@ ${configRelPath} already exists and was not created by 'jig init' (or has been e
|
|
|
5098
5109
|
writeFileSync5(abs, content, "utf8");
|
|
5099
5110
|
files[rel] = checksum(content);
|
|
5100
5111
|
}
|
|
5112
|
+
if (multiMode) {
|
|
5113
|
+
const rel = relKey(...tokensRelDir, "theme.css");
|
|
5114
|
+
const abs = join28(opts.projectRoot, ...tokensRelDir, "theme.css");
|
|
5115
|
+
if (existsSync15(abs)) {
|
|
5116
|
+
const state = fileState(opts.projectRoot, abs, rel, initManifest);
|
|
5117
|
+
if (state.tracked && !state.modified) {
|
|
5118
|
+
rmSync2(abs);
|
|
5119
|
+
delete files[rel];
|
|
5120
|
+
log(` Removed ${rel}: with more than one mode, each barrel names its mode (${relKey(...tokensRelDir, barrelFor(primaryMode))}).`);
|
|
5121
|
+
} else {
|
|
5122
|
+
log(` ${rel} has been edited, so it is left alone. With more than one mode it is replaced by ${relKey(...tokensRelDir, barrelFor(primaryMode))}; move your edits there and delete it.`);
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
}
|
|
5101
5126
|
if (detection.cssSystem === "tailwind-v4") {
|
|
5102
5127
|
const declared = declaredTokenNames(
|
|
5103
5128
|
[brandAbsPath, modeAbsPath].map((f) => {
|
|
@@ -5148,8 +5173,9 @@ ${configRelPath} already exists and was not created by 'jig init' (or has been e
|
|
|
5148
5173
|
}
|
|
5149
5174
|
}
|
|
5150
5175
|
}
|
|
5151
|
-
if (
|
|
5152
|
-
log("\nOne barrel per
|
|
5176
|
+
if (multiMode) {
|
|
5177
|
+
log("\nOne barrel per mode, and none in the global stylesheet. Import each in the layout");
|
|
5178
|
+
log("that serves its routes, and keep Tailwind and utilities.css in the global one:");
|
|
5153
5179
|
for (const surface of effectiveConfig.surfaces) {
|
|
5154
5180
|
log(` '${surface.match}' \u2192 ${relKey(...tokensRelDir, barrelFor(surface.mode))}`);
|
|
5155
5181
|
}
|
|
@@ -5174,7 +5200,33 @@ ${configRelPath} already exists and was not created by 'jig init' (or has been e
|
|
|
5174
5200
|
}
|
|
5175
5201
|
const wireTarget = findWireTarget(detection);
|
|
5176
5202
|
let wiring;
|
|
5177
|
-
if (
|
|
5203
|
+
if (multiMode) {
|
|
5204
|
+
const bare = join28(opts.projectRoot, ...tokensRelDir, "theme.css");
|
|
5205
|
+
for (const file of detection.cssFiles.filter((f) => !isTokenLayerFile(f))) {
|
|
5206
|
+
const abs = join28(opts.projectRoot, file);
|
|
5207
|
+
try {
|
|
5208
|
+
const before = readFileSync23(abs, "utf8");
|
|
5209
|
+
const after = before.split("\n").filter((l) => {
|
|
5210
|
+
const m = /^\s*@import\s+["']([^"']+)["'];?\s*$/.exec(l);
|
|
5211
|
+
return !(m && !/^[a-z]+:|^\//i.test(m[1]) && resolve2(dirname7(abs), m[1]) === bare);
|
|
5212
|
+
}).join("\n");
|
|
5213
|
+
if (after !== before) {
|
|
5214
|
+
writeFileSync5(abs, after, "utf8");
|
|
5215
|
+
log(`
|
|
5216
|
+
Unwired ${file}: removed its import of ${relKey(...tokensRelDir, "theme.css")}.`);
|
|
5217
|
+
log(` With more than one mode, the global stylesheet imports no barrel: every route would carry that mode's tokens.`);
|
|
5218
|
+
}
|
|
5219
|
+
} catch (err) {
|
|
5220
|
+
log(`
|
|
5221
|
+
Could not edit ${file}: ${err.message}. Remove its import of ${relKey(...tokensRelDir, "theme.css")} by hand.`);
|
|
5222
|
+
}
|
|
5223
|
+
}
|
|
5224
|
+
wiring = {
|
|
5225
|
+
target: null,
|
|
5226
|
+
status: "per-route",
|
|
5227
|
+
snippet: effectiveConfig.surfaces.map((s) => `${s.match} \u2192 ${relKey(...tokensRelDir, barrelFor(s.mode))}`).join("\n")
|
|
5228
|
+
};
|
|
5229
|
+
} else if (wireTarget) {
|
|
5178
5230
|
const targetAbsDir = dirname7(join28(opts.projectRoot, wireTarget));
|
|
5179
5231
|
const barrelImport = relativeImportPath(
|
|
5180
5232
|
targetAbsDir,
|
|
@@ -6117,8 +6169,8 @@ function lastAssistantText(transcriptPath) {
|
|
|
6117
6169
|
}
|
|
6118
6170
|
function asksOwner(text) {
|
|
6119
6171
|
if (!text) return false;
|
|
6120
|
-
const
|
|
6121
|
-
return
|
|
6172
|
+
const prose = text.replace(/```[\s\S]*?```/g, "").replace(/`[^`\n]*`/g, "");
|
|
6173
|
+
return /\?(?=[\s)\]"'*_]|$)/.test(prose);
|
|
6122
6174
|
}
|
|
6123
6175
|
var ASKS_THE_OWNER = /* @__PURE__ */ new Set(["decide", "spec", "mockup"]);
|
|
6124
6176
|
function commandProblems(root, command) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jig-ui",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.0",
|
|
4
4
|
"description": "A design system for coding agents. 130 numbered UI rules, brand x mode design tokens, and an installer for Claude Code, Codex, Cursor and opencode.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
package/rules/02-tokens.md
CHANGED
|
@@ -25,9 +25,11 @@ lives in `src/styles/`, `app/assets/stylesheets/jig/` in a Rails app. Set `brand
|
|
|
25
25
|
in `jig.config.json` to put it elsewhere. Projects set up before 0.7.0 keep their
|
|
26
26
|
`.jig/tokens/` layout; `update` does not move them.
|
|
27
27
|
|
|
28
|
-
So **never hardcode that path**. `init` writes
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
So **never hardcode that path**. `init` writes a barrel, `theme.css`, which imports
|
|
29
|
+
the brand and the mode in the right order — import the barrel, and relocating the
|
|
30
|
+
layer changes one line instead of every stylesheet. With more than one mode, every
|
|
31
|
+
barrel names its mode instead (`theme.editorial.css`, `theme.operator.css`); see
|
|
32
|
+
"Multiple modes in one app" below:
|
|
31
33
|
|
|
32
34
|
```css
|
|
33
35
|
/* src/styles/jig/theme.css — written by init */
|
|
@@ -512,8 +514,14 @@ the same token names with different values, so importing all three into one
|
|
|
512
514
|
document leaves only the last — the other two are inert. That is the mechanical
|
|
513
515
|
reason behind the seam rule below.
|
|
514
516
|
|
|
515
|
-
**Multiple modes in one app** — scope by route, not by class.
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
517
|
+
**Multiple modes in one app** — scope by route, not by class. Every barrel names its
|
|
518
|
+
mode, `jig/theme.<mode>.css`, and each route's layout or entry point imports the
|
|
519
|
+
one for its mode. The global stylesheet imports **no** barrel: one imported there
|
|
520
|
+
puts that mode's tokens under every route, so the operator pages carry editorial's
|
|
521
|
+
too and get whichever loaded last. It keeps what every route shares — Tailwind and
|
|
522
|
+
`utilities.css`, whose aliases name tokens rather than values and so hold for any
|
|
523
|
+
mode. When a second mode is declared, `init` replaces `theme.css` with
|
|
524
|
+
`theme.<first mode>.css` and removes the import it had wired, then prints which
|
|
525
|
+
barrel each surface imports; which layout serves `/admin/**` is your routing, which
|
|
526
|
+
it cannot see. Do not attempt to nest two modes in one document (`01-modes.md`,
|
|
527
|
+
seam rules).
|