@vernikr/size-report 2.4.0 → 2.6.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/README.md +989 -1081
- package/bin/postinstall.js +17 -18
- package/bin/size.js +2 -2
- package/package.json +3 -4
- package/src/args.js +72 -72
- package/src/artifact.js +14 -14
- package/src/check.js +41 -42
- package/src/cli.js +26 -29
- package/src/config.js +87 -91
- package/src/css.js +14 -14
- package/src/data.js +26 -50
- package/src/derived.js +31 -35
- package/src/doctor.js +95 -99
- package/src/explain.js +46 -47
- package/src/git.js +66 -71
- package/src/history.js +74 -83
- package/src/hook.js +130 -149
- package/src/init.js +37 -37
- package/src/journal.js +17 -15
- package/src/locales.js +31 -22
- package/src/metrics.js +72 -89
- package/src/minify.js +28 -27
- package/src/modes.js +57 -60
- package/src/optional.js +13 -11
- package/src/page/app.css +76 -94
- package/src/page/app.js +124 -80
- package/src/page/build.js +193 -50
- package/src/page/dom.js +8 -9
- package/src/page/panel.js +157 -69
- package/src/page/payload.js +168 -0
- package/src/page/state.js +144 -104
- package/src/page/table.js +270 -86
- package/src/parse-worker.js +10 -10
- package/src/parse.js +43 -45
- package/src/project.js +100 -104
- package/src/refusal.js +75 -76
- package/src/size-table.js +41 -76
- package/src/strip/forms.js +5 -5
- package/src/strip/guard.js +28 -28
- package/src/strip/js.js +27 -27
- package/src/strip.js +17 -21
- package/src/table.css +54 -19
- package/src/tokens.js +27 -27
- package/src/tool.js +10 -11
- package/templates/README.md +71 -77
- package/templates/ci.yml +33 -33
- package/templates/size-report.config.json +3 -3
- package/CHANGELOG.md +0 -690
package/src/page/app.css
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
/*
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* внешней ссылки в ней быть не может: только системные семейства шрифтов
|
|
7
|
-
* (`ui-sans-serif`) и системные цвета (`Canvas`, `CanvasText`, `AccentColor`),
|
|
8
|
-
* которые есть в любой теме.
|
|
1
|
+
/* The report page's styling — what stands on top of the shared table (`src/table.css`): the canvas and the typography,
|
|
2
|
+
* the panel of choices, the empty states and the adaptation to a narrow window. Numbers and the colour of a delta are
|
|
3
|
+
* absent here on purpose: the shared part sets them, and the package holds one set of styles rather than two. The page
|
|
4
|
+
* opens from disk, without a server and without a network, so it can hold no external reference at all: only system font
|
|
5
|
+
* families (`ui-sans-serif`) and system colours (`Canvas`, `CanvasText`, `AccentColor`), which every theme has.
|
|
9
6
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* весь экран. */
|
|
7
|
+
* The shared part is frozen by the artifact's bytes (`src/css.js`), which is why everything where the page departs from
|
|
8
|
+
* its geometry is gathered in the "adaptations" section together with its reason: the shared part cannot grow, while on a
|
|
9
|
+
* narrow screen the commit column would eat the whole screen. */
|
|
14
10
|
|
|
15
11
|
:root {
|
|
16
12
|
color-scheme: light dark;
|
|
@@ -22,8 +18,8 @@
|
|
|
22
18
|
--tint: rgba(127, 127, 127, .07);
|
|
23
19
|
}
|
|
24
20
|
|
|
25
|
-
/*
|
|
26
|
-
*
|
|
21
|
+
/* Typography and rhythm: one step between blocks (--gap), a large heading, a muted note — so that the eye reaches the
|
|
22
|
+
* numbers rather than the service text. */
|
|
27
23
|
body {
|
|
28
24
|
margin: 0;
|
|
29
25
|
padding: var(--pad) var(--pad) 48px;
|
|
@@ -35,8 +31,8 @@ body {
|
|
|
35
31
|
h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em; }
|
|
36
32
|
.sub { margin: 0 0 calc(var(--gap) + 4px); color: var(--muted); font-size: 12.5px; }
|
|
37
33
|
|
|
38
|
-
/*
|
|
39
|
-
*
|
|
34
|
+
/* The panel of choices is a card: it separates the controls from the data and does not merge with the table that begins
|
|
35
|
+
* below. */
|
|
40
36
|
.panel {
|
|
41
37
|
margin: 0 0 var(--gap);
|
|
42
38
|
padding: 12px 14px 13px;
|
|
@@ -56,20 +52,18 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
56
52
|
}
|
|
57
53
|
.panel .cap { display: block; margin-bottom: 4px; }
|
|
58
54
|
.panel .row { display: flex; flex-wrap: wrap; gap: 3px 6px; align-items: center; }
|
|
59
|
-
/*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
55
|
+
/* The way of counting is visible text under the switches: the token dictionary and the way of compression come from the
|
|
56
|
+
* settings of the run rather than from a checkbox, so pointing a mouse is not enough — the reader has to see what produced
|
|
57
|
+
* the number. */
|
|
62
58
|
.panel .about { margin: 5px 0 0; color: var(--muted); font-size: 11.5px; }
|
|
63
59
|
|
|
64
|
-
/*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
* прокручивается сама: иначе управление вытолкнуло бы таблицу за экран.
|
|
60
|
+
/* The file tree: nesting is shown by an indent and a level line, and a folder is a switch like a file — only its checkbox
|
|
61
|
+
* answers for the whole subtree, while the number beside it says for how many files. The list is longer than the window,
|
|
62
|
+
* so the panel scrolls itself: otherwise the controls would push the table off the screen.
|
|
68
63
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* подписей в списке много и они короткие, а рядом с ними стоит таблица. */
|
|
64
|
+
* On a narrow screen the list itself scrolls (the panel grows with the page there), on a wide one the whole panel does
|
|
65
|
+
* (below): there is one scroll, and it belongs to whoever is really bounded by the window. Files use the same font as the
|
|
66
|
+
* table's numbers: the list holds many short captions, and the table stands right next to it. */
|
|
73
67
|
.panel .files { max-height: min(30vh, 320px); overflow: auto; font-size: 12.5px; }
|
|
74
68
|
.panel .tree { margin: 0; padding: 0 0 0 14px; list-style: none; }
|
|
75
69
|
.panel .tree .tree { margin-left: 14px; padding-left: 9px; border-left: 1px solid var(--line); }
|
|
@@ -77,13 +71,12 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
77
71
|
.box.dir { font-weight: 600; }
|
|
78
72
|
.box .n { margin-left: 1px; color: var(--muted); font-size: 11px; }
|
|
79
73
|
|
|
80
|
-
/*
|
|
81
|
-
*
|
|
82
|
-
* иначе каждый клик считал бы таблицу заново.
|
|
74
|
+
/* A folded folder (a class on the row, set by a click on the sign): the subtree lies in the markup and is simply not shown.
|
|
75
|
+
* That way folding rebuilds nothing — otherwise every click would count the table anew.
|
|
83
76
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
77
|
+
* The folding sign: every row has the room for it (the row's indent) while only folders carry one, which lines the leaves
|
|
78
|
+
* and the folders up in a single column. It is not part of the checkbox: the checkbox answers for the numbers, the sign for
|
|
79
|
+
* how much of the tree is visible. */
|
|
87
80
|
.panel .tree li.folded > .tree { display: none; }
|
|
88
81
|
.panel .tree li { position: relative; }
|
|
89
82
|
.panel .tree li > .fold {
|
|
@@ -97,9 +90,8 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
97
90
|
}
|
|
98
91
|
.panel .tree li > .fold:hover { color: var(--ink); }
|
|
99
92
|
|
|
100
|
-
/*
|
|
101
|
-
*
|
|
102
|
-
* технологии. */
|
|
93
|
+
/* A switch is a label around an input: one label and one click target, which is why a mouse, the keyboard (`Space` on the
|
|
94
|
+
* input) and assistive technology all reach it. */
|
|
103
95
|
.box {
|
|
104
96
|
display: inline-flex;
|
|
105
97
|
gap: 6px;
|
|
@@ -111,25 +103,20 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
111
103
|
.box:hover { background: var(--tint); }
|
|
112
104
|
.box input { margin: 0; accent-color: AccentColor; }
|
|
113
105
|
.box.all { font-weight: 600; }
|
|
114
|
-
/*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
106
|
+
/* A file or folder outside the report: the checkbox is there but off and unavailable — no numbers were measured for it, so
|
|
107
|
+
* there is nothing to switch. The row is muted and does not respond to the mouse: there is nothing to promise a click
|
|
108
|
+
* with. */
|
|
117
109
|
.box.plain { cursor: default; opacity: .55; }
|
|
118
110
|
.box.plain:hover { background: none; }
|
|
119
111
|
.box.plain input { cursor: default; }
|
|
120
112
|
|
|
121
|
-
/*
|
|
122
|
-
*
|
|
123
|
-
*
|
|
124
|
-
* «файла ещё нет» — в подсказке клетки.
|
|
125
|
-
*
|
|
126
|
-
* Приближённое число помечено пунктиром, а не цветом: цвет в таблице занят дельтой
|
|
127
|
-
* (рост и спад), и второй смысл на том же признаке читался бы как первый. */
|
|
128
|
-
#grid td.approx { text-decoration: underline dotted; text-underline-offset: 2.5px; }
|
|
113
|
+
/* There is no legend under the file tree, and on purpose: below the list it pushed the numbers away, while its content
|
|
114
|
+
* already stands next to what it explains — the colour of a delta is named by the sign of the number itself, the way each
|
|
115
|
+
* number was counted stands under the metric switches, and the mark of a gap lives in the cell's text. */
|
|
129
116
|
|
|
130
|
-
/*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
117
|
+
/* The table has a frame and a scroll of its own: the header and the commit column stick to it (the rules of stickiness
|
|
118
|
+
* live in the shared part) rather than to the page, so scrolling sideways shows whose row it is while scrolling down shows
|
|
119
|
+
* which column it is. */
|
|
133
120
|
.shell {
|
|
134
121
|
overflow: auto;
|
|
135
122
|
max-height: calc(100vh - 300px);
|
|
@@ -137,12 +124,12 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
137
124
|
border-radius: var(--radius);
|
|
138
125
|
background: Canvas;
|
|
139
126
|
}
|
|
140
|
-
/*
|
|
127
|
+
/* Numbers are denser than the page's text: there are more of them, they are shorter, and they are read by their digits. */
|
|
141
128
|
#grid { font-size: 12.5px; }
|
|
142
129
|
|
|
143
|
-
/*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
130
|
+
/* The message about a link that came in stands above the table, so that it cannot be missed, while it does not push the
|
|
131
|
+
* table away: one line in the place of the page. Its colour is the system one (the accent), for the page keeps no colours
|
|
132
|
+
* of its own. */
|
|
146
133
|
.notice {
|
|
147
134
|
margin: 0 0 var(--gap);
|
|
148
135
|
padding: 9px 13px;
|
|
@@ -153,8 +140,8 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
153
140
|
}
|
|
154
141
|
.notice[hidden] { display: none; }
|
|
155
142
|
|
|
156
|
-
/*
|
|
157
|
-
*
|
|
143
|
+
/* The empty states: when there is nothing to assemble a table from, the page says so in words rather than showing an empty
|
|
144
|
+
* grid. */
|
|
158
145
|
.state {
|
|
159
146
|
margin: var(--gap) 0 0;
|
|
160
147
|
padding: 11px 13px;
|
|
@@ -168,28 +155,24 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
168
155
|
.note { margin: 14px 0 0; max-width: 90em; color: var(--muted); font-size: 12px; }
|
|
169
156
|
.note code { background: var(--tint); padding: 0 3px; border-radius: 3px; }
|
|
170
157
|
|
|
171
|
-
/*
|
|
172
|
-
*
|
|
158
|
+
/* The keyboard: the focus ring is visible on any background (the system accent colour) and shifts no layout. Journal links
|
|
159
|
+
* and every switch are reachable with Tab. */
|
|
173
160
|
:focus-visible { outline: 2px solid AccentColor; outline-offset: 2px; border-radius: 3px; }
|
|
174
161
|
|
|
175
|
-
/*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
* управление за экран. Узкое окно эту же раскладку снимает (ниже): там столбцы
|
|
180
|
-
* снова идут друг под другом, потому что рядом им не хватает ширины.
|
|
162
|
+
/* A wide page: the panel of choices (metrics, file tree) stands **left** of the table, and the whole page fits the window.
|
|
163
|
+
* That is not decoration: a desktop has much side room and little vertical room — the switches and the numbers are visible
|
|
164
|
+
* at once, and neither scrolling the numbers nor scrolling the file list takes the controls off the screen. A narrow window
|
|
165
|
+
* drops this layout (below): there the columns run one under another again, because side by side they lack the width.
|
|
181
166
|
*
|
|
182
|
-
*
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* получилось» (тогда она вытолкнула бы таблицу за экран), а та же рабочая строка,
|
|
189
|
-
* внутри которой она прокручивается: список файлов длиннее окна — обычное дело.
|
|
167
|
+
* The layout is a grid on `body` rather than a wrapper in the markup: the page is assembled by pasting chapters
|
|
168
|
+
* (`src/page/build.js`), and adding nodes to it for the sake of styling would mean changing the page's shape in two places
|
|
169
|
+
* instead of one. There are five rows, named by subject: the heading, the message about a link, the **working row**, the
|
|
170
|
+
* empty state and the note. Only the working row stretches — the table gets all the remaining height and the panel no more
|
|
171
|
+
* than that; the panel's own height is not "whatever came out" (it would push the table off the screen) but that same
|
|
172
|
+
* working row, inside which it scrolls: a file list longer than the window is the usual case.
|
|
190
173
|
*
|
|
191
|
-
*
|
|
192
|
-
*
|
|
174
|
+
* The threshold of 900px is the same as the adaptations' below: one border between "wide" and "narrow", or the page would
|
|
175
|
+
* be left with no rule at all between two thresholds. */
|
|
193
176
|
@media (min-width: 900px) {
|
|
194
177
|
body {
|
|
195
178
|
box-sizing: border-box;
|
|
@@ -208,22 +191,20 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
208
191
|
#shell { grid-area: 3 / 2 / 4 / 3; }
|
|
209
192
|
#state { grid-area: 4 / 2 / 5 / 3; align-self: start; }
|
|
210
193
|
#note { grid-area: 5 / 2 / 6 / 3; }
|
|
211
|
-
/*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
* поля, и уезжает вместе с ним. */
|
|
194
|
+
/* The working row runs the full height, as the table does: the panel ends where it ends. The panel scrolls rather than the
|
|
195
|
+
* page. It has no top padding here: the list drives under it, and the sticky row of categories would stand not flush with
|
|
196
|
+
* the edge but under a band of passing files. The padding has not gone anywhere — it is on the first field and travels
|
|
197
|
+
* away with it. */
|
|
216
198
|
#panel { grid-area: 2 / 1 / -1 / 2; min-height: 0; margin-bottom: 0; padding-top: 0; overflow: auto; }
|
|
217
199
|
.panel > fieldset:first-child { padding-top: 12px; }
|
|
218
|
-
/*
|
|
219
|
-
*
|
|
200
|
+
/* Here the row rather than the window sets the table's height: it needs a height rule of its own only in a narrow window,
|
|
201
|
+
* where the whole page scrolls. */
|
|
220
202
|
.shell { max-height: none; }
|
|
221
|
-
/*
|
|
222
|
-
*
|
|
203
|
+
/* The panel has a single scroll: the file list starts none of its own, and the "Files" field no longer cuts the tree with
|
|
204
|
+
* its ceiling. */
|
|
223
205
|
.panel .files { max-height: none; overflow: visible; }
|
|
224
|
-
/*
|
|
225
|
-
*
|
|
226
|
-
* проезжающие строки списка. */
|
|
206
|
+
/* The category switches stay in sight while the tree is scrolled. The background is the panel's own (the surface plus its
|
|
207
|
+
* tint), or the passing rows of the list would read through them. */
|
|
227
208
|
.panel .cats {
|
|
228
209
|
position: sticky;
|
|
229
210
|
top: 0;
|
|
@@ -231,20 +212,21 @@ h1 { margin: 0 0 3px; font-size: 21px; font-weight: 650; letter-spacing: -.012em
|
|
|
231
212
|
background-color: Canvas;
|
|
232
213
|
background-image: linear-gradient(var(--tint), var(--tint));
|
|
233
214
|
}
|
|
234
|
-
/*
|
|
235
|
-
*
|
|
215
|
+
/* Without metrics there is no table, and a stretched empty row has no place there: the empty state takes the free space of
|
|
216
|
+
* that row rather than the band above it. */
|
|
236
217
|
body:has(#shell[hidden]) #state { grid-area: 3 / 2 / 4 / 3; }
|
|
237
218
|
}
|
|
238
219
|
|
|
239
|
-
/*
|
|
240
|
-
*
|
|
241
|
-
*
|
|
242
|
-
*
|
|
243
|
-
* под ней на одном единственном размере окна. */
|
|
220
|
+
/* Adaptations: the one place where the page overrides the shared geometry — because the shared part is frozen by the
|
|
221
|
+
* artifact's bytes, not because it is handier that way. The threshold is 899px rather than 900: at exactly 900px both halves
|
|
222
|
+
* would apply to one page, and the table's height ceiling would survive from the "narrow" into the "wide" one — that is,
|
|
223
|
+
* empty space under it at that single window size. */
|
|
244
224
|
@media (max-width: 899px) {
|
|
245
225
|
body { padding: 14px 14px 32px; }
|
|
246
226
|
h1 { font-size: 18px; }
|
|
247
227
|
#grid { font-size: 12px; }
|
|
248
228
|
.shell { max-height: calc(100vh - 260px); }
|
|
249
|
-
|
|
229
|
+
/* The commit column needs no rule of its own here any more: its width is the measure `--clip` of the shared part
|
|
230
|
+
* (`src/table.css`), and on a narrow screen that measure stands as it is. A shorter one would be `--clip: 150px`
|
|
231
|
+
* here — the adaptation the shared part cannot make for itself. */
|
|
250
232
|
}
|
package/src/page/app.js
CHANGED
|
@@ -1,106 +1,150 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { appUnpack } from './payload.js';
|
|
2
|
+
import { appAddressDrop, appApply, appBoot, appData, appFoldRead, appLinkUse, appNotice, appRead, appUi, appView, appWrite } from './state.js';
|
|
3
|
+
import { appColumn, appContribute, appMetrics, appState, appTable, appTotals, appTotalsReset } from './table.js';
|
|
4
|
+
import { appPanel, appPanelAll, appPanelState } from './panel.js';
|
|
4
5
|
|
|
5
|
-
/*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
/* Колонки, которых коснулся последний коммит, идут впереди: отчёт пересобирается
|
|
15
|
-
* после каждого коммита, и первый вопрос читателя — что принесла эта правка. Внутри
|
|
16
|
-
* каждой части порядок прежний, из настроек: `sort` устойчив, а порядок колонок — это
|
|
17
|
-
* то, к чему читатель привык. Знак берётся из истории (её знает движок), а не из
|
|
18
|
-
* чисел: правка без изменения размера тоже правка. */
|
|
19
|
-
files.sort((a, b) => (appData.last[a] === true ? 0 : 1) - (appData.last[b] === true ? 0 : 1));
|
|
6
|
+
/* Assembling the report: the table is built once (`appTable` of the table chapter) and everything afterwards only
|
|
7
|
+
* shows, hides and recounts. A click on any switch therefore costs a class, a number and the fields it reached — the
|
|
8
|
+
* whole table used to be destroyed and built again, which was 81 % of the cost of a click and produced a hundred
|
|
9
|
+
* thousand dead nodes for the collector to walk.
|
|
10
|
+
*
|
|
11
|
+
* Hence two paths and no third: `appPaint` draws the whole view (the first drawing, a record from the browser's
|
|
12
|
+
* memory, a link in the address), while `appSwitch`, `appSwitchGroup` and `appSwitchMetric` are what one click on a
|
|
13
|
+
* box does. Neither makes a node.
|
|
14
|
+
*/
|
|
20
15
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
appState(metrics.length, files.length);
|
|
24
|
-
if (metrics.length === 0) return;
|
|
16
|
+
// The table's cache of node references: made once, at the first drawing.
|
|
17
|
+
let appCache = null;
|
|
25
18
|
|
|
26
|
-
|
|
27
|
-
|
|
19
|
+
/* The note under the table: what a row is and how the report was made. It does not depend on the choice, so it is
|
|
20
|
+
* written once — with the table rather than with every drawing of it. */
|
|
21
|
+
function appNote() {
|
|
28
22
|
document.getElementById('note').textContent = appUi.note
|
|
29
23
|
.replace('{rows}', appData.rows.length)
|
|
30
24
|
.replace('{command}', appData.report.fixCommand);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* What the empty states are told: how many metrics and how many files are left. The table stands there in either
|
|
28
|
+
* case (it is built once) — the words are about what is shown. */
|
|
29
|
+
export function appCounts() {
|
|
30
|
+
appState(appData.metrics.filter((m) => appView.metrics[m.key] === true).length,
|
|
31
|
+
appView.files.filter((on) => on === true).length);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/* The whole view drawn: every column, the totals of the whole selection, the metrics, the empty states and the panel's
|
|
35
|
+
* fields. This is what a link, a record from the memory and the first drawing need — and it makes no node either. */
|
|
36
|
+
export function appPaint() {
|
|
37
|
+
appData.files.forEach((_f, i) => appColumn(appCache, i, appView.files[i]));
|
|
38
|
+
appTotalsReset(appCache);
|
|
39
|
+
appMetrics(appCache);
|
|
40
|
+
appCounts();
|
|
41
|
+
appPanelAll();
|
|
31
42
|
appWrite();
|
|
32
43
|
}
|
|
33
44
|
|
|
34
|
-
/*
|
|
35
|
-
*
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
45
|
+
/* One file switched by the reader: the view, its column, its share of the totals and the fields it shows in — each in
|
|
46
|
+
* its own place. The message about a link fades here: by this action the reader has read it. */
|
|
47
|
+
export function appSwitch(i, on) {
|
|
48
|
+
if (appView.files[i] === on) return;
|
|
49
|
+
appView.files[i] = on;
|
|
50
|
+
appColumn(appCache, i, on);
|
|
51
|
+
appContribute(appCache, i, on);
|
|
52
|
+
appTotals(appCache);
|
|
53
|
+
appCounts();
|
|
54
|
+
appPanelState([i]);
|
|
55
|
+
appWrite();
|
|
56
|
+
appNotice('');
|
|
46
57
|
}
|
|
47
58
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
59
|
+
/* A group switched at once — a folder or a category: the same work per file, then the totals once and the fields of
|
|
60
|
+
* the files the choice really reached (switching a folder on when a part of it was already on touches only the rest,
|
|
61
|
+
* and a field that did not move is not written). */
|
|
62
|
+
export function appSwitchGroup(indexes, on) {
|
|
63
|
+
const touched = indexes.filter((i) => appView.files[i] !== on);
|
|
64
|
+
touched.forEach((i) => {
|
|
65
|
+
appView.files[i] = on;
|
|
66
|
+
appColumn(appCache, i, on);
|
|
67
|
+
appContribute(appCache, i, on);
|
|
52
68
|
});
|
|
69
|
+
appTotals(appCache);
|
|
70
|
+
appCounts();
|
|
71
|
+
appPanelState(touched);
|
|
72
|
+
appWrite();
|
|
73
|
+
appNotice('');
|
|
53
74
|
}
|
|
54
75
|
|
|
55
|
-
/*
|
|
56
|
-
*
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
/* One metric switched: a class on the table and the headings' `colSpan`. The totals do not move with a metric — they
|
|
77
|
+
* are sums over files — and the metric's own field is the box the reader just clicked. */
|
|
78
|
+
export function appSwitchMetric() {
|
|
79
|
+
appMetrics(appCache);
|
|
80
|
+
appCounts();
|
|
81
|
+
appWrite();
|
|
82
|
+
appNotice('');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* The first drawing: the choice is already in the view (the link and the memory are applied above), the panel is
|
|
86
|
+
* built to match it, the table is built once — every column of every file — and the view is painted over it. */
|
|
87
|
+
function appFirst() {
|
|
64
88
|
appPanel();
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/* Сообщение о ссылке переживает отрисовку, которая сама же им и вызвана, и
|
|
69
|
-
* гаснет от действия читателя: он его уже прочитал. */
|
|
70
|
-
if (keepNotice !== true) appNotice('');
|
|
89
|
+
appCache = appTable(document.getElementById('grid'));
|
|
90
|
+
appNote();
|
|
91
|
+
appPaint();
|
|
71
92
|
}
|
|
72
93
|
|
|
73
|
-
/*
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
else
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
94
|
+
/* The page's one asynchronous step, and why there is one. The block in the artifact is packed, and the platform's own
|
|
95
|
+
* unpacker answers with a promise, so the first drawing waits for it; everything after the first drawing is as
|
|
96
|
+
* synchronous as it was, and a click costs what it cost. A host that cannot unpack is told in words instead of being
|
|
97
|
+
* left with an empty table — the reader would not know whether the report or the browser is at fault. */
|
|
98
|
+
let appBooted = false;
|
|
99
|
+
|
|
100
|
+
/* Restoring happens before the first drawing: for someone opening the page for the first time the view has to be the
|
|
101
|
+
* default rather than someone else's choice. A link outranks the memory: it is the sender's explicit choice, and
|
|
102
|
+
* while the reader has changed nothing it does not replace his own — writing it to the memory is what does not
|
|
103
|
+
* happen. A refused link is not an empty table but a message: the reader sees both what happened and what is shown
|
|
104
|
+
* instead. */
|
|
105
|
+
async function appBegin() {
|
|
106
|
+
try {
|
|
107
|
+
appBoot(await appUnpack(document.getElementById('data')));
|
|
108
|
+
} catch (_e) {
|
|
109
|
+
appNotice(appUi.unpack);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
const appStart = appLinkUse();
|
|
113
|
+
if (appStart === 'ours') appTransient = true;
|
|
114
|
+
else if (appStart === 'refused') appForeign = true;
|
|
115
|
+
if (appStart !== 'ours') {
|
|
116
|
+
const appSaved = appRead();
|
|
117
|
+
if (appSaved !== null) appApply(appSaved);
|
|
118
|
+
}
|
|
119
|
+
/* The folded tree is the onlooker's memory rather than the reader's choice: it comes back even when someone
|
|
120
|
+
* else's link is open (otherwise a link sent over would unfold the tree again on every visit). */
|
|
121
|
+
appFoldRead();
|
|
122
|
+
appFirst();
|
|
123
|
+
appBooted = true;
|
|
124
|
+
appStartup = false;
|
|
125
|
+
appForeign = false;
|
|
126
|
+
appTransient = false;
|
|
84
127
|
}
|
|
85
|
-
/* Сложенное дерево — память смотрящего, а не выбор читателя: она возвращается и
|
|
86
|
-
* тогда, когда открыта чужая ссылка (иначе присланная ссылка разложила бы дерево
|
|
87
|
-
* заново на каждом заходе). */
|
|
88
|
-
appFoldRead();
|
|
89
|
-
appRender(true);
|
|
90
|
-
appStartup = false;
|
|
91
|
-
appForeign = false;
|
|
92
|
-
appTransient = false;
|
|
93
128
|
|
|
94
|
-
/*
|
|
95
|
-
*
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
129
|
+
/* Whoever opened the page and has to know when the first drawing is over waits for this promise — the checks do
|
|
130
|
+
* (`tools/page-harness.js`); the page itself has no use for it. */
|
|
131
|
+
window.appDrawn = appBegin();
|
|
132
|
+
|
|
133
|
+
/* The anchor changed on an open page: the choice in the new address is applied by the same code as at opening. The
|
|
134
|
+
* page's own address raises no such event (`replaceState` does not), so there is no loop here. A refusal touches
|
|
135
|
+
* neither the view — the reader keeps looking at what he looked at — nor the address: it was sent to the reader,
|
|
136
|
+
* and until he acts it is not ours. The message about the link stays: this drawing is exactly what it explains. */
|
|
99
137
|
window.addEventListener('hashchange', () => {
|
|
138
|
+
/* The first drawing has not happened yet: the address the page was opened with is the business of that drawing,
|
|
139
|
+
* and a change that arrives before it has drawn nothing to replace. */
|
|
140
|
+
if (!appBooted) return;
|
|
141
|
+
/* An address that came in from outside is read first and a write this page was still holding is dropped: the reader
|
|
142
|
+
* has the address they were sent, not the one the previous click armed (see `appAddressDrop`). */
|
|
143
|
+
appAddressDrop();
|
|
100
144
|
const state = appLinkUse();
|
|
101
145
|
if (state === 'refused') appForeign = true;
|
|
102
146
|
appTransient = state === 'ours';
|
|
103
|
-
|
|
147
|
+
appPaint();
|
|
104
148
|
appForeign = false;
|
|
105
149
|
appTransient = false;
|
|
106
150
|
});
|