@grainulation/grainulation 1.1.0 → 1.1.2
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/lib/doctor.js +19 -20
- package/lib/router.js +6 -6
- package/lib/server.mjs +10 -4
- package/lib/setup.js +2 -2
- package/package.json +3 -8
- package/public/grainulation-tokens.css +0 -316
- package/public/index.html +0 -1139
package/lib/doctor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
const {
|
|
2
|
-
const { existsSync } = require('node:fs');
|
|
1
|
+
const { execFileSync } = require('node:child_process');
|
|
2
|
+
const { existsSync, readFileSync, readdirSync } = require('node:fs');
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { getInstallable } = require('./ecosystem');
|
|
5
5
|
|
|
@@ -17,8 +17,8 @@ const { getInstallable } = require('./ecosystem');
|
|
|
17
17
|
*/
|
|
18
18
|
function checkGlobal(packageName) {
|
|
19
19
|
try {
|
|
20
|
-
const out =
|
|
21
|
-
stdio: 'pipe',
|
|
20
|
+
const out = execFileSync('npm', ['list', '-g', packageName, '--depth=0'], {
|
|
21
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
22
22
|
encoding: 'utf-8',
|
|
23
23
|
timeout: 5000,
|
|
24
24
|
});
|
|
@@ -35,8 +35,8 @@ function checkGlobal(packageName) {
|
|
|
35
35
|
*/
|
|
36
36
|
function checkNpxCache(packageName) {
|
|
37
37
|
try {
|
|
38
|
-
const prefix =
|
|
39
|
-
stdio: 'pipe',
|
|
38
|
+
const prefix = execFileSync('npm', ['config', 'get', 'cache'], {
|
|
39
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
40
40
|
encoding: 'utf-8',
|
|
41
41
|
timeout: 5000,
|
|
42
42
|
}).trim();
|
|
@@ -44,14 +44,13 @@ function checkNpxCache(packageName) {
|
|
|
44
44
|
if (!existsSync(npxDir)) return null;
|
|
45
45
|
|
|
46
46
|
// npx cache has hash-named directories, each with node_modules
|
|
47
|
-
const { readdirSync } = require('node:fs');
|
|
48
47
|
const entries = readdirSync(npxDir, { withFileTypes: true });
|
|
49
48
|
for (const entry of entries) {
|
|
50
49
|
if (!entry.isDirectory()) continue;
|
|
51
50
|
const pkgJson = path.join(npxDir, entry.name, 'node_modules', packageName, 'package.json');
|
|
52
51
|
if (existsSync(pkgJson)) {
|
|
53
52
|
try {
|
|
54
|
-
const pkg = JSON.parse(
|
|
53
|
+
const pkg = JSON.parse(readFileSync(pkgJson, 'utf-8'));
|
|
55
54
|
return { version: pkg.version || 'installed', method: 'npx cache' };
|
|
56
55
|
} catch {
|
|
57
56
|
return { version: 'installed', method: 'npx cache' };
|
|
@@ -72,7 +71,7 @@ function checkLocal(packageName) {
|
|
|
72
71
|
try {
|
|
73
72
|
const pkgJson = path.join(process.cwd(), 'node_modules', packageName, 'package.json');
|
|
74
73
|
if (existsSync(pkgJson)) {
|
|
75
|
-
const pkg = JSON.parse(
|
|
74
|
+
const pkg = JSON.parse(readFileSync(pkgJson, 'utf-8'));
|
|
76
75
|
return { version: pkg.version || 'installed', method: 'local' };
|
|
77
76
|
}
|
|
78
77
|
return null;
|
|
@@ -98,7 +97,7 @@ function checkSource(packageName) {
|
|
|
98
97
|
const pkgJson = candidate.endsWith('package.json') ? candidate : path.join(candidate, 'package.json');
|
|
99
98
|
if (existsSync(pkgJson)) {
|
|
100
99
|
try {
|
|
101
|
-
const pkg = JSON.parse(
|
|
100
|
+
const pkg = JSON.parse(readFileSync(pkgJson, 'utf-8'));
|
|
102
101
|
if (pkg.name === packageName) {
|
|
103
102
|
return { version: pkg.version || 'installed', method: 'source' };
|
|
104
103
|
}
|
|
@@ -117,8 +116,8 @@ function checkSource(packageName) {
|
|
|
117
116
|
*/
|
|
118
117
|
function checkNpxNoInstall(packageName) {
|
|
119
118
|
try {
|
|
120
|
-
const out =
|
|
121
|
-
stdio: 'pipe',
|
|
119
|
+
const out = execFileSync('npx', ['--no-install', packageName, '--version'], {
|
|
120
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
122
121
|
encoding: 'utf-8',
|
|
123
122
|
timeout: 5000,
|
|
124
123
|
}).trim();
|
|
@@ -163,8 +162,8 @@ function getNodeVersion() {
|
|
|
163
162
|
|
|
164
163
|
function getNpmVersion() {
|
|
165
164
|
try {
|
|
166
|
-
return
|
|
167
|
-
stdio: 'pipe',
|
|
165
|
+
return execFileSync('npm', ['--version'], {
|
|
166
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
168
167
|
encoding: 'utf-8',
|
|
169
168
|
timeout: 5000,
|
|
170
169
|
}).trim();
|
|
@@ -175,8 +174,8 @@ function getNpmVersion() {
|
|
|
175
174
|
|
|
176
175
|
function getPnpmVersion() {
|
|
177
176
|
try {
|
|
178
|
-
return
|
|
179
|
-
stdio: 'pipe',
|
|
177
|
+
return execFileSync('pnpm', ['--version'], {
|
|
178
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
180
179
|
encoding: 'utf-8',
|
|
181
180
|
timeout: 5000,
|
|
182
181
|
}).trim();
|
|
@@ -187,8 +186,8 @@ function getPnpmVersion() {
|
|
|
187
186
|
|
|
188
187
|
function getBiomeVersion() {
|
|
189
188
|
try {
|
|
190
|
-
const out =
|
|
191
|
-
stdio: 'pipe',
|
|
189
|
+
const out = execFileSync('npx', ['biome', '--version'], {
|
|
190
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
192
191
|
encoding: 'utf-8',
|
|
193
192
|
timeout: 5000,
|
|
194
193
|
}).trim();
|
|
@@ -201,8 +200,8 @@ function getBiomeVersion() {
|
|
|
201
200
|
|
|
202
201
|
function getHooksPath() {
|
|
203
202
|
try {
|
|
204
|
-
return
|
|
205
|
-
stdio: 'pipe',
|
|
203
|
+
return execFileSync('git', ['config', 'core.hooksPath'], {
|
|
204
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
206
205
|
encoding: 'utf-8',
|
|
207
206
|
timeout: 5000,
|
|
208
207
|
}).trim();
|
package/lib/router.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const { execFileSync, spawn } = require('node:child_process');
|
|
2
|
-
const { existsSync } = require('node:fs');
|
|
2
|
+
const { existsSync, readFileSync } = require('node:fs');
|
|
3
3
|
const path = require('node:path');
|
|
4
4
|
const { getByName, getInstallable } = require('./ecosystem');
|
|
5
5
|
|
|
@@ -92,7 +92,7 @@ function findSourceBin(tool) {
|
|
|
92
92
|
try {
|
|
93
93
|
const pkgPath = path.join(dir, 'package.json');
|
|
94
94
|
if (!existsSync(pkgPath)) continue;
|
|
95
|
-
const pkg = JSON.parse(
|
|
95
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
96
96
|
if (pkg.name !== tool.package) continue;
|
|
97
97
|
// Find the bin entry
|
|
98
98
|
if (pkg.bin) {
|
|
@@ -261,7 +261,7 @@ function statusData() {
|
|
|
261
261
|
|
|
262
262
|
if (hasClaims) {
|
|
263
263
|
try {
|
|
264
|
-
const claimsRaw =
|
|
264
|
+
const claimsRaw = readFileSync(path.join(cwd, 'claims.json'), 'utf-8');
|
|
265
265
|
const claims = JSON.parse(claimsRaw);
|
|
266
266
|
const claimList = Array.isArray(claims) ? claims : claims.claims || [];
|
|
267
267
|
const byType = {};
|
|
@@ -281,7 +281,7 @@ function statusData() {
|
|
|
281
281
|
for (const pidFile of [farmerPidPath, farmerPidAlt]) {
|
|
282
282
|
if (existsSync(pidFile)) {
|
|
283
283
|
try {
|
|
284
|
-
const pid =
|
|
284
|
+
const pid = readFileSync(pidFile, 'utf-8').trim();
|
|
285
285
|
process.kill(Number(pid), 0);
|
|
286
286
|
farmerPidValue = Number(pid);
|
|
287
287
|
} catch {
|
|
@@ -339,7 +339,7 @@ function status(opts) {
|
|
|
339
339
|
|
|
340
340
|
if (hasClaims) {
|
|
341
341
|
try {
|
|
342
|
-
const claimsRaw =
|
|
342
|
+
const claimsRaw = readFileSync(path.join(cwd, 'claims.json'), 'utf-8');
|
|
343
343
|
const claims = JSON.parse(claimsRaw);
|
|
344
344
|
const claimList = Array.isArray(claims) ? claims : claims.claims || [];
|
|
345
345
|
const total = claimList.length;
|
|
@@ -377,7 +377,7 @@ function status(opts) {
|
|
|
377
377
|
for (const pidFile of [farmerPid, farmerPidAlt]) {
|
|
378
378
|
if (existsSync(pidFile)) {
|
|
379
379
|
try {
|
|
380
|
-
const pid =
|
|
380
|
+
const pid = readFileSync(pidFile, 'utf-8').trim();
|
|
381
381
|
// Check if process is still running
|
|
382
382
|
process.kill(Number(pid), 0);
|
|
383
383
|
farmerRunning = true;
|
package/lib/server.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* grainulation serve [--port 9098]
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
import {
|
|
14
|
+
import { execFileSync } from 'node:child_process';
|
|
15
15
|
import { existsSync, mkdirSync, readdirSync, readFileSync, renameSync, statSync, writeFileSync } from 'node:fs';
|
|
16
16
|
import { createServer } from 'node:http';
|
|
17
17
|
import { createRequire } from 'node:module';
|
|
@@ -176,7 +176,10 @@ function detectTool(pkg) {
|
|
|
176
176
|
|
|
177
177
|
// 1. Global npm
|
|
178
178
|
try {
|
|
179
|
-
const out =
|
|
179
|
+
const out = execFileSync('npm', ['list', '-g', pkg, '--depth=0'], {
|
|
180
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
181
|
+
encoding: 'utf-8',
|
|
182
|
+
});
|
|
180
183
|
const match = out.match(new RegExp(`${escapeRegex(pkg)}@(\\S+)`));
|
|
181
184
|
if (match) return { installed: true, version: match[1], method: 'global' };
|
|
182
185
|
} catch {
|
|
@@ -185,7 +188,10 @@ function detectTool(pkg) {
|
|
|
185
188
|
|
|
186
189
|
// 2. npx cache
|
|
187
190
|
try {
|
|
188
|
-
const prefix =
|
|
191
|
+
const prefix = execFileSync('npm', ['config', 'get', 'cache'], {
|
|
192
|
+
stdio: ['pipe', 'pipe', 'ignore'],
|
|
193
|
+
encoding: 'utf-8',
|
|
194
|
+
}).trim();
|
|
189
195
|
const npxDir = join(prefix, '_npx');
|
|
190
196
|
if (existsSync(npxDir)) {
|
|
191
197
|
const entries = readdirSync(npxDir, { withFileTypes: true });
|
|
@@ -242,7 +248,7 @@ function runDoctor() {
|
|
|
242
248
|
const nodeVersion = process.version;
|
|
243
249
|
let npmVersion = 'unknown';
|
|
244
250
|
try {
|
|
245
|
-
npmVersion =
|
|
251
|
+
npmVersion = execFileSync('npm', ['--version'], { stdio: ['pipe', 'pipe', 'ignore'], encoding: 'utf-8' }).trim();
|
|
246
252
|
} catch {
|
|
247
253
|
/* ignore */
|
|
248
254
|
}
|
package/lib/setup.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const readline = require('node:readline');
|
|
2
|
-
const {
|
|
2
|
+
const { execFileSync } = require('node:child_process');
|
|
3
3
|
const { getInstallable, getCategories } = require('./ecosystem');
|
|
4
4
|
const { getVersion } = require('./doctor');
|
|
5
5
|
|
|
@@ -108,7 +108,7 @@ async function run() {
|
|
|
108
108
|
for (const tool of toInstall) {
|
|
109
109
|
console.log(` Installing ${tool.package}...`);
|
|
110
110
|
try {
|
|
111
|
-
|
|
111
|
+
execFileSync('npm', ['install', '-g', tool.package], { stdio: 'pipe' });
|
|
112
112
|
console.log(` \x1b[32m\u2713\x1b[0m ${tool.name} installed`);
|
|
113
113
|
} catch (err) {
|
|
114
114
|
console.log(` \x1b[31m\u2717\x1b[0m ${tool.name} failed: ${err.message}`);
|
package/package.json
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@grainulation/grainulation",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "Structured research for decisions that satisfice",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"workspaces": [
|
|
7
|
-
"packages/*"
|
|
8
|
-
],
|
|
9
6
|
"bin": {
|
|
10
7
|
"grainulation": "bin/grainulation.js"
|
|
11
8
|
},
|
|
@@ -18,15 +15,13 @@
|
|
|
18
15
|
},
|
|
19
16
|
"files": [
|
|
20
17
|
"bin/",
|
|
21
|
-
"lib/"
|
|
22
|
-
"public/"
|
|
18
|
+
"lib/"
|
|
23
19
|
],
|
|
24
20
|
"scripts": {
|
|
25
21
|
"test": "node test/basic.test.js",
|
|
26
22
|
"lint": "biome ci .",
|
|
27
23
|
"format": "biome check --write .",
|
|
28
|
-
"start": "node bin/grainulation.js"
|
|
29
|
-
"postinstall": "git config core.hooksPath .githooks || true"
|
|
24
|
+
"start": "node bin/grainulation.js"
|
|
30
25
|
},
|
|
31
26
|
"keywords": [
|
|
32
27
|
"research",
|
|
@@ -1,316 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* grainulation-tokens.css v1.0.0
|
|
3
|
-
* Shared design tokens for the grainulation ecosystem.
|
|
4
|
-
* Home: barn (the design-system tool)
|
|
5
|
-
*
|
|
6
|
-
* Usage:
|
|
7
|
-
* <link rel="stylesheet" href="grainulation-tokens.css">
|
|
8
|
-
* <html data-tool="wheat"> <!-- activates wheat accent scale -->
|
|
9
|
-
*
|
|
10
|
-
* Token architecture (three layers):
|
|
11
|
-
* 1. Base layer -- backgrounds, foregrounds, borders, spacing, type, radii
|
|
12
|
-
* 2. Semantic layer -- accent (maps to tool), status colors, feedback colors
|
|
13
|
-
* 3. Tool scales -- per-tool 5-weight accent ramp (50/200/400/600/800)
|
|
14
|
-
*
|
|
15
|
-
* Reset layer:
|
|
16
|
-
* Box model, scrollbar, font smoothing, shared components
|
|
17
|
-
*
|
|
18
|
-
* Accent activation:
|
|
19
|
-
* Set data-tool="<name>" on <html> to load that tool's accent scale.
|
|
20
|
-
* Default accent (no data-tool) = barn rose-red.
|
|
21
|
-
*
|
|
22
|
-
* Extracted from working UIs:
|
|
23
|
-
* - barn/public/index.html (accent: #f43f5e rose — lightened from #e11d48 for WCAG AA)
|
|
24
|
-
* - wheat/public/index.html (accent: #fbbf24 amber)
|
|
25
|
-
* - farmer/public/index.html (accent: #22c55e emerald)
|
|
26
|
-
* - grainulation/public/index.html (accent: #9ca3af neutral)
|
|
27
|
-
*/
|
|
28
|
-
|
|
29
|
-
/* ══════════════════════════════════════════════════════════════════════════════
|
|
30
|
-
LAYER 1 — BASE TOKENS
|
|
31
|
-
Shared across every tool. Extracted from wheat + barn + farmer + grainulation.
|
|
32
|
-
══════════════════════════════════════════════════════════════════════════════ */
|
|
33
|
-
|
|
34
|
-
:root {
|
|
35
|
-
/* ── Backgrounds (dark tier) ──
|
|
36
|
-
#0a0e1a is universal across all four UIs.
|
|
37
|
-
bg2/bg3/bg4 from wheat + grainulation (Tailwind Slate scale). */
|
|
38
|
-
--bg: #0a0e1a;
|
|
39
|
-
--bg2: #111827;
|
|
40
|
-
--bg3: #1e293b;
|
|
41
|
-
--bg4: #334155;
|
|
42
|
-
|
|
43
|
-
/* ── Foregrounds ──
|
|
44
|
-
fg/fg2/fg3 from wheat + grainulation.
|
|
45
|
-
Barn used --text:#e8ecf1 (close to fg); standardized to #e2e8f0. */
|
|
46
|
-
--fg: #e2e8f0;
|
|
47
|
-
--fg2: #94a3b8;
|
|
48
|
-
--fg3: #64748b;
|
|
49
|
-
|
|
50
|
-
/* ── Backward-compat aliases ──
|
|
51
|
-
Barn-originated names still used in barn + farmer inline styles. */
|
|
52
|
-
--text: var(--fg);
|
|
53
|
-
--muted: var(--fg2);
|
|
54
|
-
--dim: var(--fg3);
|
|
55
|
-
|
|
56
|
-
/* ── Borders ──
|
|
57
|
-
Wheat/grainulation use solid #1e293b; barn/farmer use rgba.
|
|
58
|
-
Provide both — --border is the structural one, --border-subtle for overlays. */
|
|
59
|
-
--border: #1e293b;
|
|
60
|
-
--border-subtle: rgba(255, 255, 255, 0.08);
|
|
61
|
-
|
|
62
|
-
/* ── Glass (farmer-originated, useful for overlays) ── */
|
|
63
|
-
--glass: rgba(255, 255, 255, 0.08);
|
|
64
|
-
--glass-border: rgba(255, 255, 255, 0.12);
|
|
65
|
-
|
|
66
|
-
/* ── Radii ── (from wheat + barn + grainulation, converged) */
|
|
67
|
-
--radius: 8px;
|
|
68
|
-
--radius-sm: 4px;
|
|
69
|
-
--radius-lg: 12px;
|
|
70
|
-
--radius-full: 9999px;
|
|
71
|
-
|
|
72
|
-
/* ── Typography ──
|
|
73
|
-
Sans from barn/farmer; mono from wheat. Both used across UIs. */
|
|
74
|
-
--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", "Inter", sans-serif;
|
|
75
|
-
--font-mono: "SF Mono", "Cascadia Code", "JetBrains Mono", "Fira Code", "Consolas", monospace;
|
|
76
|
-
--text-xs: 9px;
|
|
77
|
-
--text-sm: 10px;
|
|
78
|
-
--text-base: 12px;
|
|
79
|
-
--text-md: 13px;
|
|
80
|
-
--text-lg: 15px;
|
|
81
|
-
--text-xl: 18px;
|
|
82
|
-
--line-height: 1.5;
|
|
83
|
-
|
|
84
|
-
/* ── Spacing ── */
|
|
85
|
-
--space-xs: 4px;
|
|
86
|
-
--space-sm: 8px;
|
|
87
|
-
--space-md: 12px;
|
|
88
|
-
--space-lg: 16px;
|
|
89
|
-
--space-xl: 24px;
|
|
90
|
-
--space-2xl: 32px;
|
|
91
|
-
|
|
92
|
-
/* ── Transitions ── */
|
|
93
|
-
--transition-fast: 0.1s ease;
|
|
94
|
-
--transition-base: 0.15s ease;
|
|
95
|
-
--transition-slow: 0.3s ease;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
/* ══════════════════════════════════════════════════════════════════════════════
|
|
99
|
-
LAYER 2 — SEMANTIC TOKENS
|
|
100
|
-
Status, feedback, and accent. Accent defaults to barn rose-red.
|
|
101
|
-
══════════════════════════════════════════════════════════════════════════════ */
|
|
102
|
-
|
|
103
|
-
:root {
|
|
104
|
-
/* ── Status colors ──
|
|
105
|
-
Converged values: green from barn/farmer (#22c55e) vs wheat (#34d399).
|
|
106
|
-
Using the brighter #22c55e for success (used for connection dots);
|
|
107
|
-
keeping #34d399 as --success-light for softer contexts. */
|
|
108
|
-
--success: #22c55e;
|
|
109
|
-
--success-light: #34d399;
|
|
110
|
-
--warning: #f59e0b;
|
|
111
|
-
--error: #f87171;
|
|
112
|
-
--info: #60a5fa;
|
|
113
|
-
|
|
114
|
-
/* ── Semantic palette (named colors, used across UIs) ── */
|
|
115
|
-
--green: #22c55e;
|
|
116
|
-
--red: #f87171;
|
|
117
|
-
--blue: #60a5fa;
|
|
118
|
-
--purple: #a78bfa;
|
|
119
|
-
--orange: #fb923c;
|
|
120
|
-
--cyan: #22d3ee;
|
|
121
|
-
|
|
122
|
-
/* ── Accent (default: barn rose) ──
|
|
123
|
-
Overridden per-tool via [data-tool] selectors below.
|
|
124
|
-
--accent-bg is the very faint tint for hover/active states.
|
|
125
|
-
Lightened from #e11d48 to #f43f5e for WCAG AA (5.24:1 vs #0a0e1a). */
|
|
126
|
-
--accent: #f43f5e;
|
|
127
|
-
--accent-light: #fb7185;
|
|
128
|
-
--accent-dim: rgba(244, 63, 94, 0.12);
|
|
129
|
-
--accent-bg: rgba(244, 63, 94, 0.06);
|
|
130
|
-
--accent-border: rgba(244, 63, 94, 0.25);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
/* ══════════════════════════════════════════════════════════════════════════════
|
|
134
|
-
LAYER 3 — TOOL-SPECIFIC ACCENT SCALES
|
|
135
|
-
Each tool gets a 5-weight ramp (50/200/400/600/800) plus the four
|
|
136
|
-
semantic accent tokens. Activated via data-tool="<name>" on <html>.
|
|
137
|
-
══════════════════════════════════════════════════════════════════════════════ */
|
|
138
|
-
|
|
139
|
-
/* ── wheat: amber (#fbbf24) ── */
|
|
140
|
-
[data-tool="wheat"] {
|
|
141
|
-
--accent-50: #fffbeb;
|
|
142
|
-
--accent-200: #fde68a;
|
|
143
|
-
--accent-400: #fbbf24;
|
|
144
|
-
--accent-600: #d97706;
|
|
145
|
-
--accent-800: #92400e;
|
|
146
|
-
--accent: #fbbf24;
|
|
147
|
-
--accent-light: #fcd34d;
|
|
148
|
-
--accent-dim: rgba(251, 191, 36, 0.08);
|
|
149
|
-
--accent-bg: rgba(251, 191, 36, 0.06);
|
|
150
|
-
--accent-border: rgba(251, 191, 36, 0.25);
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/* ── barn: rose (#f43f5e) — default, repeated for explicitness ──
|
|
154
|
-
Lightened from #e11d48 for WCAG AA compliance (5.24:1 vs #0a0e1a). */
|
|
155
|
-
[data-tool="barn"] {
|
|
156
|
-
--accent-50: #fff1f2;
|
|
157
|
-
--accent-200: #fecdd3;
|
|
158
|
-
--accent-400: #fb7185;
|
|
159
|
-
--accent-600: #f43f5e;
|
|
160
|
-
--accent-800: #be123c;
|
|
161
|
-
--accent: #f43f5e;
|
|
162
|
-
--accent-light: #fb7185;
|
|
163
|
-
--accent-dim: rgba(244, 63, 94, 0.12);
|
|
164
|
-
--accent-bg: rgba(244, 63, 94, 0.06);
|
|
165
|
-
--accent-border: rgba(244, 63, 94, 0.25);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/* ── farmer: emerald (#22c55e) ── */
|
|
169
|
-
[data-tool="farmer"] {
|
|
170
|
-
--accent-50: #ecfdf5;
|
|
171
|
-
--accent-200: #a7f3d0;
|
|
172
|
-
--accent-400: #34d399;
|
|
173
|
-
--accent-600: #16a34a;
|
|
174
|
-
--accent-800: #166534;
|
|
175
|
-
--accent: #22c55e;
|
|
176
|
-
--accent-light: #4ade80;
|
|
177
|
-
--accent-dim: rgba(34, 197, 94, 0.1);
|
|
178
|
-
--accent-bg: rgba(34, 197, 94, 0.06);
|
|
179
|
-
--accent-border: rgba(34, 197, 94, 0.25);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/* ── mill: blue (#3b82f6) ── */
|
|
183
|
-
[data-tool="mill"] {
|
|
184
|
-
--accent-50: #eff6ff;
|
|
185
|
-
--accent-200: #bfdbfe;
|
|
186
|
-
--accent-400: #60a5fa;
|
|
187
|
-
--accent-600: #2563eb;
|
|
188
|
-
--accent-800: #1e40af;
|
|
189
|
-
--accent: #3b82f6;
|
|
190
|
-
--accent-light: #60a5fa;
|
|
191
|
-
--accent-dim: rgba(59, 130, 246, 0.1);
|
|
192
|
-
--accent-bg: rgba(59, 130, 246, 0.06);
|
|
193
|
-
--accent-border: rgba(59, 130, 246, 0.25);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/* ── silo: purple (#a78bfa) ── */
|
|
197
|
-
[data-tool="silo"] {
|
|
198
|
-
--accent-50: #f5f3ff;
|
|
199
|
-
--accent-200: #ddd6fe;
|
|
200
|
-
--accent-400: #a78bfa;
|
|
201
|
-
--accent-600: #7c3aed;
|
|
202
|
-
--accent-800: #5b21b6;
|
|
203
|
-
--accent: #a78bfa;
|
|
204
|
-
--accent-light: #c4b5fd;
|
|
205
|
-
--accent-dim: rgba(167, 139, 250, 0.1);
|
|
206
|
-
--accent-bg: rgba(167, 139, 250, 0.06);
|
|
207
|
-
--accent-border: rgba(167, 139, 250, 0.25);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
/* ── harvest: orange (#fb923c) ── */
|
|
211
|
-
[data-tool="harvest"] {
|
|
212
|
-
--accent-50: #fff7ed;
|
|
213
|
-
--accent-200: #fed7aa;
|
|
214
|
-
--accent-400: #fb923c;
|
|
215
|
-
--accent-600: #ea580c;
|
|
216
|
-
--accent-800: #9a3412;
|
|
217
|
-
--accent: #fb923c;
|
|
218
|
-
--accent-light: #fdba74;
|
|
219
|
-
--accent-dim: rgba(251, 146, 60, 0.1);
|
|
220
|
-
--accent-bg: rgba(251, 146, 60, 0.06);
|
|
221
|
-
--accent-border: rgba(251, 146, 60, 0.25);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/* ── orchard: cyan (#22d3ee) ── */
|
|
225
|
-
[data-tool="orchard"] {
|
|
226
|
-
--accent-50: #ecfeff;
|
|
227
|
-
--accent-200: #a5f3fc;
|
|
228
|
-
--accent-400: #22d3ee;
|
|
229
|
-
--accent-600: #0891b2;
|
|
230
|
-
--accent-800: #155e75;
|
|
231
|
-
--accent: #22d3ee;
|
|
232
|
-
--accent-light: #67e8f9;
|
|
233
|
-
--accent-dim: rgba(34, 211, 238, 0.1);
|
|
234
|
-
--accent-bg: rgba(34, 211, 238, 0.06);
|
|
235
|
-
--accent-border: rgba(34, 211, 238, 0.25);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
/* ── grainulation: neutral/white (#9ca3af) ── */
|
|
239
|
-
[data-tool="grainulation"],
|
|
240
|
-
[data-tool="grainulation"] {
|
|
241
|
-
--accent-50: #f9fafb;
|
|
242
|
-
--accent-200: #e5e7eb;
|
|
243
|
-
--accent-400: #9ca3af;
|
|
244
|
-
--accent-600: #6b7280;
|
|
245
|
-
--accent-800: #374151;
|
|
246
|
-
--accent: #9ca3af;
|
|
247
|
-
--accent-light: #d1d5db;
|
|
248
|
-
--accent-dim: rgba(156, 163, 175, 0.1);
|
|
249
|
-
--accent-bg: rgba(156, 163, 175, 0.06);
|
|
250
|
-
--accent-border: rgba(156, 163, 175, 0.25);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
/* ══════════════════════════════════════════════════════════════════════════════
|
|
254
|
-
RESET + BASE STYLES — shared across all tool UIs
|
|
255
|
-
══════════════════════════════════════════════════════════════════════════════ */
|
|
256
|
-
|
|
257
|
-
*,
|
|
258
|
-
*::before,
|
|
259
|
-
*::after {
|
|
260
|
-
box-sizing: border-box;
|
|
261
|
-
margin: 0;
|
|
262
|
-
padding: 0;
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
html,
|
|
266
|
-
body {
|
|
267
|
-
height: 100%;
|
|
268
|
-
overflow: hidden;
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
body {
|
|
272
|
-
font-family: var(--font-sans);
|
|
273
|
-
background: var(--bg);
|
|
274
|
-
color: var(--fg);
|
|
275
|
-
font-size: var(--text-md);
|
|
276
|
-
line-height: var(--line-height);
|
|
277
|
-
-webkit-font-smoothing: antialiased;
|
|
278
|
-
-moz-osx-font-smoothing: grayscale;
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/* ── Scrollbar (webkit) ── */
|
|
282
|
-
|
|
283
|
-
::-webkit-scrollbar {
|
|
284
|
-
width: 6px;
|
|
285
|
-
height: 6px;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
::-webkit-scrollbar-track {
|
|
289
|
-
background: transparent;
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
::-webkit-scrollbar-thumb {
|
|
293
|
-
background: var(--bg4);
|
|
294
|
-
border-radius: 3px;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
::-webkit-scrollbar-thumb:hover {
|
|
298
|
-
background: var(--fg3);
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/* ── Shared components ── */
|
|
302
|
-
|
|
303
|
-
.conn-dot,
|
|
304
|
-
.connection-dot {
|
|
305
|
-
width: 8px;
|
|
306
|
-
height: 8px;
|
|
307
|
-
border-radius: 50%;
|
|
308
|
-
background: var(--green);
|
|
309
|
-
display: inline-block;
|
|
310
|
-
flex-shrink: 0;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
.conn-dot.disconnected,
|
|
314
|
-
.connection-dot.disconnected {
|
|
315
|
-
background: var(--red);
|
|
316
|
-
}
|