@agentwhy/cli 0.2.1 → 0.3.1
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/dist/adapter/codex/contract/assumptions.js +4 -4
- package/dist/adapter/codex/contract/deliveries.js +12 -0
- package/dist/adapter/codex/contract/messages.js +8 -1
- package/dist/adapter/codex/events/cell-commands.js +191 -0
- package/dist/adapter/codex/events/delivered-output.js +30 -0
- package/dist/adapter/codex/events/rollout-scan.js +99 -7
- package/dist/core/access/command-line.js +4 -1
- package/dist/core/access/listing.js +10 -3
- package/dist/core/access/protected-access.js +94 -1
- package/dist/core/access/recorded-effect.js +22 -1
- package/dist/report/build-report.js +125 -19
- package/dist/report/render/report-page/file-story.js +33 -2
- package/dist/report/render/report-page/files-view.js +176 -55
- package/dist/report/render/report-page/files.js +51 -8
- package/dist/report/render/report-page/fix-wizard-script.js +17 -7
- package/dist/report/render/report-page/fix-wizard.js +4 -1
- package/dist/report/render/report-page/helpers.js +2 -1
- package/dist/report/render/report-page/report-page-renderer.js +51 -12
- package/dist/report/render/report-page/story-window.js +33 -12
- package/dist/report/render/report-page/to-do-view.js +42 -10
- package/dist/report/render/ui/app-sidebar.js +31 -2
- package/dist/report/render/ui/confirm-dialog.js +69 -4
- package/dist/report/render/ui/fold-line.js +2 -0
- package/dist/report/render/ui/popup.js +1 -1
- package/dist/report/render/ui/project-list.js +49 -10
- package/dist/report/render/ui/words/app-words.js +6 -0
- package/dist/report/render/ui/words/conversations-words.js +12 -0
- package/dist/report/render/ui/words/projects-words.js +9 -0
- package/dist/report/render/ui/words/report-words.js +114 -12
- package/dist/report/render/ui/words/settings-words.js +12 -9
- package/dist/report/start/conversations/calendar-view.js +1 -1
- package/dist/report/start/conversations/conversation-columns.js +6 -4
- package/dist/report/start/conversations/period-section.js +33 -17
- package/dist/report/start/conversations/periods.js +7 -1
- package/dist/report/start/render/session-status.js +14 -9
- package/dist/report/start/session-start.js +6 -3
- package/dist/report/start/settings/alerts-tab.js +2 -1
- package/package.json +1 -1
|
@@ -10,7 +10,8 @@ import { STATUS_ICON_STYLE } from '../ui/status-icon.js';
|
|
|
10
10
|
import { BUTTON_STYLE } from '../ui/button.js';
|
|
11
11
|
import { CALLOUT_STYLE } from '../ui/callout.js';
|
|
12
12
|
import { CHECKLIST_SCRIPT, CHECKLIST_STYLE } from '../ui/checklist.js';
|
|
13
|
-
import { confirmDialog, CONFIRM_DIALOG_STYLE } from '../ui/confirm-dialog.js';
|
|
13
|
+
import { confirmCase, confirmDialog, CONFIRM_DIALOG_STYLE } from '../ui/confirm-dialog.js';
|
|
14
|
+
import { MODE_SVG } from '../ui/mode-icon.js';
|
|
14
15
|
import { DRAWER_STYLE } from '../ui/drawer.js';
|
|
15
16
|
import { FILE_CHIP_SCRIPT, FILE_CHIP_STYLE } from '../ui/file-chip.js';
|
|
16
17
|
import { FOLD_LINE_STYLE } from '../ui/fold-line.js';
|
|
@@ -90,7 +91,7 @@ export class ReportPageRenderer {
|
|
|
90
91
|
scripts: [POPUP_SCRIPT, CHECKLIST_SCRIPT, ASK_PANEL_SCRIPT, FILE_CHIP_SCRIPT, PILL_TABS_SCRIPT, FIX_WIZARD_SCRIPT, HELPERS_SCRIPT, FILES_SCRIPT, ADVANCED_SCRIPT, REPORT_VIEWS_SCRIPT],
|
|
91
92
|
sidebar: appSidebar({ home: page.withIndexLink ? 'index.html' : '#todo', items: nav, showProject: false }),
|
|
92
93
|
main: '<section id="todo" data-view>' + toDoView(items, report, done, recordGaps(report).any, clock, rows, { back: page.withIndexLink, ...(page.title === undefined ? {} : { title: page.title }) }, names) + '</section>' +
|
|
93
|
-
'<section id="files" data-view>' + filesView(rows, items, protectKeys, names) + '</section>' +
|
|
94
|
+
'<section id="files" data-view>' + filesView(rows, items, protectKeys, names, report.everydayNamesLeftOut ?? 0) + '</section>' +
|
|
94
95
|
'<section id="helpers" data-view>' + helpersView(report, items, names) + '</section>' +
|
|
95
96
|
'<section id="advanced" data-view>' + advancedView(report, items, done, clock, names) + '</section>' +
|
|
96
97
|
// Windows opened from more than one view live outside all of them (a hidden view hides what is in it).
|
|
@@ -107,23 +108,61 @@ export class ReportPageRenderer {
|
|
|
107
108
|
* "Protect this file?" (P38, P38a-c): the file's own path, or every file of its name where the option is ticked. The
|
|
108
109
|
* page's script sends the rule served, and otherwise shows the `init --protect` command in the window (R61). For an
|
|
109
110
|
* everyday file it is "Make this file private?": the same rule, which is what adding a file in Settings writes.
|
|
111
|
+
*
|
|
112
|
+
* `block-or-track-from-the-report` BT1: where the page can say exactly what holds the file - an everyday file, and a
|
|
113
|
+
* private one no rule of the project holds - the window asks which mode it gets, **Block** or **Track**, in Settings'
|
|
114
|
+
* own words and glyphs (F57). Where this run could not read the settings at all (*Can't tell*), it asks nothing: a
|
|
115
|
+
* told entry written under a deny rule nobody can see changes nothing while reading as a change (BTD3).
|
|
110
116
|
*/
|
|
111
117
|
function protectWindow(row, at, patterns, names) {
|
|
112
118
|
const path = row.path;
|
|
113
119
|
// The option protects every file of its name, so it says the name alone; the chip says which file this is.
|
|
114
120
|
const name = e(nameOf(path));
|
|
115
|
-
const
|
|
121
|
+
const everyday = row.protection === 'na';
|
|
122
|
+
const words = everyday ? 'mp' : 'pr';
|
|
123
|
+
const found = ' data-pattern="' + e(patterns.exact) + '" data-pattern-every="' + e(patterns.every) + '"';
|
|
124
|
+
const sentence = (key) => inLanguages((t) => t(key, { path: '<code>' + e(path) + '</code>' }));
|
|
125
|
+
// Each case has a tick of its own, so the one the person sees is the one the script reads (BT3).
|
|
126
|
+
const tick = (which, key) => '<label class="pr-option"><input type="checkbox" data-protect-every="' + which + '"> ' +
|
|
127
|
+
inLanguages((t) => t(key, { name: '<code>' + name + '</code>' })) + '</label>';
|
|
128
|
+
const say = (key) => inLanguages((t) => t(key));
|
|
129
|
+
// BT8: Block hands over `init --protect`, as it always has; no flag writes a told list, so Track hands over the
|
|
130
|
+
// command that opens the page where the change can be made, as Settings' own switch does.
|
|
131
|
+
const handover = (which, body) => '<div class="wz-handover" data-note-command="' + which + '" hidden><p class="wz-handover-text">' + body + '</p>' + commandRow() + '</div>';
|
|
132
|
+
const note = (body) => '<div class="cf-note" data-protect-note>' +
|
|
133
|
+
'<p class="wz-reason" data-note-reason role="alert" hidden></p>' + body + '</div>';
|
|
134
|
+
if (!everyday && row.protection !== 'no') {
|
|
135
|
+
return confirmDialog({
|
|
136
|
+
id: 'protect-' + at,
|
|
137
|
+
title: say(words + '.title'),
|
|
138
|
+
subject: '<span class="tc-chip" title="' + e(path) + '">' + e(names(path)) + '</span>',
|
|
139
|
+
sentence: sentence(words + '.sentence'),
|
|
140
|
+
option: tick('block', words + '.option'),
|
|
141
|
+
cancel: say('app.cancel'),
|
|
142
|
+
confirm: say(words + '.confirm'),
|
|
143
|
+
confirmAttributes: ' data-protect="' + at + '" data-protect-mode="block"' + found,
|
|
144
|
+
note: note(handover('block', say(words + '.cmd'))),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
const mode = (which) => ({
|
|
148
|
+
mark: MODE_SVG[which],
|
|
149
|
+
markTone: which === 'block' ? 'mint' : 'sand',
|
|
150
|
+
name: say('md.' + which),
|
|
151
|
+
why: say('md.' + which + '.why'),
|
|
152
|
+
sentence: sentence(words + (which === 'block' ? '' : '.tell') + '.sentence'),
|
|
153
|
+
option: tick(which, words + (which === 'block' ? '' : '.tell') + '.option'),
|
|
154
|
+
confirm: say('md.go.' + which),
|
|
155
|
+
// BT4: coral only where the change takes protection away - Track on a private file, which `refuse` was keeping
|
|
156
|
+
// shell commands off. On an everyday file nothing held it, so tracking it only adds a notice, and is mint (BTD4).
|
|
157
|
+
...(which === 'tell' && !everyday ? { tone: 'primary' } : {}),
|
|
158
|
+
confirmAttributes: ' data-protect="' + at + '" data-protect-mode="' + which + '"' + found,
|
|
159
|
+
});
|
|
116
160
|
return confirmDialog({
|
|
117
161
|
id: 'protect-' + at,
|
|
118
|
-
title:
|
|
162
|
+
title: say(words + '.title'),
|
|
119
163
|
subject: '<span class="tc-chip" title="' + e(path) + '">' + e(names(path)) + '</span>',
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
confirm: inLanguages((t) => t(words + '.confirm')),
|
|
124
|
-
confirmAttributes: ' data-protect="' + at + '" data-pattern="' + e(patterns.exact) + '" data-pattern-every="' + e(patterns.every) + '"',
|
|
125
|
-
note: '<div class="cf-note" data-protect-note>' +
|
|
126
|
-
'<p class="wz-reason" data-note-reason role="alert" hidden></p>' +
|
|
127
|
-
'<div class="wz-handover" data-note-command hidden><p class="wz-handover-text">' + inLanguages((t) => t(words + '.cmd')) + '</p>' + commandRow() + '</div></div>',
|
|
164
|
+
cancel: say('app.cancel'),
|
|
165
|
+
choice: { question: say('md.q'), options: [mode('block'), mode('tell')] },
|
|
166
|
+
note: note(confirmCase(1, handover('block', say(words + '.cmd'))) + confirmCase(2, handover('tell', say('md.cmd.tell')))),
|
|
128
167
|
});
|
|
129
168
|
}
|
|
@@ -45,7 +45,7 @@ export function storyPopup(story, subject, id, sessionId, clock, extras, action)
|
|
|
45
45
|
'<p class="sw-summary">' + (subject.summary ?? summaryOf(story, subject.keys)) + '</p>' +
|
|
46
46
|
(extras.context === undefined ? '' : '<p class="sw-context">' + extras.context + '</p>') + '</div>';
|
|
47
47
|
const body = '<div class="sw-body">' + pillTabs([
|
|
48
|
-
{ label: label('st.tab.story'), panel: storyPanel(story, subject
|
|
48
|
+
{ label: label('st.tab.story'), panel: storyPanel(story, subject, clock, names) },
|
|
49
49
|
{ label: label('st.tab.diagram'), panel: diagramPanel(story, subject, names) },
|
|
50
50
|
{ label: label('st.tab.record'), panel: recordPanel(story, subject, sessionId, clock, names) },
|
|
51
51
|
...(extras.tabs ?? []),
|
|
@@ -96,6 +96,7 @@ function sentences(entries, keys, names) {
|
|
|
96
96
|
const lines = (t) => t(entry.inResult === true ? 'st.sub.inResultLines' : 'st.sub.lines', { n: entry.lines ?? 0, did: entry.did === undefined ? '' : '<code>' + e(entry.did) + '</code>' });
|
|
97
97
|
return { title: (t) => t(title), sub: helperFirst ? job : entry.lines !== undefined ? lines : entry.inResult === true ? inResult : did };
|
|
98
98
|
}
|
|
99
|
+
case 'changed': return { title: (t) => t('st.e.changed'), sub: helperFirst ? job : did };
|
|
99
100
|
case 'named': return { title: (t) => t('st.e.named'), sub: helperFirst ? job : did };
|
|
100
101
|
case 'unknown': return { title: (t) => t('st.e.unknown'), sub: helperFirst ? job : (t) => t('st.sub.unknown') };
|
|
101
102
|
case 'stopped': return { title: (t) => t('st.e.stopped'), sub: helperFirst ? job : did };
|
|
@@ -120,7 +121,11 @@ function sentences(entries, keys, names) {
|
|
|
120
121
|
* P13: one entry per record that named the file, then the AI company - whose line never says what it keeps (F24) - where
|
|
121
122
|
* its contents reached an AI at all.
|
|
122
123
|
*/
|
|
123
|
-
function storyPanel(story,
|
|
124
|
+
function storyPanel(story, subject, clock, names) {
|
|
125
|
+
const { keys, exposed } = subject;
|
|
126
|
+
// EF1: the same fact, in the tone the file deserves. "It was exposed", in coral, warns about a file nobody marked
|
|
127
|
+
// private and that the row says there is nothing to do about; the contents did reach the AI, and that is all it says.
|
|
128
|
+
const everyday = subject.everyday === true;
|
|
124
129
|
const lines = sentences(story.entries, keys, names);
|
|
125
130
|
// P13, M4: each entry's time beside it where the record has one; the entries stay in the record's order either way.
|
|
126
131
|
const timed = story.entries.some((each) => each.at !== undefined);
|
|
@@ -131,7 +136,7 @@ function storyPanel(story, keys, clock, exposed, names) {
|
|
|
131
136
|
(sub === '' ? '' : '<div class="sw-ev-sub">' + sub + '</div>') + '</div></li>';
|
|
132
137
|
return '<ol class="sw-story">' +
|
|
133
138
|
story.entries.map((each, at) => entry(avatar(initialsOf(each.agent), each.agent.ordinal === undefined ? 'coral' : 'grey', 40), inLanguages((t) => whoOf(each.agent, t)), inLanguages((t) => lines[at]?.title(t) ?? ''), inLanguages((t) => lines[at]?.sub(t) ?? ''), !exposed && at === story.entries.length - 1, false, each.at)).join('') +
|
|
134
|
-
(exposed ? entry(avatar('↗', 'alert', 40), inLanguages((t) => t('st.company')), inLanguages((t) => t('st.e.company')), inLanguages((t) => t('st.sub.company')), true,
|
|
139
|
+
(exposed ? entry(avatar('↗', everyday ? 'grey' : 'alert', 40), inLanguages((t) => t(everyday ? 'st.company.everyday' : 'st.company')), inLanguages((t) => t('st.e.company')), inLanguages((t) => t('st.sub.company')), true, !everyday) : '') +
|
|
135
140
|
'</ol>';
|
|
136
141
|
}
|
|
137
142
|
/** What a node of the diagram says it did: "read the keys · 2×", "passed it back". */
|
|
@@ -140,19 +145,25 @@ function didOf(holder, story, keys) {
|
|
|
140
145
|
const named = story.entries.some((entry) => entry.agent.index === holder.agent.index && entry.kind === 'named');
|
|
141
146
|
return inLanguages((t) => [
|
|
142
147
|
...(holder.read ? [t(keys ? 'st.d.readKeys' : 'st.d.readIt') + (reads > 1 ? ' · ' + t('st.d.times', { n: reads }) : '')] : []),
|
|
143
|
-
...(
|
|
148
|
+
...(holder.changed ? [t('st.d.changed')] : []),
|
|
149
|
+
...(!holder.read && !holder.changed && named ? [t('st.d.named')] : []),
|
|
144
150
|
...(holder.passed ? [t('st.d.passed')] : []),
|
|
145
151
|
...(holder.saved ? [t('st.d.saved')] : []),
|
|
146
152
|
...(holder.stopped ? [t('st.d.stopped')] : []),
|
|
147
153
|
].join(' · '));
|
|
148
154
|
}
|
|
155
|
+
/** What the diagram's file box is called: a private file, or a file no rule marks private (EF1). */
|
|
156
|
+
function fileSub(subject) {
|
|
157
|
+
return subject.everyday === true ? 'st.d.fileEveryday' : 'st.d.file';
|
|
158
|
+
}
|
|
149
159
|
/** How an AI's line reaches the file: read beats stopped beats only its name; none where it only passed something on. */
|
|
150
160
|
function reachOf(holder, story) {
|
|
151
161
|
if (holder.read)
|
|
152
162
|
return 'read';
|
|
153
163
|
if (holder.stopped)
|
|
154
164
|
return 'stopped';
|
|
155
|
-
|
|
165
|
+
// A write reaches the file as surely as a listing names it, and says nothing about what was shown to the AI.
|
|
166
|
+
return story.entries.some((entry) => entry.agent.index === holder.agent.index && (entry.kind === 'named' || entry.kind === 'changed')) ? 'named' : undefined;
|
|
156
167
|
}
|
|
157
168
|
const NODE_HEIGHT = 64;
|
|
158
169
|
const ROW = 84;
|
|
@@ -238,9 +249,9 @@ function diagramPanel(story, subject, names) {
|
|
|
238
249
|
return box(at(idOf(holder)), initialsOf(holder.agent), toneOf(reach), inLanguages((t) => whoOf(holder.agent, t)), didOf(holder, story, keys), reach === 'read' ? ' hd-risk' : '');
|
|
239
250
|
}).join('') +
|
|
240
251
|
(exposed || subject.look === undefined
|
|
241
|
-
? box(at('file'), '!', 'coral', '<span class="hd-mono">' + e(names(subject.path)) + '</span>', inLanguages((t) => t(
|
|
242
|
-
: box(at('file'), subject.look.glyph, subject.look.tone, '<span class="hd-mono">' + e(names(subject.path)) + '</span>', inLanguages((t) => t(
|
|
243
|
-
(company === undefined ? '' : box(at('company'), '↗', 'company', inLanguages((t) => t('st.company')), inLanguages((t) => t('st.d.company')), ' sd-company'));
|
|
252
|
+
? box(at('file'), subject.everyday === true ? '·' : '!', subject.everyday === true ? 'grey' : 'coral', '<span class="hd-mono">' + e(names(subject.path)) + '</span>', inLanguages((t) => t(fileSub(subject))), subject.everyday === true ? '' : ' hd-risk')
|
|
253
|
+
: box(at('file'), subject.look.glyph, subject.look.tone, '<span class="hd-mono">' + e(names(subject.path)) + '</span>', inLanguages((t) => t(fileSub(subject))))) +
|
|
254
|
+
(company === undefined ? '' : box(at('company'), '↗', subject.everyday === true ? 'grey' : 'company', inLanguages((t) => t(subject.everyday === true ? 'st.company.everyday' : 'st.company')), inLanguages((t) => t('st.d.company')), subject.everyday === true ? '' : ' sd-company'));
|
|
244
255
|
return '<div class="hd-scroll sd-diagram"><div class="hd-board" data-diagram style="min-width:' + Math.round(width * 0.8) + 'px;height:' + height + 'px">' +
|
|
245
256
|
'<svg class="hd-lines" viewBox="0 0 ' + width + ' ' + height + '" preserveAspectRatio="none" aria-hidden="true">' + lines.join('') + '</svg>' +
|
|
246
257
|
legend + boxes + '</div></div>' +
|
|
@@ -252,7 +263,9 @@ function recordPanel(story, subject, sessionId, clock, names) {
|
|
|
252
263
|
const lines = sentences(story.entries, subject.keys, names);
|
|
253
264
|
const numbers = stats([
|
|
254
265
|
{ label: label('st.stat.opened'), value: String(story.opened) },
|
|
255
|
-
|
|
266
|
+
// Coral counts a private file's readers. On a file nobody marked private it would colour an ordinary read as a
|
|
267
|
+
// finding, and the row beside it says there is nothing to do (EF1).
|
|
268
|
+
{ label: label('st.stat.readers'), value: String(story.readers), ...(subject.everyday === true ? {} : { tone: 'coral' }) },
|
|
256
269
|
{ label: label('st.stat.stopped'), value: String(story.stopped) },
|
|
257
270
|
{ label: label('st.stat.record'), value: label(story.complete ? 'st.complete' : 'st.gaps'), ...(story.complete ? { tone: 'mint' } : {}) },
|
|
258
271
|
], 'record');
|
|
@@ -272,8 +285,13 @@ function recordPanel(story, subject, sessionId, clock, names) {
|
|
|
272
285
|
'<span>' + (entry.did === undefined ? '<span class="sw-dim">—</span>' : '<span class="sw-tool">' + e(entry.did) + '</span>') + '</span>' +
|
|
273
286
|
'<span>' + tag(label('st.res.' + entry.outcome), entry.outcome === 'succeeded' ? 'mint' : entry.outcome === 'blocked' ? 'grey' : 'coral', 'sm') + '</span>' +
|
|
274
287
|
'<span class="sw-mono sw-dim">' + entry.evidence.map((one) => e(one)).join(' – ') + '</span></div>').join('') +
|
|
275
|
-
'</div>'
|
|
288
|
+
'</div>' +
|
|
289
|
+
// EF8: a story stops at the calls a window is worth, and the rest are counted - never dropped in silence.
|
|
290
|
+
(story.leftOut === undefined ? '' : '<p class="sw-times-note">' + label('st.rec.leftOut', { n: story.leftOut }) + '</p>');
|
|
276
291
|
const yes = (on) => '<span class="sw-yn' + (on ? ' sw-yes' : '') + '">' + label(on ? 'st.yes' : 'st.no') + '</span>';
|
|
292
|
+
// EF4: agentwhy follows a value out of a protected file only, so for any other file these four were never looked for.
|
|
293
|
+
// "No" would be a claim it cannot support, and invariant 4 forbids exactly that.
|
|
294
|
+
const followed = (on) => story.traced ? yes(on) : '<span class="sw-yn sw-untracked">' + label('st.untracked') + '</span>';
|
|
277
295
|
const roleOf = (agent, t) => agent.ordinal === undefined ? t('st.role.main')
|
|
278
296
|
: agent.broughtBy === undefined ? t('st.role.helperAny')
|
|
279
297
|
: t('st.role.helper', { who: agent.broughtBy === 'main' ? t('st.mainLower') : t('st.helper', { ordinal: agent.broughtBy }) });
|
|
@@ -283,7 +301,8 @@ function recordPanel(story, subject, sessionId, clock, names) {
|
|
|
283
301
|
story.holders.map((holder) => '<div class="sw-grid sw-grid-each">' +
|
|
284
302
|
'<span class="sw-strong">' + inLanguages((t) => whoOf(holder.agent, t)) + '</span>' +
|
|
285
303
|
'<span class="sw-dim">' + inLanguages((t) => roleOf(holder.agent, t)) + '</span>' +
|
|
286
|
-
|
|
304
|
+
'<span>' + yes(holder.read) + '</span>' +
|
|
305
|
+
[holder.passed, holder.saved, holder.repeated, holder.used].map((on) => '<span>' + followed(on) + '</span>').join('') +
|
|
287
306
|
'<span class="sw-mono">' + holder.agent.actions + '</span></div>').join('') +
|
|
288
307
|
'</div>';
|
|
289
308
|
const rule = ruleKey(subject.pattern);
|
|
@@ -291,7 +310,8 @@ function recordPanel(story, subject, sessionId, clock, names) {
|
|
|
291
310
|
const details = '<div class="sw-rec-title sw-rec-alone">' + label('st.rec.details') + '</div><div class="sw-facts">' +
|
|
292
311
|
fact('st.fact.path', e(subject.path), true) +
|
|
293
312
|
fact('st.fact.rule', subject.pattern === undefined ? '—' : e(subject.pattern), true) +
|
|
294
|
-
|
|
313
|
+
// EF1: a file no rule marks private has no answer to "why it's private", and is not given one.
|
|
314
|
+
(subject.everyday === true ? '' : fact('st.fact.why', label(rule === undefined ? 'st.why.other' : rule.replace('rule.', 'st.why.')), false)) +
|
|
295
315
|
fact('st.fact.session', e(sessionId), true) +
|
|
296
316
|
fact('st.fact.complete', label(story.complete ? 'st.complete.yes' : 'st.complete.no'), false) +
|
|
297
317
|
'</div>';
|
|
@@ -347,6 +367,7 @@ export const STORY_WINDOW_STYLE = String.raw `
|
|
|
347
367
|
.sw-dot{width:8px;height:8px;border-radius:50%;background:var(--coral);flex:none}.sw-dot-grey{background:var(--white-35)}
|
|
348
368
|
.sw-tool{font-family:var(--mono);font-size:13px;color:var(--text);background:var(--white-07);border:1px solid var(--white-10);border-radius:6px;padding:3px 8px}
|
|
349
369
|
.sw-yn{font-size:12px;font-weight:700;border-radius:999px;padding:4px 11px;color:var(--text-2);background:var(--white-07)}
|
|
370
|
+
.sw-untracked{font-size:11px;letter-spacing:0.02em;color:var(--text-3);background:transparent;border:1px solid var(--white-12)}
|
|
350
371
|
.sw-yes{color:var(--on-coral);background:var(--coral)}
|
|
351
372
|
.sw-facts{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));border-radius:14px;border:1px solid var(--white-09);overflow:hidden}
|
|
352
373
|
.sw-fact{display:grid;grid-template-columns:150px minmax(0,1fr);gap:14px;padding:14px 18px;border-bottom:1px solid var(--white-06);border-right:1px solid var(--white-06);align-items:baseline}
|
|
@@ -95,6 +95,20 @@ function codexGap(report) {
|
|
|
95
95
|
return 'legacy';
|
|
96
96
|
return capabilities.some((one) => one.question === 'actions' && one.state !== 'supported') ? 'partial' : undefined;
|
|
97
97
|
}
|
|
98
|
+
/**
|
|
99
|
+
* Whether a record's gaps leave nothing about a private file not known (F17, amended 2026-10-05): no private file whose
|
|
100
|
+
* row is an attempt without a known end - an attempt like that at a file the row already shows read adds nothing to
|
|
101
|
+
* what is known of it - no file's text a process printed that no agent is shown to have received (X14), no command of a
|
|
102
|
+
* cell left unread (X23a), and no gap but the format's own - what it never records, a text that may not be whole, a copy
|
|
103
|
+
* of the agent's own words joined to no utterance (X31). A missing source, a record that could not be read, an action
|
|
104
|
+
* or an agent left unjoined: each leaves what happened not known.
|
|
105
|
+
*/
|
|
106
|
+
function nothingUnknown(report, rows) {
|
|
107
|
+
const formats = new Set(['capability-absent', 'capability-unmeasured', 'result-incomplete']);
|
|
108
|
+
const formatOwn = (gap) => (formats.has(gap.kind) && !(gap.kind === 'capability-unmeasured' && gap.question === 'actions')) ||
|
|
109
|
+
(gap.kind === 'relation-unresolved' && gap.question === 'own-words');
|
|
110
|
+
return !rows.some((row) => row.private && row.access === 'unknown') && (report.tally.printedUnseen ?? 0) === 0 && report.gaps.every(formatOwn);
|
|
111
|
+
}
|
|
98
112
|
/** P60: a record with a gap says so above the list, and where to read what is missing - for Codex, in its own words (X23). */
|
|
99
113
|
function gapBanner(report) {
|
|
100
114
|
const words = codexGap(report) === undefined ? 'rp.gap.banner' : 'rp.gap.codex.banner';
|
|
@@ -131,16 +145,30 @@ function nothingView(report, gaps, rows, context, names) {
|
|
|
131
145
|
// The maintainer, 2026-09-30: where the record shows a private file stopped, and none whose outcome is not recorded,
|
|
132
146
|
// that is said in mint - the protection worked - and the gap moves to the line under it; never "All good.".
|
|
133
147
|
const stopped = rows.some((row) => row.private && row.access === 'stopped') && !rows.some((row) => row.private && row.access === 'unknown');
|
|
148
|
+
// F17 as amended 2026-10-05 by the maintainer, here as on the conversation's row: a Codex record whose gaps are the
|
|
149
|
+
// format's own, and that leaves nothing about a private file not known, is answered as the clean page answers -
|
|
150
|
+
// "All good." / "Nothing to fix." ("Claude's looks better", the same day) - and what Codex does not write down is
|
|
151
|
+
// said in the lead, after what the clean page would say there. Only where it reached a private file or was stopped
|
|
152
|
+
// from one: with neither, the gaps are all the record says, and its row says "Couldn't check fully".
|
|
153
|
+
const known = codex !== undefined && (stopped || report.tally.filesReached > 0) && nothingUnknown(report, rows);
|
|
154
|
+
const calm = stopped || known;
|
|
155
|
+
// A read the person let happen is said as the clean page says it (F57a); names alone, as nothing private read.
|
|
156
|
+
const answer = stopped ? 'rp.gap.stopped' : allowedReads(rows).length > 0 ? 'rp.nothing.allowed' : 'rp.nothing.title';
|
|
157
|
+
const codexLead = codex === 'legacy' ? 'rp.gap.codex.legacy' : 'rp.gap.codex.lead';
|
|
134
158
|
return '<div class="rp-narrow rp-alone">' +
|
|
135
159
|
hero({
|
|
136
160
|
eyebrow: '',
|
|
137
|
-
fact: say('rp.gap.fact'),
|
|
138
|
-
action: say(
|
|
139
|
-
|
|
161
|
+
fact: say(known ? 'rp.nothing.fact' : 'rp.gap.fact'),
|
|
162
|
+
action: say(known ? 'rp.nothing.action' : stopped ? 'rp.gap.stopped'
|
|
163
|
+
: codex === undefined ? 'rp.gap.action' : codex === 'legacy' ? 'rp.gap.codex.legacyAction' : 'rp.gap.codex.action'),
|
|
164
|
+
lead: known ? inLanguages((t) => t(answer) + ' ' + t(codexLead))
|
|
165
|
+
: say(codex === undefined ? 'rp.gap.body' : codex === 'legacy' ? 'rp.gap.codex.legacy' : stopped ? 'rp.gap.codex.lead' : 'rp.gap.codex.body'),
|
|
140
166
|
centred: true,
|
|
141
|
-
...(
|
|
167
|
+
...(calm ? { calm: true, mark: 'tick' } : { mark: 'unsure' }),
|
|
142
168
|
}) +
|
|
143
|
-
|
|
169
|
+
// The maintainer, 2026-10-05: always white. Reading what the record misses fixes nothing, and coral is for a fix
|
|
170
|
+
// (guidelines §1); a stop and "All good." stood over a coral button that read as one more thing to do.
|
|
171
|
+
heroGo(say('rp.gap.link'), 'light') +
|
|
144
172
|
youAsked(context.title) + whatHappened(report, rows, { shape: false, gaps }, names) + back + '</div>';
|
|
145
173
|
}
|
|
146
174
|
const named = rows.filter((row) => row.private && row.access === 'name').length;
|
|
@@ -158,8 +186,8 @@ function nothingView(report, gaps, rows, context, names) {
|
|
|
158
186
|
pill({ label: say('rp.clean.record'), tone: 'secondary', href: '#advanced' }) + '</div></details></div>';
|
|
159
187
|
}
|
|
160
188
|
/** The one button under a screen's answer, where it leads to the record (P59, P60). */
|
|
161
|
-
function heroGo(label) {
|
|
162
|
-
return '<div class="rp-hero-go">' + pill({ label, tone
|
|
189
|
+
function heroGo(label, tone = 'primary') {
|
|
190
|
+
return '<div class="rp-hero-go">' + pill({ label, tone, size: 'lg', href: '#advanced', attributes: ' data-record-link' }) + '</div>';
|
|
163
191
|
}
|
|
164
192
|
/**
|
|
165
193
|
* The private files the AI read that ask for nothing (F57a): on a screen with nothing to fix, every private file read is
|
|
@@ -202,10 +230,14 @@ function whatHappened(report, rows, under, names) {
|
|
|
202
230
|
line: say('rp.clean.allowedLine', { n: allowed.length }), paths: allowed });
|
|
203
231
|
}
|
|
204
232
|
const named = privately('name');
|
|
233
|
+
// A file whose text a process printed and no agent is shown to have seen (X14): what it saw of it is not known.
|
|
234
|
+
const printed = new Set(report.stories.filter((story) => story.read === true).map((story) => story.path));
|
|
205
235
|
if (named.length > 0) {
|
|
206
236
|
lines.push({ glyph: LOOKS.name.glyph, tone: LOOKS.name.tone, title: say('rp.clean.named', { n: named.length }), tag: { label: say(LOOKS.name.label), tone: 'blue' },
|
|
207
|
-
// X10:
|
|
208
|
-
|
|
237
|
+
// X10: a name seen is not "nothing to do" where an attempt has no known end - the record cannot show the AI saw no
|
|
238
|
+
// more. Amended 2026-10-05 by the maintainer: under P60's gap card alone, with every attempt's end known, it is,
|
|
239
|
+
// as its row in Conversations says.
|
|
240
|
+
line: say(under.gaps && (unknown.length > 0 || named.some((path) => printed.has(path))) ? 'rp.clean.namedLineGap' : 'rp.clean.namedLine', { n: named.length }), paths: named });
|
|
209
241
|
}
|
|
210
242
|
const stopped = privately('stopped');
|
|
211
243
|
if (stopped.length > 0) {
|
|
@@ -262,7 +294,7 @@ export const TO_DO_VIEW_STYLE = String.raw `
|
|
|
262
294
|
.rp-todo-label{font-size:14px;color:var(--text-2)}
|
|
263
295
|
.rp-gap{display:flex;gap:12px;align-items:flex-start;padding:14px 16px;margin:0 0 18px;border-radius:14px;background:var(--coral-07);border:1px solid var(--coral-35);font-size:15px;line-height:1.5}
|
|
264
296
|
.rp-gap-mark{flex:none;width:24px;height:24px;border-radius:50%;background:var(--coral);color:var(--on-coral);font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center}
|
|
265
|
-
.rp-gap-link{font-weight:600;color:var(--
|
|
297
|
+
.rp-gap-link{font-weight:600;color:var(--text);white-space:nowrap}.rp-gap-link:hover{color:var(--white);text-decoration:underline}
|
|
266
298
|
.rp-hero-go{display:flex;justify-content:center;margin:-12px 0 36px}
|
|
267
299
|
.rp-narrow .hero-lead code{font-size:14px;color:var(--text-soft)}
|
|
268
300
|
/* X28: which AI the page is about, readable at a glance above the heading - the badge of the kit, larger here alone. */
|
|
@@ -26,13 +26,35 @@ export function appSidebar(spec) {
|
|
|
26
26
|
(spec.project === undefined || spec.place === undefined ? '' : '<span class="sb-project-place">' + e(spec.place) + '</span>') +
|
|
27
27
|
'</span>' + (link ? '<span class="sb-project-go" aria-hidden="true">›</span></a>' : '</div>')) +
|
|
28
28
|
'<nav class="sb-nav"' + labelAttributes((t) => t('app.nav')) + '>' + spec.items.map(item).join('') + '</nav>' +
|
|
29
|
-
'<div class="sb-foot">' +
|
|
29
|
+
'<div class="sb-foot">' + support() +
|
|
30
30
|
'<select class="sb-lang js-only" id="lang"' + labelAttributes((t) => t('app.lang')) + '>' +
|
|
31
31
|
LANGS.map((lang) => '<option value="' + lang + '"' + (lang === DEFAULT_LANG ? ' selected' : '') + '>' + LANG_NAMES[lang] + '</option>').join('') +
|
|
32
32
|
'</select>' +
|
|
33
33
|
'<div class="sb-local"><span class="sb-dot" aria-hidden="true"></span><span>' + inLanguages((t) => t('app.local')) + '</span></div>' +
|
|
34
34
|
'</div></aside>';
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Where a person can support agentwhy (F7, added 2026-10-05 by the maintainer): the project's sponsors page, and its
|
|
38
|
+
* page for companies. Plain links, opened in a new tab with no referrer, as F23's provider links are - a served page's
|
|
39
|
+
* address holds its token - so the page still makes no request of its own, and "Nothing is uploaded" stays true.
|
|
40
|
+
*/
|
|
41
|
+
const SUPPORT = [
|
|
42
|
+
{ name: 'app.support.sponsor', href: 'https://www.agentwhy.dev/sponsors.html', main: true },
|
|
43
|
+
{ name: 'app.support.companies', href: 'https://www.agentwhy.dev/companies.html' },
|
|
44
|
+
];
|
|
45
|
+
/** The only addresses outside the page any page holds: each a link a person may click, never a request (F7). */
|
|
46
|
+
export const SUPPORT_ADDRESSES = SUPPORT.map((link) => link.href);
|
|
47
|
+
/**
|
|
48
|
+
* Quiet by design (the maintainer, 2026-10-05: "subtle, not in the user's eyes, premium"): two small links in the
|
|
49
|
+
* column's muted grey, no card, no colour and no count, that brighten only under the pointer - the page's work stays
|
|
50
|
+
* the one thing that asks for attention (guidelines §1).
|
|
51
|
+
*/
|
|
52
|
+
function support() {
|
|
53
|
+
return '<div class="sb-support">' +
|
|
54
|
+
SUPPORT.map((link) => '<a class="sb-support-link' + (link.main === true ? ' sb-support-main' : '') + '" href="' + link.href + '"' +
|
|
55
|
+
' target="_blank" rel="noopener noreferrer"><span>' + inLanguages((t) => t(link.name)) + '</span><span class="sb-support-go" aria-hidden="true">↗</span></a>').join('') +
|
|
56
|
+
'</div>';
|
|
57
|
+
}
|
|
36
58
|
/** The first letter or digit of a folder's name, for its mark: `test-project` is T, `_scratch` is S. */
|
|
37
59
|
export function initial(name) {
|
|
38
60
|
return (/[\p{L}\p{N}]/u.exec(name)?.[0] ?? '·').toUpperCase();
|
|
@@ -71,10 +93,17 @@ export const APP_SIDEBAR_STYLE = String.raw `
|
|
|
71
93
|
.sb-lang{align-self:flex-start;margin:0 10px;background:var(--card);color:var(--text-2);border:1px solid var(--white-10);border-radius:8px;padding:5px 8px;font:inherit;font-size:13px}
|
|
72
94
|
.sb-local{padding:16px 10px 0;border-top:1px solid var(--white-07);display:flex;gap:10px;font-size:13px;line-height:1.5;color:var(--text-2)}
|
|
73
95
|
.sb-dot{flex:none;width:8px;height:8px;border-radius:50%;background:var(--mint);margin-top:6px}
|
|
96
|
+
.sb-support{display:flex;flex-direction:column;gap:2px;padding:0 10px}
|
|
97
|
+
.sb-support-link{display:inline-flex;align-items:center;gap:6px;align-self:flex-start;padding:3px 0;font-size:12.5px;font-weight:500;letter-spacing:0.01em;color:var(--text-3);transition:color .15s}
|
|
98
|
+
.sb-support-link:hover{color:var(--text-soft)}
|
|
99
|
+
.sb-support-main:hover{color:var(--coral-text)}
|
|
100
|
+
.sb-support-go{font-size:11px;opacity:.7;transition:transform .15s}
|
|
101
|
+
.sb-support-link:hover .sb-support-go{transform:translate(1px,-1px)}
|
|
74
102
|
@media (max-width:860px){
|
|
75
103
|
.sb{position:static;width:auto;height:auto;border-right:0;border-bottom:1px solid var(--white-07);padding:16px}
|
|
76
104
|
.sb-brand{padding:0 4px 14px}.sb-project{margin-bottom:12px}
|
|
77
105
|
.sb-nav{flex-direction:row;overflow-x:auto;gap:6px}.sb-item{white-space:nowrap}
|
|
78
|
-
.sb-foot{flex-direction:row;align-items:center;justify-content:space-between;margin-top:12px}.sb-local{border-top:0;padding:0}.sb-lang{margin:0}
|
|
106
|
+
.sb-foot{flex-direction:row;flex-wrap:wrap;align-items:center;justify-content:space-between;gap:10px 14px;margin-top:12px}.sb-local{border-top:0;padding:0}.sb-lang{margin:0}
|
|
107
|
+
.sb-support{flex-direction:row;flex-wrap:wrap;gap:4px 16px;padding:0;flex-basis:100%}
|
|
79
108
|
}
|
|
80
109
|
`;
|
|
@@ -2,23 +2,58 @@
|
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
import { pill } from './button.js';
|
|
4
4
|
import { CLOSES, popup } from './popup.js';
|
|
5
|
+
/**
|
|
6
|
+
* The parts one case supplies, each drawn outside `inLanguages` so the swap and the language rule never fight over
|
|
7
|
+
* `display`. Exported for what a window builds itself and hands over as one piece - the note, whose handed-over
|
|
8
|
+
* command differs with the case (BT8).
|
|
9
|
+
*/
|
|
10
|
+
export function confirmCase(at, body) {
|
|
11
|
+
return inCase(at, body);
|
|
12
|
+
}
|
|
13
|
+
function inCase(at, body) {
|
|
14
|
+
return body === '' ? '' : '<span class="cf-case cf-case-' + at + '">' + body + '</span>';
|
|
15
|
+
}
|
|
16
|
+
function confirmButton(spec) {
|
|
17
|
+
return pill({ label: spec.confirm, tone: spec.tone ?? 'mint', size: 'lg', button: true, attributes: spec.confirmAttributes });
|
|
18
|
+
}
|
|
19
|
+
function choiceBlock(id, choice) {
|
|
20
|
+
const question = id + '-q';
|
|
21
|
+
// A real radio behind the kit's own ring (`scope-choice.ts`), so the choice is made and read the same with a script
|
|
22
|
+
// or without one, and the card that is chosen is seen before it is read (guidelines §9.3).
|
|
23
|
+
const option = (one, at) => '<label class="cf-opt cf-opt-' + (one.markTone ?? 'grey') + '">' +
|
|
24
|
+
'<input class="cf-radio cf-radio-' + at + '" type="radio" name="' + id + '-case" value="' + at + '"' + (at === 1 ? ' checked' : '') + '>' +
|
|
25
|
+
'<span class="cf-ring" aria-hidden="true"></span>' +
|
|
26
|
+
'<span class="cf-opt-mark" aria-hidden="true">' + one.mark + '</span>' +
|
|
27
|
+
'<span><span class="cf-opt-name">' + one.name + '</span><span class="cf-opt-why">' + one.why + '</span></span></label>';
|
|
28
|
+
return '<div class="cf-choice" role="radiogroup" aria-labelledby="' + question + '">' +
|
|
29
|
+
'<span class="cf-choice-q" id="' + question + '">' + choice.question + '</span>' +
|
|
30
|
+
option(choice.options[0], 1) + option(choice.options[1], 2) + '</div>';
|
|
31
|
+
}
|
|
5
32
|
export function confirmDialog(spec) {
|
|
6
33
|
const title = spec.id + '-title';
|
|
34
|
+
const sentence = spec.choice === undefined ? spec.sentence
|
|
35
|
+
: inCase(1, spec.choice.options[0].sentence) + inCase(2, spec.choice.options[1].sentence);
|
|
36
|
+
const option = spec.choice === undefined ? spec.option
|
|
37
|
+
: inCase(1, spec.choice.options[0].option) + inCase(2, spec.choice.options[1].option);
|
|
38
|
+
const confirms = spec.choice === undefined
|
|
39
|
+
? confirmButton(spec)
|
|
40
|
+
: inCase(1, confirmButton(spec.choice.options[0])) + inCase(2, confirmButton(spec.choice.options[1]));
|
|
7
41
|
return popup({
|
|
8
42
|
id: spec.id,
|
|
9
43
|
size: 'confirm',
|
|
10
44
|
labelledBy: title,
|
|
11
|
-
body: '<div class="cf">' +
|
|
45
|
+
body: '<div class="cf' + (spec.choice === undefined ? '' : ' cf-choose') + '">' +
|
|
12
46
|
(spec.glyph === '' ? '' : '<div class="cf-mark" aria-hidden="true">' + (spec.glyph ?? '⊘') + '</div>') +
|
|
13
47
|
'<h3 class="cf-title" id="' + title + '">' + spec.title + '</h3>' +
|
|
14
48
|
(spec.subject === '' ? '' : '<div class="cf-subject">' + spec.subject + '</div>') +
|
|
15
|
-
'<p class="cf-sentence">' +
|
|
49
|
+
'<p class="cf-sentence">' + sentence + '</p>' +
|
|
16
50
|
(spec.detail === undefined ? '' : '<div class="cf-detail">' + spec.detail + '</div>') +
|
|
17
|
-
(spec.
|
|
51
|
+
(spec.choice === undefined ? '' : choiceBlock(spec.id, spec.choice)) +
|
|
52
|
+
(option === '' ? '' : '<div class="cf-option">' + option + '</div>') +
|
|
18
53
|
(spec.note ?? '') +
|
|
19
54
|
'<div class="cf-actions">' +
|
|
20
55
|
pill({ label: spec.cancel, tone: 'outline', size: 'lg', button: true, attributes: CLOSES }) +
|
|
21
|
-
|
|
56
|
+
confirms +
|
|
22
57
|
'</div></div>',
|
|
23
58
|
});
|
|
24
59
|
}
|
|
@@ -33,4 +68,34 @@ export const CONFIRM_DIALOG_STYLE = String.raw `
|
|
|
33
68
|
.cf-detail ul{margin:0;padding-left:20px;display:grid;gap:8px}
|
|
34
69
|
.cf-actions{display:flex;justify-content:flex-end;gap:10px}
|
|
35
70
|
.cf-actions .pill-outline{font-weight:400}
|
|
71
|
+
/* A window that asks takes the next size up of the family (popup.ts): at the confirm's own 460px the two cards drew
|
|
72
|
+
four lines each and the sentence above them read as a wall (the maintainer, 2026-10-05). Narrower screens are
|
|
73
|
+
unchanged - the window is still the viewport less its margin there. */
|
|
74
|
+
dialog.pp-confirm:has(.cf-choose){max-width:560px}
|
|
75
|
+
.cf-choose{padding:30px 32px}
|
|
76
|
+
@media (max-width:640px){.cf-choose{padding:24px 20px}}
|
|
77
|
+
.cf-choose .cf-sentence{margin-bottom:20px}
|
|
78
|
+
.cf-case{display:none}
|
|
79
|
+
.cf:has(.cf-radio-1:checked) .cf-case-1{display:contents}
|
|
80
|
+
.cf:has(.cf-radio-2:checked) .cf-case-2{display:contents}
|
|
81
|
+
.cf-choice{display:grid;gap:8px;margin:0 0 22px}
|
|
82
|
+
.cf-choice-q{font-size:13px;font-weight:600;color:var(--text-3)}
|
|
83
|
+
.cf-opt{position:relative;display:grid;grid-template-columns:20px 26px minmax(0,1fr);gap:11px;align-items:start;padding:13px 15px;border-radius:12px;border:1px solid var(--white-09);background:var(--panel);cursor:pointer}
|
|
84
|
+
.cf-opt:hover{background:var(--raised)}
|
|
85
|
+
.cf-opt:has(.cf-radio:checked){background:var(--raised-2)}
|
|
86
|
+
.cf-opt-mint:has(.cf-radio:checked){border-color:var(--mint-50)}
|
|
87
|
+
.cf-opt-sand:has(.cf-radio:checked){border-color:var(--sand-35)}
|
|
88
|
+
.cf-opt:has(.cf-radio:focus-visible){outline:2px solid var(--coral);outline-offset:2px}
|
|
89
|
+
.cf-radio{position:absolute;opacity:0;pointer-events:none}
|
|
90
|
+
.cf-ring{flex:none;width:20px;height:20px;border-radius:50%;border:1.5px solid var(--white-30);display:flex;align-items:center;justify-content:center;margin-top:3px}
|
|
91
|
+
.cf-ring:after{content:"";width:10px;height:10px;border-radius:50%}
|
|
92
|
+
.cf-opt-mint .cf-radio:checked+.cf-ring{border-color:var(--mint)}.cf-opt-mint .cf-radio:checked+.cf-ring:after{background:var(--mint)}
|
|
93
|
+
.cf-opt-sand .cf-radio:checked+.cf-ring{border-color:var(--sand)}.cf-opt-sand .cf-radio:checked+.cf-ring:after{background:var(--sand)}
|
|
94
|
+
.cf-opt-grey .cf-radio:checked+.cf-ring{border-color:var(--text)}.cf-opt-grey .cf-radio:checked+.cf-ring:after{background:var(--text)}
|
|
95
|
+
.cf-opt-mark{flex:none;width:26px;height:26px;border-radius:50%;display:flex;align-items:center;justify-content:center;background:var(--white-06);color:var(--text-2)}
|
|
96
|
+
.cf-opt-mark svg{width:13px;height:13px}
|
|
97
|
+
.cf-opt-mint .cf-opt-mark{background:var(--mint-14);color:var(--mint)}
|
|
98
|
+
.cf-opt-sand .cf-opt-mark{background:var(--sand-16);color:var(--sand)}
|
|
99
|
+
.cf-opt-name{display:block;font-size:14.5px;font-weight:650;line-height:1.3;color:var(--text)}
|
|
100
|
+
.cf-opt-why{display:block;margin-top:4px;font-size:13px;line-height:1.45;color:var(--text-2)}
|
|
36
101
|
`;
|
|
@@ -21,4 +21,6 @@ export const FOLD_LINE_STYLE = String.raw `
|
|
|
21
21
|
.fold-hide{display:none}.fold[open] .fold-hide{display:inline}.fold[open] .fold-show{display:none}
|
|
22
22
|
.fold-sand>.fold-line{border:1px solid var(--sand-35);background:var(--card)}.fold-sand>.fold-line:hover{border-color:var(--sand)}
|
|
23
23
|
.fold-sand .fold-mark{width:32px;height:32px;background:var(--sand-16);color:var(--sand)}.fold-sand .fold-mark svg{width:16px;height:16px}
|
|
24
|
+
.fold-grey>.fold-line{border:1px solid var(--white-16);background:var(--card)}.fold-grey>.fold-line:hover{border-color:var(--white-32)}
|
|
25
|
+
.fold-grey .fold-mark{width:32px;height:32px;background:var(--white-10);color:var(--text-2);font-size:15px}
|
|
24
26
|
`;
|
|
@@ -28,7 +28,7 @@ dialog.pp-wide{isolation:isolate}
|
|
|
28
28
|
html:not(.js) dialog.pp-wide:target{position:relative}
|
|
29
29
|
dialog.pp::backdrop{background:var(--backdrop);backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px)}
|
|
30
30
|
dialog.pp-wizard{max-width:640px;margin-top:56px;max-height:calc(100vh - 112px)}
|
|
31
|
-
dialog.pp-list{max-width:
|
|
31
|
+
dialog.pp-list{max-width:920px;margin-top:56px;max-height:calc(100vh - 112px)}
|
|
32
32
|
dialog.pp-small,dialog.pp-pick,dialog.pp-confirm{border-color:var(--white-10);border-radius:20px;margin:auto}
|
|
33
33
|
dialog.pp-small{max-width:560px}dialog.pp-pick{max-width:520px}dialog.pp-confirm{max-width:460px}
|
|
34
34
|
html:not(.js) dialog.pp:target{display:block;position:static;margin:24px auto;max-height:none}
|