@galda/cli 0.10.122 → 0.10.123
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/app/index.html +194 -84
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -1292,6 +1292,79 @@
|
|
|
1292
1292
|
body[data-layout="flagship"] .gearpop{position:fixed;top:120px;right:auto;width:380px;max-height:calc(100vh - 160px);border-radius:14px;
|
|
1293
1293
|
background:color-mix(in srgb,var(--panel) 74%,transparent);
|
|
1294
1294
|
-webkit-backdrop-filter:blur(28px) saturate(1.35);backdrop-filter:blur(28px) saturate(1.35)}
|
|
1295
|
+
/* ── Settings = a centred two-column dialog (CDO 2026-08-02, pattern C) ────────────
|
|
1296
|
+
Four sections that each hold ten-plus controls are a sidebar's job, not a tab
|
|
1297
|
+
strip's: the 380px column beside the rail was showing them 380px at a time, and
|
|
1298
|
+
the panel covered the feed while doing it. Same parts, new frame — the tab
|
|
1299
|
+
buttons keep their data-gctab wiring, the sticky Save/Cancel footer keeps its
|
|
1300
|
+
shape, and everything below 760px still gets the bottom sheet further down.
|
|
1301
|
+
Scoped to flagship only: the legacy anchored popover in other layouts is
|
|
1302
|
+
untouched (it is 320px wide and a left rail would ruin it).
|
|
1303
|
+
No scrim (Masa 2026-08-02): a dimmed board made opening Settings feel like a modal
|
|
1304
|
+
interruption. The dialog just appears — its own shadow separates it from the board,
|
|
1305
|
+
and outside-tap/Esc already close it.
|
|
1306
|
+
ONE HEIGHT for every section (Masa 2026-08-02): the panel used to resize as you moved
|
|
1307
|
+
between Theme (long) and MCP (short), so the thing under your cursor jumped. The frame
|
|
1308
|
+
is fixed and the content column scrolls inside it. */
|
|
1309
|
+
body[data-layout="flagship"] .gearpop.open{display:flex;flex-direction:row;gap:22px;
|
|
1310
|
+
/* Straddles the rail rather than sitting dead centre (Masa 2026-08-02): landing on top
|
|
1311
|
+
of the rail says "this belongs to the gear you just pressed", and it leaves the feed
|
|
1312
|
+
— the thing you were reading — visible on the right instead of split in half. */
|
|
1313
|
+
top:50%;left:96px;transform:translateY(-50%);right:auto;bottom:auto;
|
|
1314
|
+
width:min(880px,calc(100vw - 128px));max-width:none;height:min(720px,86vh);max-height:86vh;
|
|
1315
|
+
border-radius:16px;padding:18px 20px 0;
|
|
1316
|
+
overflow:hidden; /* the .gbody column scrolls, not the frame */
|
|
1317
|
+
/* Lifted off the board instead of matching it: on the dark themes --panel is very near
|
|
1318
|
+
black, so a black dialog on a black board read as a hole rather than a surface. The
|
|
1319
|
+
mix is ink-based, so it lightens in the dark and stays white on the light themes. */
|
|
1320
|
+
background:color-mix(in srgb,var(--ink) 5%,rgb(from var(--panel) r g b));
|
|
1321
|
+
-webkit-backdrop-filter:none;backdrop-filter:none;
|
|
1322
|
+
box-shadow:0 24px 70px rgba(0,0,0,.42),0 0 0 1px var(--hair2)}
|
|
1323
|
+
body[data-layout="flagship"] .gearpop .gnav{flex:0 0 158px;display:flex;flex-direction:column;gap:2px;
|
|
1324
|
+
border-right:1px solid var(--hair);padding:2px 12px 12px 0}
|
|
1325
|
+
body[data-layout="flagship"] .gearpop .gnav .ggrp{font:600 9px/1 var(--mono);letter-spacing:.12em;
|
|
1326
|
+
text-transform:uppercase;color:var(--ink3);padding:14px 10px 6px}
|
|
1327
|
+
body[data-layout="flagship"] .gearpop .gnav .ggrp:first-child{padding-top:2px}
|
|
1328
|
+
body[data-layout="flagship"] .gearpop .gnav button{border:0;background:none;text-align:left;
|
|
1329
|
+
padding:8px 10px;border-radius:8px;cursor:pointer;font:500 12.5px/1 var(--ui);color:var(--ink2)}
|
|
1330
|
+
body[data-layout="flagship"] .gearpop .gnav button:hover{color:var(--ink)} /* glyph only (§5.5) */
|
|
1331
|
+
body[data-layout="flagship"] .gearpop .gnav button.on{background:var(--hover);color:var(--ink);font-weight:600}
|
|
1332
|
+
body[data-layout="flagship"] .gearpop .gcol{flex:1;min-width:0;display:flex;flex-direction:column}
|
|
1333
|
+
/* padding-bottom: the note used to end flush against the frame's edge with nowhere for
|
|
1334
|
+
the eye to land (Masa 2026-08-02). Also the scroll's own breathing room. */
|
|
1335
|
+
body[data-layout="flagship"] .gearpop .gbody{flex:1;min-height:0;overflow-y:auto;scrollbar-width:thin;padding-bottom:26px}
|
|
1336
|
+
body[data-layout="flagship"] .gearpop .gbody > h4:first-child{margin-top:2px}
|
|
1337
|
+
body[data-layout="flagship"] .gearpop .cfoot{margin:14px -20px 0;padding:14px 20px;bottom:0;border-radius:0;
|
|
1338
|
+
background:color-mix(in srgb,var(--ink) 5%,rgb(from var(--panel) r g b))}
|
|
1339
|
+
/* ONE COLUMN (Masa 2026-08-02). This was two-across, and reading it was worse, not
|
|
1340
|
+
better: a settings row is label + description + control on one line, so pairing two
|
|
1341
|
+
of them puts four things on a line and the eye stops knowing which control belongs to
|
|
1342
|
+
which label. The width now buys long descriptions that no longer wrap, which is what
|
|
1343
|
+
it should have bought in the first place. The class stays a grid so the group keeps
|
|
1344
|
+
one place to change. */
|
|
1345
|
+
.gearpop .gtwo{display:grid;grid-template-columns:1fr}
|
|
1346
|
+
/* The free-tier Upgrade CTA is a bare button, not a .srow — as a full-width grid child
|
|
1347
|
+
it read as a banner. .bx-up-set pins width:100%, right in the 320px popover, wrong
|
|
1348
|
+
here. Keep it the size of its label, on the right edge where every other control in
|
|
1349
|
+
this list sits (Masa 2026-08-02) — a lone left-aligned button broke the column of
|
|
1350
|
+
chips it belongs to. */
|
|
1351
|
+
.gearpop .gtwo > .bx-cta{justify-self:end;align-self:center;margin:9px 0;width:auto}
|
|
1352
|
+
/* ── FEEDBACK: the founder's note, last thing in SETTINGS (Masa 2026-08-02) ────────
|
|
1353
|
+
A letter, not a settings row: hairline above, mono label, plain prose, one mail
|
|
1354
|
+
button. No fill, no badge, no icon block (§5.5). It lives inside the scrolling
|
|
1355
|
+
body so it never covers the sticky Save/Cancel. */
|
|
1356
|
+
.gearpop .gfeed{border-top:1px solid var(--hair);margin-top:13px;padding-top:13px}
|
|
1357
|
+
.gearpop .gfeed.gsolo{border-top:0;margin-top:2px;padding-top:0}
|
|
1358
|
+
.gearpop .gfeed.gsolo p{font-size:13px;line-height:1.8;max-width:56ch;margin-bottom:13px}
|
|
1359
|
+
.gearpop .gfeed.gsolo .gsig{font-size:12px;margin-bottom:18px}
|
|
1360
|
+
.gearpop .gfeed.gsolo .gmail{height:32px;padding:0 15px;font-size:12.5px}
|
|
1361
|
+
.gearpop .gfeed h4{margin:0 0 8px}
|
|
1362
|
+
.gearpop .gfeed p{font-size:11.5px;line-height:1.75;color:var(--ink2);margin:0 0 9px;max-width:54ch}
|
|
1363
|
+
.gearpop .gfeed .gsig{font-size:11px;color:var(--ink3);margin:0 0 11px}
|
|
1364
|
+
.gearpop .gfeed .gmail{display:inline-flex;align-items:center;gap:6px;height:27px;padding:0 12px;
|
|
1365
|
+
border-radius:8px;background:var(--chipbg);color:var(--ink);text-decoration:none;font:500 11.5px/1 var(--mono)}
|
|
1366
|
+
.gearpop .gfeed .gmail:hover{color:var(--ink);background:color-mix(in srgb,var(--ink) 10%,rgb(from var(--panel) r g b))}
|
|
1367
|
+
.gearpop .gfeed .gmail svg{width:12px;height:12px;opacity:.55;fill:none;stroke:currentColor;stroke-width:1.4}
|
|
1295
1368
|
/* settings = EDIT SESSION (HANDOFF-v45 §3, flagship .cfoot/.mcpbox port): sticky footer,
|
|
1296
1369
|
always visible over the internal scroll — Save = ink-solid persists, Cancel = ghost
|
|
1297
1370
|
restores the open-time snapshot. */
|
|
@@ -1333,14 +1406,6 @@
|
|
|
1333
1406
|
.gearpop .srow.dis{opacity:.38;pointer-events:none}
|
|
1334
1407
|
.gearpop .srow .sl{flex:1;min-width:0;font-size:12.5px;color:var(--ink)}
|
|
1335
1408
|
.gearpop .srow .sd{display:block;font-size:10.5px;color:var(--ink3);margin-top:2px}
|
|
1336
|
-
/* Project rules read-only view (§1.5a): a quiet framed panel under its row —
|
|
1337
|
-
mono filename label + the file body, scrollable. Reuses --canvas/--hair so it
|
|
1338
|
-
matches the MCP box; no new surface invented. */
|
|
1339
|
-
.gearpop .rulesview{margin:2px 0 9px;display:flex;flex-direction:column;gap:8px}
|
|
1340
|
-
.gearpop .rulesfile{background:var(--canvas);border-radius:8px;overflow:hidden}
|
|
1341
|
-
.gearpop .rulesname{font:600 9px/1 var(--mono);letter-spacing:.08em;text-transform:uppercase;color:var(--ink3);padding:8px 10px 0}
|
|
1342
|
-
.gearpop .rulestext{margin:6px 0 0;padding:0 10px 10px;max-height:220px;overflow:auto;font:400 11px/1.6 var(--mono);color:var(--ink2);white-space:pre-wrap;word-break:break-word}
|
|
1343
|
-
.gearpop .rulesempty{color:var(--ink3);font-style:italic}
|
|
1344
1409
|
/* MCP tab: live status dot + connect command + quiet explanation */
|
|
1345
1410
|
.gearpop .mcpstat{display:flex;align-items:center;gap:8px;font:600 11px/1 var(--ui);color:var(--ink);padding:2px 0 10px}
|
|
1346
1411
|
.gearpop .mcpstat i{width:7px;height:7px;border-radius:50%;background:var(--green);box-shadow:0 0 6px rgba(var(--green-rgb),.5)}
|
|
@@ -2987,6 +3052,16 @@
|
|
|
2987
3052
|
body[data-layout="flagship"] .gearpop{left:8px!important;right:8px!important;top:auto!important;
|
|
2988
3053
|
bottom:calc(env(safe-area-inset-bottom) + 8px)!important;width:auto!important;max-width:none!important;
|
|
2989
3054
|
max-height:82dvh!important;border-radius:16px;padding-top:8px}
|
|
3055
|
+
/* The desktop dialog above centres with a transform and lays its nav out sideways —
|
|
3056
|
+
both have to be undone here or the sheet floats off-centre with a 158px rail. */
|
|
3057
|
+
body[data-layout="flagship"] .gearpop.open{transform:none!important;flex-direction:column!important;
|
|
3058
|
+
padding:8px 13px 0!important;gap:0!important}
|
|
3059
|
+
body[data-layout="flagship"] .gearpop .gnav{flex:0 0 auto;flex-direction:row;gap:6px;overflow-x:auto;
|
|
3060
|
+
scrollbar-width:none;border-right:0;border-bottom:1px solid var(--hair);padding:0 0 8px;margin-bottom:10px}
|
|
3061
|
+
body[data-layout="flagship"] .gearpop .gnav::-webkit-scrollbar{display:none}
|
|
3062
|
+
body[data-layout="flagship"] .gearpop .gnav .ggrp{display:none} /* group labels need a column to sit in */
|
|
3063
|
+
body[data-layout="flagship"] .gearpop .gnav button{white-space:nowrap;min-height:38px;padding:0 12px;font-size:13px}
|
|
3064
|
+
body[data-layout="flagship"] .gearpop .cfoot{margin:14px -13px 0;padding:12px 13px}
|
|
2990
3065
|
body[data-layout="flagship"] .gearpop::before{content:"";position:sticky;top:0;z-index:2;display:block;
|
|
2991
3066
|
width:38px;height:5px;margin:0 auto 12px;border-radius:3px;background:var(--hair2)}
|
|
2992
3067
|
body[data-layout="flagship"] .gearpop .csw{width:30px;height:30px}
|
|
@@ -3255,12 +3330,6 @@
|
|
|
3255
3330
|
same right column as the fold toggle above it; the menu opens BELOW the gear. -->
|
|
3256
3331
|
<div class="lbrow"><span class="lb">Projects</span>
|
|
3257
3332
|
<button class="ghostbtn morebtn" id="fsMoreBtn" title="Settings"><svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3.2"/><path d="M19.4 15a1.7 1.7 0 00.34 1.87l.06.06a2 2 0 11-2.83 2.83l-.06-.06a1.7 1.7 0 00-1.87-.34 1.7 1.7 0 00-1 1.56V21a2 2 0 11-4 0v-.09a1.7 1.7 0 00-1-1.56 1.7 1.7 0 00-1.87.34l-.06.06a2 2 0 11-2.83-2.83l.06-.06a1.7 1.7 0 00.34-1.87 1.7 1.7 0 00-1.56-1H3a2 2 0 110-4h.09a1.7 1.7 0 001.56-1 1.7 1.7 0 00-.34-1.87l-.06-.06a2 2 0 112.83-2.83l.06.06a1.7 1.7 0 001.87.34h.09a1.7 1.7 0 001-1.56V3a2 2 0 114 0v.09a1.7 1.7 0 001 1.56h.09a1.7 1.7 0 001.87-.34l.06-.06a2 2 0 112.83 2.83l-.06.06a1.7 1.7 0 00-.34 1.87v.09a1.7 1.7 0 001.56 1H21a2 2 0 110 4h-.09a1.7 1.7 0 00-1.56 1z"/></svg></button>
|
|
3258
|
-
<div class="moremenu" id="fsMoreMenu">
|
|
3259
|
-
<button class="ghostbtn" id="fsCust">Theme</button>
|
|
3260
|
-
<button class="ghostbtn" id="fsConn">Connect MCP</button>
|
|
3261
|
-
<button class="ghostbtn" id="fsHist">History</button>
|
|
3262
|
-
<button class="ghostbtn" id="fsSet">Settings</button>
|
|
3263
|
-
</div>
|
|
3264
3333
|
</div>
|
|
3265
3334
|
<button class="fshamb" id="fsHamburger" title="Open To Do and Review"><svg viewBox="0 0 24 24"><path d="M5 7h14M5 12h14M5 17h14"/></svg><span class="fshamb-lb">Review</span><span class="fshamb-n" id="fsHambN"></span></button>
|
|
3266
3335
|
<div id="fsProjects"></div>
|
|
@@ -3565,7 +3634,7 @@ const savedTheme = (() => {
|
|
|
3565
3634
|
if (v !== s) localStorage.setItem('theme', v);
|
|
3566
3635
|
return v;
|
|
3567
3636
|
})();
|
|
3568
|
-
const state = { repos: null, repoHome: '', /* GET /api/repos — the folders a project can point at (Context Bar) */ projects: [], goals: [], tasks: [], act: {}, actAt: {}, todos: {}, chatTurns: {}, active: localStorage.getItem('sel:project') || 'default', editing: null, attach: [], connectCommand: '', models: ['sonnet'], agentModels: null, agentEfforts: null, agents: ['claude-code', 'codex'], auth: null, expanded: new Set(), openThreads: new Set(), queueOrder: {}, queueWaits: {}, externalActivity: [], selectedGoal: null, composerDetachedGoal: null, workflowColumns: {}, wfDraft: null, reviewDefinitions: {}, rdDraft: null, runningCounts: {}, parallelLimits: null, hostReadiness: [], connectedApp: localStorage.getItem('sel:agent') || 'claude-code', reviewOpenGoal: null, reviewApprovals: {}, goalDetailOpen: null, qreplyOpen: new Set(), logCollapsed: localStorage.getItem('logCollapsed') !== '0', logEntries: [], layout: localStorage.getItem('layout') || 'flagship', theme: savedTheme, boardskin: localStorage.getItem('boardskin') || 'auto', summaryPattern: Number(localStorage.getItem('summaryPattern')) || 3, peraSpot: 0, skill: null, skillsCache: null,
|
|
3637
|
+
const state = { repos: null, repoHome: '', /* GET /api/repos — the folders a project can point at (Context Bar) */ projects: [], goals: [], tasks: [], act: {}, actAt: {}, todos: {}, chatTurns: {}, active: localStorage.getItem('sel:project') || 'default', editing: null, attach: [], connectCommand: '', models: ['sonnet'], agentModels: null, agentEfforts: null, agents: ['claude-code', 'codex'], auth: null, expanded: new Set(), openThreads: new Set(), queueOrder: {}, queueWaits: {}, externalActivity: [], selectedGoal: null, composerDetachedGoal: null, workflowColumns: {}, wfDraft: null, reviewDefinitions: {}, rdDraft: null, runningCounts: {}, parallelLimits: null, hostReadiness: [], connectedApp: localStorage.getItem('sel:agent') || 'claude-code', reviewOpenGoal: null, reviewApprovals: {}, goalDetailOpen: null, qreplyOpen: new Set(), logCollapsed: localStorage.getItem('logCollapsed') !== '0', logEntries: [], layout: localStorage.getItem('layout') || 'flagship', theme: savedTheme, boardskin: localStorage.getItem('boardskin') || 'auto', summaryPattern: Number(localStorage.getItem('summaryPattern')) || 3, peraSpot: 0, skill: null, skillsCache: null,
|
|
3569
3638
|
// per-message send overrides set via slash-commands (reset after each send,
|
|
3570
3639
|
// except model which is sticky in localStorage). palette = open command list.
|
|
3571
3640
|
// Scoped per project (msgByProject) so a skill/mode picked in one project can't leak
|
|
@@ -8320,8 +8389,14 @@ function renderClawdHero(){
|
|
|
8320
8389
|
};
|
|
8321
8390
|
syncFsTvSeg();
|
|
8322
8391
|
tgUpdate();
|
|
8323
|
-
|
|
8324
|
-
|
|
8392
|
+
// One press, one dialog (Masa 2026-08-02). The gear used to open a four-item menu
|
|
8393
|
+
// (Theme / Connect MCP / History / Settings) whose only job was to pick a tab — and the
|
|
8394
|
+
// dialog now carries those same four in its own left nav, so the menu was asking people
|
|
8395
|
+
// to choose twice. Press the gear, get Settings; switch inside if you want something else.
|
|
8396
|
+
$('fsMoreBtn').onclick = (e) => {
|
|
8397
|
+
e.stopPropagation();
|
|
8398
|
+
if ($('gearPop').classList.contains('open')) custCancel(); else custOpen();
|
|
8399
|
+
};
|
|
8325
8400
|
const closeMobilePanels = () => {
|
|
8326
8401
|
document.body.classList.remove('laneopen', 'projectsopen');
|
|
8327
8402
|
};
|
|
@@ -8349,11 +8424,7 @@ function renderClawdHero(){
|
|
|
8349
8424
|
// v46 (HANDOFF-v45 §3): the menu entries deep-link into the 4-tab settings panel —
|
|
8350
8425
|
// Theme→THEME / Connect MCP→MCP / History→HISTORY / Settings→SETTINGS.
|
|
8351
8426
|
// stopPropagation: the document click-away handler otherwise closes it in the same event.
|
|
8352
|
-
|
|
8353
|
-
$('fsConn').onclick = (e) => { e.stopPropagation(); $('fsMoreMenu').classList.remove('open'); custOpen('mcp'); };
|
|
8354
|
-
$('fsHist').onclick = (e) => { e.stopPropagation(); $('fsMoreMenu').classList.remove('open'); custOpen('history'); };
|
|
8355
|
-
$('fsSet').onclick = (e) => { e.stopPropagation(); $('fsMoreMenu').classList.remove('open'); custOpen('settings'); };
|
|
8356
|
-
// parallel-runs switcher (§1.5-1/-7): same toggle+click-away shape as fsMoreMenu above.
|
|
8427
|
+
// parallel-runs switcher (§1.5-1/-7): toggle + click-away, the shape the rail's own menus use.
|
|
8357
8428
|
// Row content is rebuilt by renderFsFeed() (running list changes live); the open/closed
|
|
8358
8429
|
// class here is left alone by that rebuild so the popover doesn't snap shut mid-render.
|
|
8359
8430
|
$('fsRunSwitch').onclick = (e) => { e.stopPropagation(); $('fsRunMenu').classList.toggle('open'); };
|
|
@@ -12762,11 +12833,38 @@ function applyCust(){
|
|
|
12762
12833
|
// histHTML/setHTML/mcpHTML port). custTab is session UI state, never persisted; switching
|
|
12763
12834
|
// tabs re-renders the panel WITHOUT touching the edit-session snapshot (custSnap) and
|
|
12764
12835
|
// without closing. =====
|
|
12765
|
-
let custTab = '
|
|
12836
|
+
let custTab = 'settings'; // the gear opens on Settings (Masa 2026-08-02): themes are a once-a-month errand, settings are the reason people press it
|
|
12766
12837
|
function gearTabsHTML(){
|
|
12767
12838
|
return `<div class="ctabs">${[['theme', 'THEME'], ['history', 'HISTORY'], ['settings', 'SETTINGS'], ['mcp', 'MCP']]
|
|
12768
12839
|
.map(([k, l]) => `<button class="ctab${custTab === k ? ' on' : ''}" data-gctab="${k}">${l}</button>`).join('')}</div>`;
|
|
12769
12840
|
}
|
|
12841
|
+
// Flagship = the centred dialog (CDO pattern C): the four tabs become a left rail,
|
|
12842
|
+
// grouped by what they are actually about instead of sitting in one flat strip.
|
|
12843
|
+
// Same data-gctab contract as the tab buttons, so wireGearCommon needs no change.
|
|
12844
|
+
// Theme sits LAST (Masa 2026-08-02): it is the section people visit least, and it was
|
|
12845
|
+
// taking the top slot — the position a reader reads first — from the one they came for.
|
|
12846
|
+
const GEAR_NAV = [
|
|
12847
|
+
['Project', [['settings', 'Settings'], ['history', 'History'], ['mcp', 'MCP']]],
|
|
12848
|
+
// Its own entry under MCP (Masa 2026-08-02) as well as the note at the foot of Settings:
|
|
12849
|
+
// a person who has decided to say something should not have to scroll a settings list to
|
|
12850
|
+
// find where. The heading is Contact, not Feedback — a section labelled the same as its
|
|
12851
|
+
// only item is a label for nothing.
|
|
12852
|
+
['Contact', [['feedback', 'Feedback']]],
|
|
12853
|
+
['Appearance', [['theme', 'Theme']]],
|
|
12854
|
+
];
|
|
12855
|
+
function gearNavHTML(){
|
|
12856
|
+
return `<nav class="gnav">${GEAR_NAV.map(([grp, items]) =>
|
|
12857
|
+
`<div class="ggrp">${grp}</div>` + items.map(([k, l]) =>
|
|
12858
|
+
`<button class="${custTab === k ? 'on' : ''}" data-gctab="${k}">${l}</button>`).join('')).join('')}</nav>`;
|
|
12859
|
+
}
|
|
12860
|
+
// The dialog only exists in flagship; every other layout keeps the anchored popover
|
|
12861
|
+
// and its top tab strip untouched.
|
|
12862
|
+
function gearIsDialog(){ return state.layout === 'flagship'; }
|
|
12863
|
+
function gearShell(html){
|
|
12864
|
+
return gearIsDialog()
|
|
12865
|
+
? gearNavHTML() + `<div class="gcol"><div class="gbody">${html}</div></div>`
|
|
12866
|
+
: gearTabsHTML() + html;
|
|
12867
|
+
}
|
|
12770
12868
|
// HISTORY (v46): approve/dismiss history from goal state — deterministic, zero LLM.
|
|
12771
12869
|
// HONEST DATA NOTE: the engine stamps no decidedAt on approve/dismiss (they only flip
|
|
12772
12870
|
// goal.status — approveGoal/dismissGoal in lib.mjs), and a dismissed goal goes BACK to
|
|
@@ -12834,20 +12932,51 @@ function gearSetHTML(){
|
|
|
12834
12932
|
: acct + freeUsage;
|
|
12835
12933
|
const st = state.stability;
|
|
12836
12934
|
const stability = st ? `<div class="srow"><div class="sl">Verification health<span class="sd">Last 24h · implementation ${st.implementationFailures} · environment ${st.verificationInfrastructure} · inconclusive ${st.timeoutsOrInconclusive} · worker holds ${st.workerHolds}</span></div><button class="optchip" data-gstability="1">Refresh</button></div>` : `<div class="srow"><div class="sl">Verification health<span class="sd">Loading…</span></div></div>`;
|
|
12837
|
-
|
|
12838
|
-
<div class="srow"><div class="sl">Language<span class="sd">Review checklist language for this project</span></div>
|
|
12935
|
+
const langRow = `<div class="srow"><div class="sl">Language<span class="sd">Review checklist language for this project</span></div>
|
|
12839
12936
|
<button class="optchip${lang === 'auto' ? ' on' : ''}" data-gslang="auto">Auto</button>
|
|
12840
12937
|
<button class="optchip${lang === 'en' ? ' on' : ''}" data-gslang="en">EN</button>
|
|
12841
|
-
<button class="optchip${lang === 'ja' ? ' on' : ''}" data-gslang="ja">JA</button></div
|
|
12842
|
-
|
|
12843
|
-
|
|
12844
|
-
|
|
12845
|
-
|
|
12846
|
-
|
|
12847
|
-
|
|
12848
|
-
|
|
12849
|
-
|
|
12850
|
-
|
|
12938
|
+
<button class="optchip${lang === 'ja' ? ' on' : ''}" data-gslang="ja">JA</button></div>`;
|
|
12939
|
+
const aboutRow = `<div class="srow"><div class="sl">About Galda<span class="sd">What it is, what the agent can touch, and what leaves this machine</span></div>
|
|
12940
|
+
<button class="optchip" data-gabout="1">Open</button></div>`;
|
|
12941
|
+
const dataRow = `<div class="srow dis"><div class="sl">Data folder<span class="sd">Runs are stored in the engine's chat-runs folder on the host</span></div></div>`;
|
|
12942
|
+
// Grouped headings + two-across rows (CDO pattern C, Masa 2026-08-02). The flat list
|
|
12943
|
+
// gave a reader no way to tell "settings for this project" from "settings for this
|
|
12944
|
+
// account" — the names were already there in the rows, only the structure was missing.
|
|
12945
|
+
//
|
|
12946
|
+
// A group with nothing in it renders nothing, heading included (§1.5-3 no empty box):
|
|
12947
|
+
// remoteAccessRow/shareUsageDataRow/hostReadinessRow all return '' when the engine has
|
|
12948
|
+
// not configured them, and on such a host the heading alone would be a label for air.
|
|
12949
|
+
const group = (title, rows) => {
|
|
12950
|
+
const body = rows.filter(Boolean).join('');
|
|
12951
|
+
return body ? `<h4>${title}</h4><div class="gtwo">${body}</div>` : '';
|
|
12952
|
+
};
|
|
12953
|
+
return group('Project', [langRow, remoteAccessRow(), shareUsageDataRow()])
|
|
12954
|
+
+ group('Account', [billingRow])
|
|
12955
|
+
// "System", not "This machine": hostReadinessRow() already labels itself
|
|
12956
|
+
// "This machine" (and a test asserts that exact label), so the heading would
|
|
12957
|
+
// have been the same words twice, three rows apart.
|
|
12958
|
+
+ group('System', [stability, aboutRow, hostReadinessRow(), dataRow])
|
|
12959
|
+
+ gearFeedbackHTML();
|
|
12960
|
+
}
|
|
12961
|
+
// The founder's note (Masa 2026-08-02). It is the last thing in SETTINGS because it
|
|
12962
|
+
// is the one row that asks something of the reader rather than offering a control,
|
|
12963
|
+
// and because a person who got this far has already formed an opinion.
|
|
12964
|
+
//
|
|
12965
|
+
// The honest limitation is the point of the copy, not an apology: few users so far
|
|
12966
|
+
// means workflows we have not met, and saying so is what earns the reply. Keep the
|
|
12967
|
+
// promise in it something Leon can actually keep.
|
|
12968
|
+
// `solo` = the Contact tab, where the note is the whole screen rather than the last thing
|
|
12969
|
+
// under a list: no hairline above it (there is nothing above it to separate from) and room
|
|
12970
|
+
// to breathe. Same words either way — one place to change them.
|
|
12971
|
+
function gearFeedbackHTML({ solo = false } = {}){
|
|
12972
|
+
return `<div class="gfeed${solo ? ' gsolo' : ''}">
|
|
12973
|
+
<h4>Feedback</h4>
|
|
12974
|
+
<p>Galda is early. Not many people are using it yet, so there are workflows it handles badly — quite possibly parts of yours.</p>
|
|
12975
|
+
<p>If it doesn't fit the way you work, write to me. I'd rather hear it straight and make it fit than guess.</p>
|
|
12976
|
+
<p class="gsig">— Leon, co-founder</p>
|
|
12977
|
+
<a class="gmail" href="mailto:hello@galda.app?subject=Galda%20feedback">
|
|
12978
|
+
<svg viewBox="0 0 16 16"><rect x="1.5" y="3.5" width="13" height="9" rx="1.5"/><path d="M2 4.5l6 4 6-4"/></svg>hello@galda.app</a>
|
|
12979
|
+
</div>`;
|
|
12851
12980
|
}
|
|
12852
12981
|
// The npx moment, moved to where the intent is.
|
|
12853
12982
|
//
|
|
@@ -13040,36 +13169,16 @@ function hostReadinessRow(){
|
|
|
13040
13169
|
const detail = (missing.length ? missing : checks).map((c) => c.detail).join(' · ');
|
|
13041
13170
|
return `<div class="srow${missing.length ? '' : ' dis'}"><div class="sl">This machine<span class="sd">${esc(detail)}</span></div></div>`;
|
|
13042
13171
|
}
|
|
13043
|
-
|
|
13044
|
-
|
|
13045
|
-
|
|
13046
|
-
|
|
13047
|
-
|
|
13048
|
-
|
|
13049
|
-
|
|
13050
|
-
|
|
13051
|
-
|
|
13052
|
-
|
|
13053
|
-
<div class="srow"><div class="sl">Project rules<span class="sd">What this project tells the AI — read-only · ${esc(names)}</span></div>
|
|
13054
|
-
<button class="optchip${state.rulesOpen ? ' on' : ''}" data-grules="1">${state.rulesOpen ? 'Hide' : 'View'}</button></div>
|
|
13055
|
-
${view}`;
|
|
13056
|
-
}
|
|
13057
|
-
// Fetch the active project's rules files once (cached per project), then rebuild
|
|
13058
|
-
// the settings panel so the row appears. The browser can't read the host FS, so
|
|
13059
|
-
// this is the only way in — GET /api/projects/:id/rules (engine, §1.5a).
|
|
13060
|
-
let rulesFetching = null;
|
|
13061
|
-
async function loadProjectRules(){
|
|
13062
|
-
const pid = state.active;
|
|
13063
|
-
if (state.projectRules[pid] || rulesFetching === pid) return;
|
|
13064
|
-
rulesFetching = pid;
|
|
13065
|
-
try {
|
|
13066
|
-
const r = await fetch(withKey(`/api/projects/${pid}/rules`));
|
|
13067
|
-
state.projectRules[pid] = r.ok ? await r.json() : { files: [] };
|
|
13068
|
-
} catch { state.projectRules[pid] = { files: [] }; }
|
|
13069
|
-
finally { rulesFetching = null; }
|
|
13070
|
-
// Only rebuild if the settings tab is still the one on screen.
|
|
13071
|
-
if ($('gearPop') && custTab === 'settings') buildGearPop();
|
|
13072
|
-
}
|
|
13172
|
+
// NO "Project rules" row (Masa 2026-08-02). It used to print CLAUDE.md / AGENTS.md
|
|
13173
|
+
// read-only in Settings. What ONE-CONVERSATION §1.5a actually decided was a NEGATIVE:
|
|
13174
|
+
// Galda must not grow its own "how we work" field, because the same rules would then
|
|
13175
|
+
// live in two places and drift — the repo already holds the answer. That decision does
|
|
13176
|
+
// not need a control to be honoured. The viewer was the incidental part, and it showed
|
|
13177
|
+
// a person their own file, which they can open in an editor. Removed.
|
|
13178
|
+
//
|
|
13179
|
+
// The engine keeps collectProjectRules (workerRunContext still names the files it found)
|
|
13180
|
+
// and its GET /api/projects/:id/rules, which nothing calls now — engine is a separate
|
|
13181
|
+
// lane, so that endpoint is a follow-up, not a silent edit from here.
|
|
13073
13182
|
// MCP (v46): connection status dot + connectCommand + COPY + quiet explanation.
|
|
13074
13183
|
// The dot is the app's own live engine link (SSE up/down) — the only real signal here.
|
|
13075
13184
|
function gearMcpHTML(){
|
|
@@ -13086,7 +13195,7 @@ function buildGearPop(){
|
|
|
13086
13195
|
// custRender, the sticky Save/Cancel footer belongs to THEME (the live-preview tab);
|
|
13087
13196
|
// the edit session itself stays open across tabs (Esc/outside still Cancel).
|
|
13088
13197
|
if (custTab === 'history') {
|
|
13089
|
-
pop.innerHTML =
|
|
13198
|
+
pop.innerHTML = gearShell(gearHistHTML());
|
|
13090
13199
|
wireGearCommon(pop);
|
|
13091
13200
|
for (const b of pop.querySelectorAll('[data-ghmore]')) b.onclick = () => {
|
|
13092
13201
|
const r = b.closest('.hrow'); const ex = r.classList.toggle('ex'); b.textContent = ex ? 'Less' : 'More';
|
|
@@ -13099,7 +13208,7 @@ function buildGearPop(){
|
|
|
13099
13208
|
return;
|
|
13100
13209
|
}
|
|
13101
13210
|
if (custTab === 'settings') {
|
|
13102
|
-
pop.innerHTML =
|
|
13211
|
+
pop.innerHTML = gearShell(gearSetHTML());
|
|
13103
13212
|
wireGearCommon(pop);
|
|
13104
13213
|
for (const b of pop.querySelectorAll('[data-gslang]')) b.onclick = () => saveLang(b.dataset.gslang);
|
|
13105
13214
|
for (const b of pop.querySelectorAll('[data-grelay]')) b.onclick = () => saveRelayPref(b.dataset.grelay === 'on');
|
|
@@ -13124,18 +13233,20 @@ function buildGearPop(){
|
|
|
13124
13233
|
catch {}
|
|
13125
13234
|
finally { await refresh(); if ($('gearPop')) buildGearPop(); }
|
|
13126
13235
|
};
|
|
13127
|
-
const rulesBtn = pop.querySelector('[data-grules]');
|
|
13128
|
-
if (rulesBtn) rulesBtn.onclick = () => { state.rulesOpen = !state.rulesOpen; buildGearPop(); };
|
|
13129
13236
|
const stabilityBtn = pop.querySelector('[data-gstability]');
|
|
13130
13237
|
if (stabilityBtn) stabilityBtn.onclick = () => { state.stability = null; buildGearPop(); loadStability(); };
|
|
13131
|
-
loadProjectRules();
|
|
13132
13238
|
if (!state.stability) loadStability();
|
|
13133
13239
|
if (!state.relayPref) loadRelayPref();
|
|
13134
13240
|
if (!state.analyticsPref) loadAnalyticsPref();
|
|
13135
13241
|
return;
|
|
13136
13242
|
}
|
|
13243
|
+
if (custTab === 'feedback') {
|
|
13244
|
+
pop.innerHTML = gearShell(gearFeedbackHTML({ solo: true }));
|
|
13245
|
+
wireGearCommon(pop);
|
|
13246
|
+
return;
|
|
13247
|
+
}
|
|
13137
13248
|
if (custTab === 'mcp') {
|
|
13138
|
-
pop.innerHTML =
|
|
13249
|
+
pop.innerHTML = gearShell(gearMcpHTML());
|
|
13139
13250
|
wireGearCommon(pop);
|
|
13140
13251
|
const mc2 = pop.querySelector('[data-gmcpcopy]'); if (mc2) mc2.onclick = () => {
|
|
13141
13252
|
navigator.clipboard?.writeText(state.connectCommand || '').then(() => { mc2.textContent = 'COPIED'; setTimeout(() => { mc2.textContent = 'COPY'; }, 1500); });
|
|
@@ -13206,7 +13317,7 @@ function buildGearPop(){
|
|
|
13206
13317
|
const th = state.theme || 'gradient';
|
|
13207
13318
|
const sec = (on, html) => `<div class="gsec${on ? '' : ' dis'}">${html}</div>`;
|
|
13208
13319
|
// FONT exploration section retired (v45 §1): the brand fonts are final — no chips.
|
|
13209
|
-
pop.innerHTML =
|
|
13320
|
+
pop.innerHTML = gearShell(
|
|
13210
13321
|
`<h4>THEME</h4><div class="swrow">${themeBtns}</div>` +
|
|
13211
13322
|
`<h4>BOARD</h4><div class="swrow">${boardChips}</div>` +
|
|
13212
13323
|
sec(th === 'gradient', `<h4>COLOR</h4><div class="swrow">${arcSw}</div>` +
|
|
@@ -13224,7 +13335,7 @@ function buildGearPop(){
|
|
|
13224
13335
|
(cust.btn === 0 ? `<h4>RING</h4><div class="swrow">${ringSw}</div>` : '') +
|
|
13225
13336
|
// LOGO row removed (v45 §3: code-fixed) · MCP CONNECT moved to the MCP tab (v46)
|
|
13226
13337
|
// sticky footer (v45 §3, flagship .cfoot): Save = persist NOW / Cancel = snapshot restore
|
|
13227
|
-
`<div class="cfoot"><button class="cbtn cancel" data-gccancel="1">Cancel</button><button class="cbtn save" data-gcsave="1">Save</button></div
|
|
13338
|
+
`<div class="cfoot"><button class="cbtn cancel" data-gccancel="1">Cancel</button><button class="cbtn save" data-gcsave="1">Save</button></div>`);
|
|
13228
13339
|
wireGearCommon(pop);
|
|
13229
13340
|
// EDIT SESSION (v45 §3): every handler below is LIVE PREVIEW ONLY — mutate cust/state,
|
|
13230
13341
|
// apply to the DOM, re-render the pop. localStorage is written ONLY in custSave().
|
|
@@ -13256,16 +13367,14 @@ function buildGearPop(){
|
|
|
13256
13367
|
// live previews that never persist and never close the panel; Save is the only moment
|
|
13257
13368
|
// localStorage is written; Cancel / Esc / outside-tap restore the snapshot fully. =====
|
|
13258
13369
|
let custSnap = null;
|
|
13259
|
-
function custPlace(){ // flagship:
|
|
13260
|
-
|
|
13261
|
-
|
|
13262
|
-
|
|
13263
|
-
|
|
13264
|
-
} else pop.style.left = '';
|
|
13370
|
+
function custPlace(){ // flagship: centred dialog (CDO pattern C) — CSS owns the position now,
|
|
13371
|
+
// so the measured rail edge that used to be written here must be cleared, or the inline
|
|
13372
|
+
// left would fight left:50% and the dialog would sit off-centre. Other layouts keep the
|
|
13373
|
+
// .gearwrap anchor and never had an inline left to begin with.
|
|
13374
|
+
$('gearPop').style.left = '';
|
|
13265
13375
|
}
|
|
13266
13376
|
function custOpen(tab){
|
|
13267
|
-
custTab = tab || '
|
|
13268
|
-
state.rulesOpen = false; // rules panel starts collapsed each time the gear opens
|
|
13377
|
+
custTab = tab || 'settings'; // v46 deep-link (flagship __custOpen(tab)): menu entries land on their tab
|
|
13269
13378
|
custSnap = { cust: { ...cust }, theme: state.theme, band: fsUI.band };
|
|
13270
13379
|
buildGearPop(); custPlace();
|
|
13271
13380
|
$('gearPop').classList.add('open');
|
|
@@ -13320,15 +13429,16 @@ document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && $('gearP
|
|
|
13320
13429
|
$('gearBtn').onclick = (e) => { e.stopPropagation(); const p = $('gearPop'); if (p.classList.contains('open')) custCancel(); else custOpen(); };
|
|
13321
13430
|
// outside-tap = Cancel (v45 §3). e.composedPath() is frozen at dispatch, so a swatch that
|
|
13322
13431
|
// buildGearPop() re-rendered (detached) mid-click still reads as INSIDE the pop —
|
|
13323
|
-
// selections never close the panel.
|
|
13324
|
-
//
|
|
13432
|
+
// selections never close the panel. #fsMoreBtn (the rail gear) is excluded so its own
|
|
13433
|
+
// click is never read as an outside tap — otherwise pressing it would close and reopen the
|
|
13434
|
+
// dialog in a single press. #deleteConfirmOverlay is excluded
|
|
13325
13435
|
// too (Masa指示 2026-07-16): it's a separate top-level overlay a settings row can open
|
|
13326
13436
|
// (e.g. Log out) — without this, confirming/cancelling it read as an outside tap and
|
|
13327
13437
|
// closed the settings panel underneath it as a surprise side effect.
|
|
13328
13438
|
document.addEventListener('click', (e) => {
|
|
13329
13439
|
const p = $('gearPop'); if (!p.classList.contains('open')) return;
|
|
13330
13440
|
if (e.composedPath().includes(p)) return;
|
|
13331
|
-
if (e.target.closest('.gearwrap') || e.target.closest('#
|
|
13441
|
+
if (e.target.closest('.gearwrap') || e.target.closest('#fsMoreBtn') || e.target.closest('#deleteConfirmOverlay')) return;
|
|
13332
13442
|
custCancel();
|
|
13333
13443
|
});
|
|
13334
13444
|
// Banff BACKDROP free upload (v3 §6.5): file → dataURL → #themebg. Persists via
|
package/package.json
CHANGED