@agentwhy/cli 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/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/conversations-words.js +12 -0
- package/dist/report/render/ui/words/projects-words.js +9 -0
- package/dist/report/start/conversations/period-section.js +23 -8
- package/package.json +1 -1
|
@@ -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}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { escapeHtml as e } from '../html-report-components.js';
|
|
2
|
-
import { inLanguages, LANGS, translator } from '../report-copy.js';
|
|
2
|
+
import { inLanguages, labelAttributes, LANGS, translator } from '../report-copy.js';
|
|
3
3
|
import { initial } from './app-sidebar.js';
|
|
4
4
|
import { dayName, localClock } from './local-date.js';
|
|
5
5
|
import { tag } from './tag.js';
|
|
@@ -29,19 +29,20 @@ export function projectList(spec) {
|
|
|
29
29
|
return pickList(spec, spec.pick, when);
|
|
30
30
|
const action = spec.action;
|
|
31
31
|
const row = (one) => '<li class="pjl-row"' + searchable(one) + '>' + cells(one, when, action) + '</li>';
|
|
32
|
-
return '<div class="pjl">' +
|
|
32
|
+
return '<div class="pjl" data-order="new">' +
|
|
33
33
|
(here === undefined ? '' :
|
|
34
34
|
'<p class="pjl-label">' + inLanguages((t) => t('proj.here')) + '</p>' +
|
|
35
35
|
'<div class="pjl-here' + (here.setUp === true ? ' pjl-here-set' : '') + '">' + cells(here, when, action) + '</div>') +
|
|
36
|
-
'<div class="pjl-label-line"><p class="pjl-label">' +
|
|
37
|
-
(
|
|
36
|
+
'<div class="pjl-label-line"><p class="pjl-label">' +
|
|
37
|
+
['new', 'az', 'za'].map((order) => '<span class="pjl-order pjl-order-' + order + '">' + inLanguages((t) => t(order === 'new' ? 'proj.others' : 'proj.others.' + order)) + '</span>').join('') +
|
|
38
|
+
'</p>' + (others.length + gone.length === 0 ? '' : search()) + '</div>' +
|
|
38
39
|
'<p class="pjl-none" data-search-none hidden></p>' +
|
|
39
40
|
(shown.length === 0 && gone.length === 0
|
|
40
41
|
? '<p class="pjl-none">' + inLanguages((t) => t('proj.none')) + '</p>'
|
|
41
42
|
: '<div class="pjl-table">' +
|
|
42
43
|
(shown.length === 0 ? '' :
|
|
43
|
-
'<div class="pjl-heads" aria-hidden="true"
|
|
44
|
-
'<span>' + inLanguages((t) => t('proj.col.status')) + '</span><span></span></div>' +
|
|
44
|
+
'<div class="pjl-heads"><span aria-hidden="true"></span><span>' + sortByName() + '</span>' +
|
|
45
|
+
'<span aria-hidden="true">' + inLanguages((t) => t('proj.col.status')) + '</span><span aria-hidden="true"></span></div>' +
|
|
45
46
|
'<ul class="pjl-rows">' + shown.map(row).join('') + '</ul>') +
|
|
46
47
|
(gone.length === 0 ? '' :
|
|
47
48
|
'<details class="pjl-gone"><summary class="pjl-gone-line"><span>' + inLanguages((t) => t('proj.goneFold', { n: gone.length })) + '</span>' +
|
|
@@ -122,6 +123,18 @@ function search() {
|
|
|
122
123
|
return '<input type="search" class="pjl-search js-only" data-project-search autocomplete="off" spellcheck="false"' +
|
|
123
124
|
' aria-label="' + e(translator('en')('proj.search')) + '" data-search-words="' + e(JSON.stringify(words)) + '">';
|
|
124
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* A-Z, Z-A, or newest first (the maintainer, 2026-10-05): the head of the Project column is the button that cycles the
|
|
128
|
+
* three, with the order it is in drawn beside the word - up for A-Z, down for Z-A, both ways for the order the list
|
|
129
|
+
* came in. Only a script can sort, so only a page with one offers it, and a page without one keeps the plain word; the
|
|
130
|
+
* line above the table says the order in words, so what changed is never only a glyph.
|
|
131
|
+
*/
|
|
132
|
+
function sortByName() {
|
|
133
|
+
return '<button type="button" class="pjl-sort js-only" data-pjl-sort' + labelAttributes((t) => t('proj.sort')) + '>' +
|
|
134
|
+
inLanguages((t) => t('proj.col.project')) + '<span class="pjl-sort-mark" aria-hidden="true">' +
|
|
135
|
+
['new', 'az', 'za'].map((order, at) => '<span class="pjl-order pjl-order-' + order + '">' + ['\u2195', '\u2191', '\u2193'][at] + '</span>').join('') +
|
|
136
|
+
'</span></button><span class="nojs-only" aria-hidden="true">' + inLanguages((t) => t('proj.col.project')) + '</span>';
|
|
137
|
+
}
|
|
125
138
|
/**
|
|
126
139
|
* A row's radio, in the column where the window offers **Open**: a real one, so the keys move between the rows as they do
|
|
127
140
|
* in any group. What the page chooses by is the id, never a path (V17). The row chosen is mint, not coral (the
|
|
@@ -131,9 +144,9 @@ function radio(group, row) {
|
|
|
131
144
|
return '<input type="radio" class="pjl-radio-input" name="' + e(group) + '" value="' + e(row.id) + '" data-pick-name="' + e(row.name) + '"' +
|
|
132
145
|
(row.current ? ' data-pick-here checked' : '') + '><span class="pjl-radio" aria-hidden="true"><span class="pjl-radio-dot"></span></span>';
|
|
133
146
|
}
|
|
134
|
-
/** What the field matches a row by: its name and where it is, in lower case. */
|
|
147
|
+
/** What the field matches a row by: its name and where it is, in lower case - and the name alone, what it sorts by. */
|
|
135
148
|
function searchable(row) {
|
|
136
|
-
return ' data-search="' + e((row.name + ' ' + row.place).toLowerCase()) + '"';
|
|
149
|
+
return ' data-search="' + e((row.name + ' ' + row.place).toLowerCase()) + '" data-sort-name="' + e(row.name.toLowerCase()) + '"';
|
|
137
150
|
}
|
|
138
151
|
/** A row's four cells: the folder's initial, the project, its status, and what it offers. */
|
|
139
152
|
function cells(row, when, action) {
|
|
@@ -166,8 +179,15 @@ export const PROJECT_LIST_STYLE = String.raw `
|
|
|
166
179
|
.pjl{--pjl-columns:44px minmax(0,1fr) 172px 128px}
|
|
167
180
|
.pjl-label{margin:22px 4px 10px;font-size:13.5px;font-weight:600;color:var(--text-3)}
|
|
168
181
|
.pjl-label:first-child{margin-top:4px}
|
|
169
|
-
.pjl-label-line{display:flex;align-items:flex-end;justify-content:space-between;gap:12px}
|
|
170
|
-
.pjl-label-line .pjl-label{margin-top:
|
|
182
|
+
.pjl-label-line{display:flex;align-items:flex-end;justify-content:space-between;gap:12px;margin-top:34px}
|
|
183
|
+
.pjl-label-line .pjl-label{margin-top:0}
|
|
184
|
+
.pjl-order{display:none}
|
|
185
|
+
.pjl[data-order="new"] .pjl-order-new,.pjl[data-order="az"] .pjl-order-az,.pjl[data-order="za"] .pjl-order-za{display:inline}
|
|
186
|
+
.pjl-sort{display:inline-flex;align-items:center;gap:7px;margin:-4px 0;padding:4px 8px;border:0;border-radius:8px;background:none;font:inherit;font-size:12.5px;font-weight:600;color:var(--text-3);cursor:pointer}
|
|
187
|
+
.pjl-sort:hover{color:var(--text);background:var(--white-05)}
|
|
188
|
+
.pjl-sort:focus-visible{outline:2px solid var(--coral);outline-offset:1px}
|
|
189
|
+
.pjl-sort-mark{font-size:12px;line-height:1;opacity:.75}
|
|
190
|
+
.pjl-sort:hover .pjl-sort-mark{opacity:1}
|
|
171
191
|
.pjl-search{width:200px;margin:0 0 8px;padding:7px 12px;border-radius:999px;border:1px solid var(--white-14);background:var(--card);color:var(--text);font:inherit;font-size:13.5px}
|
|
172
192
|
.pjl-search::placeholder{color:var(--text-3)}
|
|
173
193
|
.pjl-search:focus{outline:none;border-color:var(--white-32)}
|
|
@@ -248,6 +268,25 @@ export const PROJECT_LIST_SCRIPT = String.raw `
|
|
|
248
268
|
const list = button.closest('.pjl');
|
|
249
269
|
if (list) list.classList.remove('pjl-folded');
|
|
250
270
|
}));
|
|
271
|
+
// A-Z, Z-A, newest first: the order the rows came in is kept, so the third press is not a guess at it.
|
|
272
|
+
document.querySelectorAll('[data-pjl-sort]').forEach((button) => {
|
|
273
|
+
const list = button.closest('.pjl');
|
|
274
|
+
if (!list) return;
|
|
275
|
+
const after = { new: 'az', az: 'za', za: 'new' };
|
|
276
|
+
const groups = [...list.querySelectorAll('.pjl-table .pjl-rows')].map((rows) => ({ rows, came: [...rows.children] }));
|
|
277
|
+
button.addEventListener('click', () => {
|
|
278
|
+
const order = after[list.dataset.order] || 'az';
|
|
279
|
+
list.dataset.order = order;
|
|
280
|
+
groups.forEach(({ rows, came }) => {
|
|
281
|
+
const put = came.slice();
|
|
282
|
+
if (order !== 'new') {
|
|
283
|
+
const way = order === 'az' ? 1 : -1;
|
|
284
|
+
put.sort((one, two) => way * String(one.dataset.sortName || '').localeCompare(String(two.dataset.sortName || '')));
|
|
285
|
+
}
|
|
286
|
+
put.forEach((row) => rows.appendChild(row));
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
});
|
|
251
290
|
document.querySelectorAll('[data-project-search]').forEach((input) => {
|
|
252
291
|
const list = input.closest('.pjl');
|
|
253
292
|
if (!list) return;
|
|
@@ -68,6 +68,10 @@ export const CONVERSATIONS_WORDS = {
|
|
|
68
68
|
'conv.unchecked.partial.title.other': '{n} conversations couldn’t be fully checked.',
|
|
69
69
|
'conv.unchecked.partial': 'Their record leaves out some steps, so their reports may not show everything your AI opened. Open one to see what was written down.',
|
|
70
70
|
'conv.unchecked.mixed': 'Some are older than this check, or their record couldn’t be opened. The others leave out some steps, so their reports may not show everything. To include older ones, run {command}',
|
|
71
|
+
'conv.unchecked.lead.rest': '— older than this check, or their record couldn’t be read.',
|
|
72
|
+
'conv.unchecked.check.rest': '— not read yet.',
|
|
73
|
+
'conv.unchecked.partial.rest': '— read, but their record has gaps.',
|
|
74
|
+
'conv.unchecked.mixed.rest': '— some older than this check, the rest read with gaps.',
|
|
71
75
|
'conv.act.fix': 'Fix it →',
|
|
72
76
|
'conv.act.see': 'See report',
|
|
73
77
|
'conv.act.none': 'No report',
|
|
@@ -226,6 +230,10 @@ export const CONVERSATIONS_WORDS = {
|
|
|
226
230
|
'conv.unchecked.partial.title.many': '{n} rozmów nie zostało w pełni sprawdzonych.',
|
|
227
231
|
'conv.unchecked.partial': 'Ich zapis pomija niektóre kroki, więc raporty mogą nie pokazywać wszystkiego, co otworzyło twoje AI. Otwórz którąś, żeby zobaczyć, co zostało zapisane.',
|
|
228
232
|
'conv.unchecked.mixed': 'Część jest starsza niż to sprawdzenie albo nie dało się otworzyć ich zapisu. Zapis pozostałych pomija niektóre kroki, więc raporty mogą nie pokazywać wszystkiego. Żeby dołączyć starsze, uruchom {command}',
|
|
233
|
+
'conv.unchecked.lead.rest': '— starsze niż to sprawdzenie albo nie dało się odczytać ich zapisu.',
|
|
234
|
+
'conv.unchecked.check.rest': '— jeszcze nieprzeczytane.',
|
|
235
|
+
'conv.unchecked.partial.rest': '— przeczytane, ale ich zapis ma luki.',
|
|
236
|
+
'conv.unchecked.mixed.rest': '— część starsza niż to sprawdzenie, reszta przeczytana z lukami.',
|
|
229
237
|
'conv.act.fix': 'Napraw →',
|
|
230
238
|
'conv.act.see': 'Zobacz raport',
|
|
231
239
|
'conv.act.none': 'Brak raportu',
|
|
@@ -376,6 +384,10 @@ export const CONVERSATIONS_WORDS = {
|
|
|
376
384
|
'conv.unchecked.partial.title.other': '{n} Unterhaltungen konnten nicht vollständig geprüft werden.',
|
|
377
385
|
'conv.unchecked.partial': 'Ihre Aufzeichnung lässt einige Schritte aus, daher zeigen ihre Berichte vielleicht nicht alles, was deine KI geöffnet hat. Öffne eine, um zu sehen, was aufgezeichnet wurde.',
|
|
378
386
|
'conv.unchecked.mixed': 'Einige sind älter als diese Prüfung, oder ihre Aufzeichnung ließ sich nicht öffnen. Die übrigen lassen einige Schritte aus, daher zeigen ihre Berichte vielleicht nicht alles. Um ältere einzubeziehen, führe {command} aus',
|
|
387
|
+
'conv.unchecked.lead.rest': '— älter als diese Prüfung, oder ihre Aufzeichnung war nicht lesbar.',
|
|
388
|
+
'conv.unchecked.check.rest': '— noch nicht gelesen.',
|
|
389
|
+
'conv.unchecked.partial.rest': '— gelesen, aber ihre Aufzeichnung hat Lücken.',
|
|
390
|
+
'conv.unchecked.mixed.rest': '— einige älter als diese Prüfung, die übrigen mit Lücken gelesen.',
|
|
379
391
|
'conv.act.fix': 'Beheben →',
|
|
380
392
|
'conv.act.see': 'Bericht ansehen',
|
|
381
393
|
'conv.act.none': 'Kein Bericht',
|
|
@@ -9,6 +9,9 @@ export const PROJECTS_WORDS = {
|
|
|
9
9
|
'proj.lead': 'Pick the project you want agentwhy to watch.',
|
|
10
10
|
'proj.here': 'You’re here now',
|
|
11
11
|
'proj.others': 'Other projects · newest first',
|
|
12
|
+
'proj.others.az': 'Other projects · A–Z',
|
|
13
|
+
'proj.others.za': 'Other projects · Z–A',
|
|
14
|
+
'proj.sort': 'Sort projects by name',
|
|
12
15
|
'proj.yours': 'Your projects · newest first',
|
|
13
16
|
'moved.pageTitle': 'Another project · agentwhy',
|
|
14
17
|
'moved.title': 'This page was for {was}.',
|
|
@@ -76,6 +79,9 @@ export const PROJECTS_WORDS = {
|
|
|
76
79
|
'proj.lead': 'Wybierz projekt, który agentwhy ma obserwować.',
|
|
77
80
|
'proj.here': 'Jesteś tutaj',
|
|
78
81
|
'proj.others': 'Inne projekty · od najnowszych',
|
|
82
|
+
'proj.others.az': 'Inne projekty · A–Z',
|
|
83
|
+
'proj.others.za': 'Inne projekty · Z–A',
|
|
84
|
+
'proj.sort': 'Sortuj projekty po nazwie',
|
|
79
85
|
'proj.yours': 'Twoje projekty · od najnowszych',
|
|
80
86
|
'moved.pageTitle': 'Inny projekt · agentwhy',
|
|
81
87
|
'moved.title': 'Ta strona była dla projektu {was}.',
|
|
@@ -152,6 +158,9 @@ export const PROJECTS_WORDS = {
|
|
|
152
158
|
'proj.lead': 'Wähle das Projekt, das agentwhy beobachten soll.',
|
|
153
159
|
'proj.here': 'Hier bist du gerade',
|
|
154
160
|
'proj.others': 'Andere Projekte · die neuesten zuerst',
|
|
161
|
+
'proj.others.az': 'Andere Projekte · A–Z',
|
|
162
|
+
'proj.others.za': 'Andere Projekte · Z–A',
|
|
163
|
+
'proj.sort': 'Projekte nach Name sortieren',
|
|
155
164
|
'proj.yours': 'Deine Projekte · die neuesten zuerst',
|
|
156
165
|
'moved.pageTitle': 'Anderes Projekt · agentwhy',
|
|
157
166
|
'moved.title': 'Diese Seite war für {was}.',
|
|
@@ -78,12 +78,7 @@ export function periodSection(periods, at, index, spec) {
|
|
|
78
78
|
dataTable({ ...CONVERSATION_TABLE, rows: fix.map((item) => conversationRow(item, index.widen, !index.shared)) }) +
|
|
79
79
|
'</section>') +
|
|
80
80
|
(!spec.lists || info.length === 0 ? '' : forYourInfo(info, index)) +
|
|
81
|
-
(!spec.lists || unchecked.length === 0 ? '' :
|
|
82
|
-
'<section class="cw-need cw-unchecked" data-need><div class="cw-need-head"><span class="cw-need-dot" aria-hidden="true"></span>' +
|
|
83
|
-
'<h2 class="cw-h2">' + inLanguages((t) => t(partial ? 'look.unchecked' : 'conv.unchecked')) + '</h2><span class="cw-need-count" data-need-count>' + unchecked.length + '</span></div>' +
|
|
84
|
-
'<p class="cw-unchecked-lead">' + inLanguages((t) => t(lead, { command: '<code>' + e(index.widen) + '</code>' })) + '</p>' +
|
|
85
|
-
dataTable({ ...CONVERSATION_TABLE, rows: unchecked.map((item) => conversationRow(item, index.widen, !index.shared)) }) +
|
|
86
|
-
'</section>') +
|
|
81
|
+
(!spec.lists || unchecked.length === 0 ? '' : notFullyChecked(unchecked, partial, lead, index)) +
|
|
87
82
|
(!spec.lists || others.length === 0 ? '' : theRest(others, need.length === 0, index)) +
|
|
88
83
|
'</section>';
|
|
89
84
|
}
|
|
@@ -101,6 +96,27 @@ function forYourInfo(info, index) {
|
|
|
101
96
|
body: dataTable({ ...CONVERSATION_TABLE, rows: info.map((item) => conversationRow(item, index.widen, !index.shared)) }),
|
|
102
97
|
}) + '</section>';
|
|
103
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* "Couldn't check fully (n)" (the maintainer, 2026-10-05): what this run could not read, or read with gaps, in a fold
|
|
101
|
+
* of its own - a solid grey line with the look's ?, shut, and opened with **Show**. Every Codex record has a gap
|
|
102
|
+
* (`2026-09-27-what-codex-wrote.md` X23), so on a project worked in with Codex this list holds the whole week, asks
|
|
103
|
+
* for nothing, and was the longest thing on the page; the maintainer asked for it shut on arrival (2026-10-05), not
|
|
104
|
+
* only shuttable. Shut it still says all of it the page owes: the heading, how many, and in brief why - and the
|
|
105
|
+
* period's card above says the number too. The sentence of the reason and the command that would include them go
|
|
106
|
+
* inside, with the rows; the list is still apart from what is to fix and is never a group of the rest's table
|
|
107
|
+
* (F17, F13, O4). The script narrows it to the day chosen as it does the other lists (`data-need`), and the count it
|
|
108
|
+
* rewrites is the one in the line, which is in view whether the fold is open or not.
|
|
109
|
+
*/
|
|
110
|
+
function notFullyChecked(unchecked, partial, lead, index) {
|
|
111
|
+
return '<section class="cw-need cw-unchecked" data-need>' + foldLine({
|
|
112
|
+
summary: '<strong>' + inLanguages((t) => t(partial ? 'look.unchecked' : 'conv.unchecked')) +
|
|
113
|
+
' (<span data-need-count>' + unchecked.length + '</span>)</strong> <span class="fold-rest">' +
|
|
114
|
+
inLanguages((t) => t(lead + '.rest')) + '</span>',
|
|
115
|
+
mark: { glyph: LOOKS.unchecked.glyph, tone: 'grey' },
|
|
116
|
+
body: '<p class="cw-unchecked-lead">' + inLanguages((t) => t(lead, { command: '<code>' + e(index.widen) + '</code>' })) + '</p>' +
|
|
117
|
+
dataTable({ ...CONVERSATION_TABLE, rows: unchecked.map((item) => conversationRow(item, index.widen, !index.shared)) }),
|
|
118
|
+
}) + '</section>';
|
|
119
|
+
}
|
|
104
120
|
/** The looks of the rest, in the order of F16's ladder: the groups of F13 and F14, and the order they are listed in. */
|
|
105
121
|
const REST = ['fixed', 'name', 'stopped', 'none'];
|
|
106
122
|
/** A group's table rows past this many wait behind "Show all" while the whole period is shown (F14). */
|
|
@@ -237,8 +253,7 @@ export const PERIOD_SECTION_STYLE = String.raw `
|
|
|
237
253
|
.cw-need-head{display:flex;align-items:baseline;gap:10px;margin-bottom:12px}
|
|
238
254
|
.cw-need-dot{width:10px;height:10px;border-radius:50%;background:var(--coral);align-self:center}
|
|
239
255
|
.cw-need-count{font-size:15px;font-weight:650;color:var(--coral-text)}
|
|
240
|
-
.cw-unchecked
|
|
241
|
-
.cw-unchecked-lead{margin:-4px 0 12px;font-size:14px;line-height:1.5;color:var(--text-2)}
|
|
256
|
+
.cw-unchecked-lead{margin:0 0 12px;font-size:14px;line-height:1.5;color:var(--text-2)}
|
|
242
257
|
.cw-unchecked-lead code,.guide code{font-family:var(--mono);font-size:13px;color:var(--text);background:var(--white-07);border-radius:6px;padding:1px 6px}
|
|
243
258
|
.cw-tools{display:none;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:16px}
|
|
244
259
|
.js .cw-tools{display:flex}
|