@inkandswitch/patchwork-pkg-base 0.0.40 → 0.0.42
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/package.json +1 -1
- package/packages/account/dist/{InlineContactAvatar-TVDLE2AV.js → InlineContactAvatar-6UAAJ73R.js} +2 -1
- package/packages/account/dist/InlineContactAvatar-6UAAJ73R.js.map +7 -0
- package/packages/account/dist/index.js +1 -1
- package/packages/chat/dist/{ChatRoot-DQkQQvPO.js → ChatRoot-WdCS4aLg.js} +3001 -2807
- package/packages/chat/dist/{context-tool-BuY29sJF.js → context-tool-KkwLFr2k.js} +2 -2
- package/packages/chat/dist/{index-NiYOPQzY.js → index-P8UuZ1jj.js} +3 -3
- package/packages/chat/dist/index.js +1 -1
- package/packages/chat/dist/{tool-He3mRTzT.js → tool-DWqQOW59.js} +2 -2
- package/packages/chat/package.json +1 -1
- package/packages/codemirror-base/dist/chunk-H5POLO74.js +2658 -0
- package/packages/codemirror-base/dist/chunk-H5POLO74.js.map +7 -0
- package/packages/codemirror-base/dist/lib/codemirror.js +1 -1
- package/packages/codemirror-base/dist/tool.js +9 -5
- package/packages/codemirror-base/dist/tool.js.map +2 -2
- package/packages/codemirror-base/package.json +1 -1
- package/packages/codemirror-markdown/dist/extensions/markdown.js +0 -94
- package/packages/codemirror-markdown/dist/extensions/markdown.js.map +3 -3
- package/packages/drafts/dist/assets/{DraftOverlayProvider-B0Mtq1UF.js → DraftOverlayProvider-VYKlzOy1.js} +85 -25
- package/packages/drafts/dist/assets/DraftOverlayProvider-VYKlzOy1.js.map +1 -0
- package/packages/drafts/dist/assets/{DraftListProvider-D41q9O5B.js → DraftStateProvider-DX8b_RgQ.js} +270 -39
- package/packages/drafts/dist/assets/DraftStateProvider-DX8b_RgQ.js.map +1 -0
- package/packages/drafts/dist/assets/{clone-policy-C4pVv5sr.js → clone-policy-CIdEhg7W.js} +5 -2
- package/packages/drafts/dist/assets/{clone-policy-C4pVv5sr.js.map → clone-policy-CIdEhg7W.js.map} +1 -1
- package/packages/drafts/dist/assets/draft-docs-Bi8K1y3a.js +554 -0
- package/packages/drafts/dist/assets/draft-docs-Bi8K1y3a.js.map +1 -0
- package/packages/drafts/dist/assets/main-BR_tW0sp.js +3105 -0
- package/packages/drafts/dist/assets/main-BR_tW0sp.js.map +1 -0
- package/packages/drafts/dist/index.js +14 -14
- package/packages/drafts/dist/index.js.map +1 -1
- package/packages/drafts/package.json +1 -1
- package/packages/threepane/dist/IsolatedDocumentArea-2JIBFDOX.js +2 -0
- package/packages/threepane/dist/{PatchworkFrame-4AEBOBRE.js → PatchworkFrame-P5P6JTJE.js} +2 -2
- package/packages/threepane/dist/chunk-CGHYPNSB.js +972 -0
- package/packages/threepane/dist/chunk-CGHYPNSB.js.map +7 -0
- package/packages/threepane/dist/index.js +1 -1
- package/packages/tldraw4/dist/tool.js +61 -10
- package/packages/tldraw4/dist/tool.js.map +2 -2
- package/packages/tldraw4/package.json +2 -2
- package/packages/account/dist/InlineContactAvatar-TVDLE2AV.js.map +0 -7
- package/packages/codemirror-base/dist/chunk-5IG5TKXV.js +0 -795
- package/packages/codemirror-base/dist/chunk-5IG5TKXV.js.map +0 -7
- package/packages/drafts/dist/assets/DraftListProvider-D41q9O5B.js.map +0 -1
- package/packages/drafts/dist/assets/DraftOverlayProvider-B0Mtq1UF.js.map +0 -1
- package/packages/drafts/dist/assets/main-Cp8VsNHh.js +0 -2331
- package/packages/drafts/dist/assets/main-Cp8VsNHh.js.map +0 -1
- package/packages/threepane/dist/IsolatedDocumentArea-MZBBRFMY.js +0 -2
- package/packages/threepane/dist/chunk-HNLVJAZZ.js +0 -972
- package/packages/threepane/dist/chunk-HNLVJAZZ.js.map +0 -7
- /package/packages/threepane/dist/{IsolatedDocumentArea-MZBBRFMY.js.map → IsolatedDocumentArea-2JIBFDOX.js.map} +0 -0
- /package/packages/threepane/dist/{PatchworkFrame-4AEBOBRE.js.map → PatchworkFrame-P5P6JTJE.js.map} +0 -0
|
@@ -0,0 +1,3105 @@
|
|
|
1
|
+
(function() {
|
|
2
|
+
"use strict";
|
|
3
|
+
try {
|
|
4
|
+
if (typeof document != "undefined") {
|
|
5
|
+
var elementStyle = document.createElement("style");
|
|
6
|
+
elementStyle.appendChild(document.createTextNode(`@layer package {
|
|
7
|
+
:root,
|
|
8
|
+
:host,
|
|
9
|
+
[theme] {
|
|
10
|
+
--drafts-fg: var(--studio-line, black);
|
|
11
|
+
--drafts-muted-fg: var(--studio-line-offset-50, #6b7280);
|
|
12
|
+
--drafts-faint-fg: var(--studio-line-offset-40, #9ca3af);
|
|
13
|
+
--drafts-bg: var(--studio-fill, white);
|
|
14
|
+
--drafts-border: var(--studio-fill-offset-20, #e5e7eb);
|
|
15
|
+
--drafts-hover-bg: var(--studio-fill-offset-10, #f9fafb);
|
|
16
|
+
--drafts-selected-bg: var(--studio-fill-offset-20, #eef2ff);
|
|
17
|
+
--drafts-font: var(--studio-family-sans, system-ui, sans-serif);
|
|
18
|
+
--drafts-mono: var(--studio-family-code, ui-monospace, monospace);
|
|
19
|
+
--drafts-primary: var(--studio-primary-fill, var(--studio-primary, #3b82f6));
|
|
20
|
+
/* The accent used as ink on the ambient background (accent-coloured text,
|
|
21
|
+
not a filled surface); contrast-tuned separately from the fill. */
|
|
22
|
+
--drafts-primary-text: var(--studio-primary-text, #2563eb);
|
|
23
|
+
/* Danger as ink (the menu's delete item). */
|
|
24
|
+
--drafts-danger-text: var(--studio-danger-text, var(--studio-danger, #dc2626));
|
|
25
|
+
/* Diff edit-size colors, from the theme's diff palette. */
|
|
26
|
+
--drafts-added: var(--studio-added, #16a34a);
|
|
27
|
+
--drafts-deleted: var(--studio-deleted, #dc2626);
|
|
28
|
+
--drafts-radius: var(--studio-radius-md, 0.5rem);
|
|
29
|
+
--drafts-radius-sm: var(--studio-radius-sm, 0.25rem);
|
|
30
|
+
--drafts-shadow-sm: var(--studio-shadow-sm, 0 1px 2px 0 rgb(0 0 0 / 0.05));
|
|
31
|
+
--drafts-shadow-md: var(
|
|
32
|
+
--studio-shadow-md,
|
|
33
|
+
0 4px 6px -1px rgb(0 0 0 / 0.1),
|
|
34
|
+
0 2px 4px -2px rgb(0 0 0 / 0.1)
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.drafts-panel {
|
|
40
|
+
height: 100%;
|
|
41
|
+
display: flex;
|
|
42
|
+
flex-direction: column;
|
|
43
|
+
padding: 0.5rem;
|
|
44
|
+
gap: 0.5rem;
|
|
45
|
+
font-family: var(--drafts-font);
|
|
46
|
+
color: var(--drafts-fg);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
.drafts-empty {
|
|
50
|
+
font-size: 0.75rem;
|
|
51
|
+
color: var(--drafts-faint-fg);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Build stamp pinned below the list so deployed versions are identifiable. */
|
|
55
|
+
.drafts-version {
|
|
56
|
+
margin-top: auto;
|
|
57
|
+
font-size: 0.65rem;
|
|
58
|
+
color: var(--drafts-faint-fg);
|
|
59
|
+
text-align: right;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
.drafts-list {
|
|
63
|
+
display: flex;
|
|
64
|
+
flex-direction: column;
|
|
65
|
+
gap: 0.25rem;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
.draft-card {
|
|
69
|
+
background: var(--drafts-bg);
|
|
70
|
+
border: 1px solid var(--drafts-border);
|
|
71
|
+
border-radius: var(--drafts-radius);
|
|
72
|
+
box-shadow: var(--drafts-shadow-sm);
|
|
73
|
+
overflow: hidden;
|
|
74
|
+
transition:
|
|
75
|
+
border-color var(--studio-transition, 0.15s ease),
|
|
76
|
+
box-shadow var(--studio-transition, 0.15s ease);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/* Selection reads as a soft accent-tinted border, no extra ring — the open
|
|
80
|
+
changes list already sets the selected card apart. */
|
|
81
|
+
.draft-card[data-selected] {
|
|
82
|
+
border-color: color-mix(in oklch, var(--drafts-primary) 35%, var(--drafts-border));
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/* While the context menu is showing, lift the card's clipping so the
|
|
86
|
+
dropdown can extend past a short card. */
|
|
87
|
+
.draft-card[data-menu-open] {
|
|
88
|
+
overflow: visible;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* While the menu's "Merge into …" item is hovered, the target card (the
|
|
92
|
+
draft's parent) lights up so you can see where the changes will land. */
|
|
93
|
+
.draft-card[data-merge-target] {
|
|
94
|
+
border-color: var(--drafts-primary);
|
|
95
|
+
box-shadow:
|
|
96
|
+
0 0 0 1px var(--drafts-primary),
|
|
97
|
+
var(--drafts-shadow-sm);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.draft-card-header {
|
|
101
|
+
/* The header is a div (a rename input can't nest in a button), which,
|
|
102
|
+
unlike a button, doesn't default to border-box — without this the
|
|
103
|
+
padding pushes it past the card edge. */
|
|
104
|
+
box-sizing: border-box;
|
|
105
|
+
display: flex;
|
|
106
|
+
flex-direction: column;
|
|
107
|
+
gap: 0.25rem;
|
|
108
|
+
width: 100%;
|
|
109
|
+
padding: 0.5rem;
|
|
110
|
+
text-align: left;
|
|
111
|
+
background: transparent;
|
|
112
|
+
border: none;
|
|
113
|
+
/* Round the top corners itself: the card's overflow clipping is lifted
|
|
114
|
+
while the menu is open, so the hover background can't rely on it. */
|
|
115
|
+
border-radius: calc(var(--drafts-radius) - 1px) calc(var(--drafts-radius) - 1px) 0 0;
|
|
116
|
+
cursor: pointer;
|
|
117
|
+
font-family: inherit;
|
|
118
|
+
color: inherit;
|
|
119
|
+
transition: background var(--studio-transition, 0.15s ease);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.draft-card-header:hover {
|
|
123
|
+
background: var(--drafts-hover-bg);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.draft-card-title {
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
gap: 0.5rem;
|
|
130
|
+
font-size: 0.875rem;
|
|
131
|
+
font-weight: 500;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.draft-name {
|
|
135
|
+
overflow: hidden;
|
|
136
|
+
text-overflow: ellipsis;
|
|
137
|
+
white-space: nowrap;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Inline rename editor, swapped in for .draft-name on double-click. */
|
|
141
|
+
.draft-name-input {
|
|
142
|
+
flex: 1;
|
|
143
|
+
min-width: 0;
|
|
144
|
+
font: inherit;
|
|
145
|
+
color: inherit;
|
|
146
|
+
padding: 0 0.25rem;
|
|
147
|
+
margin: 0;
|
|
148
|
+
background: var(--drafts-bg);
|
|
149
|
+
border: 1px solid var(--drafts-primary);
|
|
150
|
+
border-radius: var(--drafts-radius-sm);
|
|
151
|
+
outline: none;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
.draft-card-url {
|
|
155
|
+
font-size: 0.75rem;
|
|
156
|
+
font-family: var(--drafts-mono);
|
|
157
|
+
color: var(--drafts-muted-fg);
|
|
158
|
+
word-break: break-all;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.draft-card-meta {
|
|
162
|
+
font-size: 0.75rem;
|
|
163
|
+
color: var(--drafts-faint-fg);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.draft-card-changes {
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
gap: 0.125rem;
|
|
170
|
+
border-top: 1px solid var(--drafts-border);
|
|
171
|
+
/* Extra top headroom: the scrubber's dot (5px) and sticker (10px) overhang
|
|
172
|
+
above the head line, which sits at 0 on the latest version — without
|
|
173
|
+
this they'd crowd into the card header. */
|
|
174
|
+
padding: 0.75rem 0.25rem 0.25rem;
|
|
175
|
+
max-height: 12rem;
|
|
176
|
+
overflow-y: auto;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* The selected card gets room to show more history; cards with little
|
|
180
|
+
history stay at their natural height. */
|
|
181
|
+
.draft-card[data-selected] .draft-card-changes {
|
|
182
|
+
max-height: 32rem;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Right-aligned icon cluster in the selected card's title: the eye, fork,
|
|
186
|
+
and (on drafts) merge-up buttons. */
|
|
187
|
+
.draft-card-tools {
|
|
188
|
+
flex-shrink: 0;
|
|
189
|
+
margin-left: auto;
|
|
190
|
+
display: inline-flex;
|
|
191
|
+
align-items: center;
|
|
192
|
+
gap: 0.125rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/* Icon actions in the card title (fork, merge): same look as the eye. */
|
|
196
|
+
.draft-card-action {
|
|
197
|
+
flex-shrink: 0;
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
justify-content: center;
|
|
201
|
+
padding: 0.25rem;
|
|
202
|
+
border: none;
|
|
203
|
+
border-radius: var(--studio-radius, 0.25rem);
|
|
204
|
+
background: transparent;
|
|
205
|
+
color: var(--drafts-faint-fg);
|
|
206
|
+
cursor: pointer;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.draft-card-action:hover:not(:disabled) {
|
|
210
|
+
background: var(--drafts-hover-bg);
|
|
211
|
+
color: var(--drafts-primary-text);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
.draft-card-action:disabled {
|
|
215
|
+
opacity: 0.35;
|
|
216
|
+
cursor: default;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/* The menu trigger stays lit while its dropdown is showing. */
|
|
220
|
+
.draft-card-action[data-active] {
|
|
221
|
+
background: var(--drafts-hover-bg);
|
|
222
|
+
color: var(--drafts-primary-text);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* ---- The card-title context menu (⋯): trigger + dropdown ---- */
|
|
226
|
+
|
|
227
|
+
/* Anchor for the absolutely positioned dropdown. */
|
|
228
|
+
.draft-menu {
|
|
229
|
+
position: relative;
|
|
230
|
+
display: inline-flex;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/* Invisible click-catcher behind the dropdown; closes the menu. */
|
|
234
|
+
.draft-menu-backdrop {
|
|
235
|
+
position: fixed;
|
|
236
|
+
inset: 0;
|
|
237
|
+
z-index: 40;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.draft-menu-list {
|
|
241
|
+
position: absolute;
|
|
242
|
+
top: calc(100% + 0.25rem);
|
|
243
|
+
right: 0;
|
|
244
|
+
z-index: 41;
|
|
245
|
+
min-width: 13rem;
|
|
246
|
+
display: flex;
|
|
247
|
+
flex-direction: column;
|
|
248
|
+
padding: 0.25rem;
|
|
249
|
+
background: var(--drafts-bg);
|
|
250
|
+
border: 1px solid var(--drafts-border);
|
|
251
|
+
border-radius: var(--drafts-radius-sm);
|
|
252
|
+
box-shadow: var(--drafts-shadow-md);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.draft-menu-item {
|
|
256
|
+
display: flex;
|
|
257
|
+
align-items: center;
|
|
258
|
+
gap: 0.5rem;
|
|
259
|
+
width: 100%;
|
|
260
|
+
padding: 0.375rem 0.5rem;
|
|
261
|
+
border: none;
|
|
262
|
+
border-radius: var(--drafts-radius-sm);
|
|
263
|
+
background: transparent;
|
|
264
|
+
font-family: inherit;
|
|
265
|
+
font-size: 0.8125rem;
|
|
266
|
+
text-align: left;
|
|
267
|
+
color: var(--drafts-fg);
|
|
268
|
+
cursor: pointer;
|
|
269
|
+
white-space: nowrap;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
.draft-menu-item:hover:not(:disabled) {
|
|
273
|
+
background: var(--drafts-hover-bg);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
.draft-menu-item:disabled {
|
|
277
|
+
opacity: 0.5;
|
|
278
|
+
cursor: default;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.draft-menu-item svg {
|
|
282
|
+
flex: none;
|
|
283
|
+
color: var(--drafts-muted-fg);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* The destructive delete item (and its confirm step) reads in danger ink. */
|
|
287
|
+
.draft-menu-item--danger {
|
|
288
|
+
color: var(--drafts-danger-text);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.draft-menu-item--danger:hover:not(:disabled) {
|
|
292
|
+
background: color-mix(in oklch, var(--drafts-danger-text) 12%, transparent);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.draft-menu-item--danger svg {
|
|
296
|
+
color: inherit;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.draft-menu-separator {
|
|
300
|
+
height: 1px;
|
|
301
|
+
margin: 0.25rem;
|
|
302
|
+
background: var(--drafts-border);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/* Sits in the card title, left-aligned right after the name, while the
|
|
306
|
+
timeline is pinned; drops the pin and returns to the latest version. */
|
|
307
|
+
.draft-card-return {
|
|
308
|
+
flex-shrink: 0;
|
|
309
|
+
padding: 0.125rem 0.5rem;
|
|
310
|
+
border: 1px solid var(--drafts-border);
|
|
311
|
+
border-radius: var(--studio-radius-round, 9999px);
|
|
312
|
+
background: transparent;
|
|
313
|
+
font-family: inherit;
|
|
314
|
+
font-size: 0.6875rem;
|
|
315
|
+
line-height: 1.2;
|
|
316
|
+
color: var(--drafts-primary-text);
|
|
317
|
+
cursor: pointer;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
.draft-card-return:hover {
|
|
321
|
+
background: var(--drafts-hover-bg);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/* The diff toggle (eye) in the card title's tool cluster. Active = diff
|
|
325
|
+
highlighting showing (the checkpoint carries baselines). */
|
|
326
|
+
.draft-card-eye {
|
|
327
|
+
flex-shrink: 0;
|
|
328
|
+
display: inline-flex;
|
|
329
|
+
align-items: center;
|
|
330
|
+
justify-content: center;
|
|
331
|
+
padding: 0.25rem;
|
|
332
|
+
border: none;
|
|
333
|
+
border-radius: var(--studio-radius, 0.25rem);
|
|
334
|
+
background: transparent;
|
|
335
|
+
color: var(--drafts-faint-fg);
|
|
336
|
+
cursor: pointer;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
.draft-card-eye:hover:not([data-disabled]) {
|
|
340
|
+
background: var(--drafts-hover-bg);
|
|
341
|
+
color: var(--drafts-primary-text);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
.draft-card-eye[data-active] {
|
|
345
|
+
color: var(--drafts-primary-text);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Kept hoverable while disabled (aria-disabled, not the attribute) so the
|
|
349
|
+
explanatory tooltip shows; clicks are ignored in the handler. */
|
|
350
|
+
.draft-card-eye[data-disabled] {
|
|
351
|
+
opacity: 0.35;
|
|
352
|
+
cursor: default;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.draft-changes-empty {
|
|
356
|
+
font-size: 0.75rem;
|
|
357
|
+
color: var(--drafts-faint-fg);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.draft-changes-building {
|
|
361
|
+
display: flex;
|
|
362
|
+
align-items: center;
|
|
363
|
+
justify-content: center;
|
|
364
|
+
gap: 0.375rem;
|
|
365
|
+
padding: 0.25rem 0;
|
|
366
|
+
font-size: 0.75rem;
|
|
367
|
+
color: var(--drafts-faint-fg);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
.draft-building-spinner {
|
|
371
|
+
flex: none;
|
|
372
|
+
width: 0.625rem;
|
|
373
|
+
height: 0.625rem;
|
|
374
|
+
border-radius: 50%;
|
|
375
|
+
border: 1.5px solid currentColor;
|
|
376
|
+
border-top-color: transparent;
|
|
377
|
+
animation: draft-building-spin 0.8s linear infinite;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
@keyframes draft-building-spin {
|
|
381
|
+
to {
|
|
382
|
+
transform: rotate(360deg);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
.draft-change-row {
|
|
387
|
+
display: flex;
|
|
388
|
+
flex-direction: column;
|
|
389
|
+
gap: 0.125rem;
|
|
390
|
+
width: 100%;
|
|
391
|
+
padding: 0.25rem 0.5rem;
|
|
392
|
+
border: none;
|
|
393
|
+
border-radius: var(--drafts-radius-sm);
|
|
394
|
+
background: transparent;
|
|
395
|
+
font-family: inherit;
|
|
396
|
+
font-size: 0.75rem;
|
|
397
|
+
text-align: left;
|
|
398
|
+
color: inherit;
|
|
399
|
+
cursor: pointer;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.draft-change-line {
|
|
403
|
+
display: flex;
|
|
404
|
+
align-items: baseline;
|
|
405
|
+
gap: 0.5rem;
|
|
406
|
+
width: 100%;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
.draft-change-row:hover {
|
|
410
|
+
background: var(--drafts-hover-bg);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
.draft-change-row[data-selected] {
|
|
414
|
+
background: var(--drafts-selected-bg);
|
|
415
|
+
box-shadow: inset 0 0 0 1px var(--drafts-primary);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.draft-change-time {
|
|
419
|
+
flex: none;
|
|
420
|
+
color: var(--drafts-muted-fg);
|
|
421
|
+
white-space: nowrap;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.draft-change-doc {
|
|
425
|
+
flex: 0 1 auto;
|
|
426
|
+
min-width: 0;
|
|
427
|
+
font-weight: 600;
|
|
428
|
+
color: var(--drafts-fg);
|
|
429
|
+
overflow: hidden;
|
|
430
|
+
text-overflow: ellipsis;
|
|
431
|
+
white-space: nowrap;
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
.draft-change-msg {
|
|
435
|
+
flex: 1 1 auto;
|
|
436
|
+
min-width: 0;
|
|
437
|
+
color: var(--drafts-fg);
|
|
438
|
+
overflow: hidden;
|
|
439
|
+
text-overflow: ellipsis;
|
|
440
|
+
white-space: nowrap;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
.draft-change-hash {
|
|
444
|
+
font-family: var(--drafts-mono);
|
|
445
|
+
font-size: 0.625rem;
|
|
446
|
+
color: var(--drafts-faint-fg);
|
|
447
|
+
word-break: break-all;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/* ---- Grouped history: one non-expandable row per time window ---- */
|
|
451
|
+
|
|
452
|
+
/* Group row layout: avatar stack, timestamp, flexible spacer, +/- counts.
|
|
453
|
+
The extra vertical padding gives each group a taller scrubber band, so
|
|
454
|
+
per-change stops inside a group are easier to hit. */
|
|
455
|
+
.draft-group-row {
|
|
456
|
+
display: flex;
|
|
457
|
+
align-items: center;
|
|
458
|
+
gap: 0.375rem;
|
|
459
|
+
width: 100%;
|
|
460
|
+
padding: 0.5rem 0.375rem;
|
|
461
|
+
border: none;
|
|
462
|
+
border-radius: var(--drafts-radius-sm);
|
|
463
|
+
background: transparent;
|
|
464
|
+
font-family: inherit;
|
|
465
|
+
font-size: 0.75rem;
|
|
466
|
+
text-align: left;
|
|
467
|
+
color: inherit;
|
|
468
|
+
cursor: pointer;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/* Group backgrounds stay lightly translucent so the row highlight reads as a
|
|
472
|
+
soft wash rather than a hard block. */
|
|
473
|
+
.draft-group-row:hover {
|
|
474
|
+
background: color-mix(in oklch, var(--drafts-hover-bg) 60%, transparent);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
.draft-group-spacer {
|
|
478
|
+
flex: 1 1 auto;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.draft-group-time {
|
|
482
|
+
color: var(--drafts-muted-fg);
|
|
483
|
+
white-space: nowrap;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
/* Author avatars */
|
|
487
|
+
.draft-avatars {
|
|
488
|
+
display: flex;
|
|
489
|
+
align-items: center;
|
|
490
|
+
flex: none;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.draft-avatar {
|
|
494
|
+
position: relative;
|
|
495
|
+
flex: none;
|
|
496
|
+
width: 18px;
|
|
497
|
+
height: 18px;
|
|
498
|
+
border-radius: 50%;
|
|
499
|
+
display: flex;
|
|
500
|
+
align-items: center;
|
|
501
|
+
justify-content: center;
|
|
502
|
+
font-size: 9px;
|
|
503
|
+
font-weight: 300;
|
|
504
|
+
color: white;
|
|
505
|
+
user-select: none;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
.draft-avatar--extra {
|
|
509
|
+
margin-left: -4px;
|
|
510
|
+
font-size: 7px;
|
|
511
|
+
background: var(--drafts-faint-fg);
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* An attributed author embeds the contact tool's avatar (a <patchwork-view
|
|
515
|
+
tool-id="contact-inline">, see AuthorAvatars); size it to the stack slot —
|
|
516
|
+
the wrapper keeps the stack geometry (overlap + z-index). */
|
|
517
|
+
.draft-avatar > patchwork-view {
|
|
518
|
+
display: flex;
|
|
519
|
+
width: 100%;
|
|
520
|
+
height: 100%;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
.draft-avatar .contact-avatar {
|
|
524
|
+
width: 100%;
|
|
525
|
+
height: 100%;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
/* ---- Scrubber: the history gutter left of the group rows ---- */
|
|
529
|
+
|
|
530
|
+
/* Two-column body inside the scrollable changes area: gutter + group rows,
|
|
531
|
+
with the indicator overlaid across both. Positioned so the rows'
|
|
532
|
+
offsetTop measurements (used to map changes onto the gutter) are relative
|
|
533
|
+
to this box, and isolated so the indicator's own stacking context stays
|
|
534
|
+
contained to this card. */
|
|
535
|
+
.draft-changes-body {
|
|
536
|
+
/* One softened ink for every scrubber mark (head, baseline, connector,
|
|
537
|
+
sticker border): mixed toward the background so the lines read as
|
|
538
|
+
annotations, not hard rules. Defined here so all of them share it. */
|
|
539
|
+
--scrub-color: color-mix(in oklch, var(--drafts-fg) 55%, var(--drafts-bg));
|
|
540
|
+
position: relative;
|
|
541
|
+
isolation: isolate;
|
|
542
|
+
display: flex;
|
|
543
|
+
align-items: stretch;
|
|
544
|
+
gap: 0.25rem;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
.draft-changes-rows {
|
|
548
|
+
flex: 1 1 auto;
|
|
549
|
+
min-width: 0;
|
|
550
|
+
display: flex;
|
|
551
|
+
flex-direction: column;
|
|
552
|
+
gap: 0.125rem;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
/* The gutter spans the whole history: top = latest change, bottom = first.
|
|
556
|
+
Purely a visual track — scrubbing starts from the indicator's handles. */
|
|
557
|
+
.draft-scrubber {
|
|
558
|
+
position: relative;
|
|
559
|
+
flex: none;
|
|
560
|
+
width: 16px;
|
|
561
|
+
border-radius: var(--drafts-radius-sm);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/* The indicator sits at the head like a calendar's now-line: a dot in the
|
|
565
|
+
gutter plus a line across the rows (the version being looked at). It paints
|
|
566
|
+
on top of everything else in the changes area (high z-index), always drawn
|
|
567
|
+
in the foreground color. The box itself ignores pointer events so rows stay
|
|
568
|
+
clickable; the handles re-enable them. */
|
|
569
|
+
.draft-scrubber-token {
|
|
570
|
+
position: absolute;
|
|
571
|
+
left: 0;
|
|
572
|
+
right: 0;
|
|
573
|
+
z-index: 20;
|
|
574
|
+
pointer-events: none;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.draft-scrubber-dot {
|
|
578
|
+
position: absolute;
|
|
579
|
+
top: -5px;
|
|
580
|
+
left: 3px;
|
|
581
|
+
width: 10px;
|
|
582
|
+
height: 10px;
|
|
583
|
+
border-radius: 50%;
|
|
584
|
+
background: var(--scrub-color);
|
|
585
|
+
pointer-events: auto;
|
|
586
|
+
cursor: grab;
|
|
587
|
+
touch-action: none;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.draft-scrubber-dot:active {
|
|
591
|
+
cursor: grabbing;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
/* The visible head line, spanning across the rows on top of them. */
|
|
595
|
+
.draft-scrubber-line {
|
|
596
|
+
position: absolute;
|
|
597
|
+
top: 0;
|
|
598
|
+
left: 8px;
|
|
599
|
+
right: 0;
|
|
600
|
+
height: 2px;
|
|
601
|
+
margin-top: -1px;
|
|
602
|
+
border-radius: 1px;
|
|
603
|
+
background: var(--scrub-color);
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
/* The baseline's line reads fainter than the head's, so the version being
|
|
607
|
+
viewed stays the dominant mark. */
|
|
608
|
+
.draft-scrubber-token--baseline .draft-scrubber-line {
|
|
609
|
+
opacity: 0.55;
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/* Invisible grab strip over the line's gutter end; dragging only starts
|
|
613
|
+
from this (or the dot), never from the rows or the bare gutter. */
|
|
614
|
+
.draft-scrubber-edge {
|
|
615
|
+
position: absolute;
|
|
616
|
+
top: -4.5px;
|
|
617
|
+
left: 0;
|
|
618
|
+
width: 16px;
|
|
619
|
+
height: 9px;
|
|
620
|
+
pointer-events: auto;
|
|
621
|
+
cursor: grab;
|
|
622
|
+
touch-action: none;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* The diff baseline handle: a hollow circle (vs the head's solid dot) in the
|
|
626
|
+
gutter, sharing the head's line style across the rows. Only rendered with
|
|
627
|
+
the eye open and a version pinned. */
|
|
628
|
+
.draft-scrubber-baseline {
|
|
629
|
+
position: absolute;
|
|
630
|
+
top: -5px;
|
|
631
|
+
left: 3px;
|
|
632
|
+
width: 10px;
|
|
633
|
+
height: 10px;
|
|
634
|
+
box-sizing: border-box;
|
|
635
|
+
border: 2px solid var(--scrub-color);
|
|
636
|
+
border-radius: 50%;
|
|
637
|
+
background: var(--studio-fill, white);
|
|
638
|
+
pointer-events: auto;
|
|
639
|
+
cursor: grab;
|
|
640
|
+
touch-action: none;
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.draft-scrubber-baseline:active {
|
|
644
|
+
cursor: grabbing;
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
/* Joins the head and baseline handles down the gutter so the two read as one
|
|
648
|
+
diff range. Above the rows, below the handles. */
|
|
649
|
+
.draft-scrubber-connector {
|
|
650
|
+
position: absolute;
|
|
651
|
+
left: 7px;
|
|
652
|
+
width: 2px;
|
|
653
|
+
z-index: 10;
|
|
654
|
+
background: var(--scrub-color);
|
|
655
|
+
opacity: 0.5;
|
|
656
|
+
pointer-events: none;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
/* While pinned, a chip anchored to the head line overlays the group row's
|
|
660
|
+
time/summary with the exact change the line sits on. Painted above
|
|
661
|
+
everything in the changes body (opaque, topmost z-index) so neither the
|
|
662
|
+
head line nor the row content shows through. */
|
|
663
|
+
.draft-scrubber-sticker {
|
|
664
|
+
position: absolute;
|
|
665
|
+
top: -10px;
|
|
666
|
+
left: calc(16px + 0.25rem);
|
|
667
|
+
right: 0;
|
|
668
|
+
height: 20px;
|
|
669
|
+
z-index: 10;
|
|
670
|
+
display: flex;
|
|
671
|
+
align-items: center;
|
|
672
|
+
gap: 0.375rem;
|
|
673
|
+
padding: 0 0.375rem;
|
|
674
|
+
border: 1px solid var(--scrub-color);
|
|
675
|
+
border-radius: var(--drafts-radius-sm);
|
|
676
|
+
background: var(--drafts-bg);
|
|
677
|
+
font-size: 0.6875rem;
|
|
678
|
+
white-space: nowrap;
|
|
679
|
+
overflow: hidden;
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
.draft-sticker-time {
|
|
683
|
+
flex: none;
|
|
684
|
+
color: var(--drafts-muted-fg);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
.draft-sticker-spacer {
|
|
688
|
+
flex: 1 1 auto;
|
|
689
|
+
}
|
|
690
|
+
|
|
691
|
+
/* Edit-size counts */
|
|
692
|
+
.draft-counts {
|
|
693
|
+
display: flex;
|
|
694
|
+
align-items: center;
|
|
695
|
+
gap: 0.375rem;
|
|
696
|
+
flex: none;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
.draft-count {
|
|
700
|
+
font-size: 0.6875rem;
|
|
701
|
+
font-weight: 300;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
.draft-count--add {
|
|
705
|
+
color: var(--drafts-added);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
.draft-count--del {
|
|
709
|
+
color: var(--drafts-deleted);
|
|
710
|
+
}`));
|
|
711
|
+
document.head.appendChild(elementStyle);
|
|
712
|
+
}
|
|
713
|
+
} catch (e) {
|
|
714
|
+
console.error("vite-plugin-css-injected-by-js", e);
|
|
715
|
+
}
|
|
716
|
+
})();
|
|
717
|
+
import { insert, createComponent, memo, template, addEventListener, effect, setAttribute, setStyleProperty, use, getOwner, delegateEvents, render } from "solid-js/web";
|
|
718
|
+
import { createContext, createSignal, createEffect, onCleanup, onMount, createMemo, Show, For } from "solid-js";
|
|
719
|
+
import { createStore, reconcile } from "solid-js/store";
|
|
720
|
+
import { encodeHeads, decodeHeads, isValidAutomergeUrl } from "@automerge/automerge-repo/slim";
|
|
721
|
+
import * as Automerge from "@automerge/automerge/slim";
|
|
722
|
+
import { s as subscribe$1 } from "./index-FKZtof6Z.js";
|
|
723
|
+
import { e as ensureMainDraft, g as getDocCreationTime, s as sameHeads, a as computeRangeEditCounts, b as computeEditCounts } from "./draft-docs-Bi8K1y3a.js";
|
|
724
|
+
import "@automerge/automerge-repo";
|
|
725
|
+
function getDefaultExportFromCjs(x) {
|
|
726
|
+
return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
|
|
727
|
+
}
|
|
728
|
+
var browser = { exports: {} };
|
|
729
|
+
var ms;
|
|
730
|
+
var hasRequiredMs;
|
|
731
|
+
function requireMs() {
|
|
732
|
+
if (hasRequiredMs) return ms;
|
|
733
|
+
hasRequiredMs = 1;
|
|
734
|
+
var s = 1e3;
|
|
735
|
+
var m = s * 60;
|
|
736
|
+
var h = m * 60;
|
|
737
|
+
var d = h * 24;
|
|
738
|
+
var w = d * 7;
|
|
739
|
+
var y = d * 365.25;
|
|
740
|
+
ms = function(val, options) {
|
|
741
|
+
options = options || {};
|
|
742
|
+
var type = typeof val;
|
|
743
|
+
if (type === "string" && val.length > 0) {
|
|
744
|
+
return parse(val);
|
|
745
|
+
} else if (type === "number" && isFinite(val)) {
|
|
746
|
+
return options.long ? fmtLong(val) : fmtShort(val);
|
|
747
|
+
}
|
|
748
|
+
throw new Error(
|
|
749
|
+
"val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
|
|
750
|
+
);
|
|
751
|
+
};
|
|
752
|
+
function parse(str) {
|
|
753
|
+
str = String(str);
|
|
754
|
+
if (str.length > 100) {
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
|
|
758
|
+
str
|
|
759
|
+
);
|
|
760
|
+
if (!match) {
|
|
761
|
+
return;
|
|
762
|
+
}
|
|
763
|
+
var n = parseFloat(match[1]);
|
|
764
|
+
var type = (match[2] || "ms").toLowerCase();
|
|
765
|
+
switch (type) {
|
|
766
|
+
case "years":
|
|
767
|
+
case "year":
|
|
768
|
+
case "yrs":
|
|
769
|
+
case "yr":
|
|
770
|
+
case "y":
|
|
771
|
+
return n * y;
|
|
772
|
+
case "weeks":
|
|
773
|
+
case "week":
|
|
774
|
+
case "w":
|
|
775
|
+
return n * w;
|
|
776
|
+
case "days":
|
|
777
|
+
case "day":
|
|
778
|
+
case "d":
|
|
779
|
+
return n * d;
|
|
780
|
+
case "hours":
|
|
781
|
+
case "hour":
|
|
782
|
+
case "hrs":
|
|
783
|
+
case "hr":
|
|
784
|
+
case "h":
|
|
785
|
+
return n * h;
|
|
786
|
+
case "minutes":
|
|
787
|
+
case "minute":
|
|
788
|
+
case "mins":
|
|
789
|
+
case "min":
|
|
790
|
+
case "m":
|
|
791
|
+
return n * m;
|
|
792
|
+
case "seconds":
|
|
793
|
+
case "second":
|
|
794
|
+
case "secs":
|
|
795
|
+
case "sec":
|
|
796
|
+
case "s":
|
|
797
|
+
return n * s;
|
|
798
|
+
case "milliseconds":
|
|
799
|
+
case "millisecond":
|
|
800
|
+
case "msecs":
|
|
801
|
+
case "msec":
|
|
802
|
+
case "ms":
|
|
803
|
+
return n;
|
|
804
|
+
default:
|
|
805
|
+
return void 0;
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
function fmtShort(ms2) {
|
|
809
|
+
var msAbs = Math.abs(ms2);
|
|
810
|
+
if (msAbs >= d) {
|
|
811
|
+
return Math.round(ms2 / d) + "d";
|
|
812
|
+
}
|
|
813
|
+
if (msAbs >= h) {
|
|
814
|
+
return Math.round(ms2 / h) + "h";
|
|
815
|
+
}
|
|
816
|
+
if (msAbs >= m) {
|
|
817
|
+
return Math.round(ms2 / m) + "m";
|
|
818
|
+
}
|
|
819
|
+
if (msAbs >= s) {
|
|
820
|
+
return Math.round(ms2 / s) + "s";
|
|
821
|
+
}
|
|
822
|
+
return ms2 + "ms";
|
|
823
|
+
}
|
|
824
|
+
function fmtLong(ms2) {
|
|
825
|
+
var msAbs = Math.abs(ms2);
|
|
826
|
+
if (msAbs >= d) {
|
|
827
|
+
return plural(ms2, msAbs, d, "day");
|
|
828
|
+
}
|
|
829
|
+
if (msAbs >= h) {
|
|
830
|
+
return plural(ms2, msAbs, h, "hour");
|
|
831
|
+
}
|
|
832
|
+
if (msAbs >= m) {
|
|
833
|
+
return plural(ms2, msAbs, m, "minute");
|
|
834
|
+
}
|
|
835
|
+
if (msAbs >= s) {
|
|
836
|
+
return plural(ms2, msAbs, s, "second");
|
|
837
|
+
}
|
|
838
|
+
return ms2 + " ms";
|
|
839
|
+
}
|
|
840
|
+
function plural(ms2, msAbs, n, name) {
|
|
841
|
+
var isPlural = msAbs >= n * 1.5;
|
|
842
|
+
return Math.round(ms2 / n) + " " + name + (isPlural ? "s" : "");
|
|
843
|
+
}
|
|
844
|
+
return ms;
|
|
845
|
+
}
|
|
846
|
+
var common;
|
|
847
|
+
var hasRequiredCommon;
|
|
848
|
+
function requireCommon() {
|
|
849
|
+
if (hasRequiredCommon) return common;
|
|
850
|
+
hasRequiredCommon = 1;
|
|
851
|
+
function setup(env) {
|
|
852
|
+
createDebug.debug = createDebug;
|
|
853
|
+
createDebug.default = createDebug;
|
|
854
|
+
createDebug.coerce = coerce;
|
|
855
|
+
createDebug.disable = disable;
|
|
856
|
+
createDebug.enable = enable;
|
|
857
|
+
createDebug.enabled = enabled;
|
|
858
|
+
createDebug.humanize = requireMs();
|
|
859
|
+
createDebug.destroy = destroy;
|
|
860
|
+
Object.keys(env).forEach((key) => {
|
|
861
|
+
createDebug[key] = env[key];
|
|
862
|
+
});
|
|
863
|
+
createDebug.names = [];
|
|
864
|
+
createDebug.skips = [];
|
|
865
|
+
createDebug.formatters = {};
|
|
866
|
+
function selectColor(namespace) {
|
|
867
|
+
let hash = 0;
|
|
868
|
+
for (let i = 0; i < namespace.length; i++) {
|
|
869
|
+
hash = (hash << 5) - hash + namespace.charCodeAt(i);
|
|
870
|
+
hash |= 0;
|
|
871
|
+
}
|
|
872
|
+
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
|
873
|
+
}
|
|
874
|
+
createDebug.selectColor = selectColor;
|
|
875
|
+
function createDebug(namespace) {
|
|
876
|
+
let prevTime;
|
|
877
|
+
let enableOverride = null;
|
|
878
|
+
let namespacesCache;
|
|
879
|
+
let enabledCache;
|
|
880
|
+
function debug2(...args) {
|
|
881
|
+
if (!debug2.enabled) {
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
const self = debug2;
|
|
885
|
+
const curr = Number(/* @__PURE__ */ new Date());
|
|
886
|
+
const ms2 = curr - (prevTime || curr);
|
|
887
|
+
self.diff = ms2;
|
|
888
|
+
self.prev = prevTime;
|
|
889
|
+
self.curr = curr;
|
|
890
|
+
prevTime = curr;
|
|
891
|
+
args[0] = createDebug.coerce(args[0]);
|
|
892
|
+
if (typeof args[0] !== "string") {
|
|
893
|
+
args.unshift("%O");
|
|
894
|
+
}
|
|
895
|
+
let index = 0;
|
|
896
|
+
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
|
|
897
|
+
if (match === "%%") {
|
|
898
|
+
return "%";
|
|
899
|
+
}
|
|
900
|
+
index++;
|
|
901
|
+
const formatter = createDebug.formatters[format];
|
|
902
|
+
if (typeof formatter === "function") {
|
|
903
|
+
const val = args[index];
|
|
904
|
+
match = formatter.call(self, val);
|
|
905
|
+
args.splice(index, 1);
|
|
906
|
+
index--;
|
|
907
|
+
}
|
|
908
|
+
return match;
|
|
909
|
+
});
|
|
910
|
+
createDebug.formatArgs.call(self, args);
|
|
911
|
+
const logFn = self.log || createDebug.log;
|
|
912
|
+
logFn.apply(self, args);
|
|
913
|
+
}
|
|
914
|
+
debug2.namespace = namespace;
|
|
915
|
+
debug2.useColors = createDebug.useColors();
|
|
916
|
+
debug2.color = createDebug.selectColor(namespace);
|
|
917
|
+
debug2.extend = extend;
|
|
918
|
+
debug2.destroy = createDebug.destroy;
|
|
919
|
+
Object.defineProperty(debug2, "enabled", {
|
|
920
|
+
enumerable: true,
|
|
921
|
+
configurable: false,
|
|
922
|
+
get: () => {
|
|
923
|
+
if (enableOverride !== null) {
|
|
924
|
+
return enableOverride;
|
|
925
|
+
}
|
|
926
|
+
if (namespacesCache !== createDebug.namespaces) {
|
|
927
|
+
namespacesCache = createDebug.namespaces;
|
|
928
|
+
enabledCache = createDebug.enabled(namespace);
|
|
929
|
+
}
|
|
930
|
+
return enabledCache;
|
|
931
|
+
},
|
|
932
|
+
set: (v) => {
|
|
933
|
+
enableOverride = v;
|
|
934
|
+
}
|
|
935
|
+
});
|
|
936
|
+
if (typeof createDebug.init === "function") {
|
|
937
|
+
createDebug.init(debug2);
|
|
938
|
+
}
|
|
939
|
+
return debug2;
|
|
940
|
+
}
|
|
941
|
+
function extend(namespace, delimiter) {
|
|
942
|
+
const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
|
|
943
|
+
newDebug.log = this.log;
|
|
944
|
+
return newDebug;
|
|
945
|
+
}
|
|
946
|
+
function enable(namespaces) {
|
|
947
|
+
createDebug.save(namespaces);
|
|
948
|
+
createDebug.namespaces = namespaces;
|
|
949
|
+
createDebug.names = [];
|
|
950
|
+
createDebug.skips = [];
|
|
951
|
+
const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
|
|
952
|
+
for (const ns of split) {
|
|
953
|
+
if (ns[0] === "-") {
|
|
954
|
+
createDebug.skips.push(ns.slice(1));
|
|
955
|
+
} else {
|
|
956
|
+
createDebug.names.push(ns);
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
function matchesTemplate(search, template2) {
|
|
961
|
+
let searchIndex = 0;
|
|
962
|
+
let templateIndex = 0;
|
|
963
|
+
let starIndex = -1;
|
|
964
|
+
let matchIndex = 0;
|
|
965
|
+
while (searchIndex < search.length) {
|
|
966
|
+
if (templateIndex < template2.length && (template2[templateIndex] === search[searchIndex] || template2[templateIndex] === "*")) {
|
|
967
|
+
if (template2[templateIndex] === "*") {
|
|
968
|
+
starIndex = templateIndex;
|
|
969
|
+
matchIndex = searchIndex;
|
|
970
|
+
templateIndex++;
|
|
971
|
+
} else {
|
|
972
|
+
searchIndex++;
|
|
973
|
+
templateIndex++;
|
|
974
|
+
}
|
|
975
|
+
} else if (starIndex !== -1) {
|
|
976
|
+
templateIndex = starIndex + 1;
|
|
977
|
+
matchIndex++;
|
|
978
|
+
searchIndex = matchIndex;
|
|
979
|
+
} else {
|
|
980
|
+
return false;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
while (templateIndex < template2.length && template2[templateIndex] === "*") {
|
|
984
|
+
templateIndex++;
|
|
985
|
+
}
|
|
986
|
+
return templateIndex === template2.length;
|
|
987
|
+
}
|
|
988
|
+
function disable() {
|
|
989
|
+
const namespaces = [
|
|
990
|
+
...createDebug.names,
|
|
991
|
+
...createDebug.skips.map((namespace) => "-" + namespace)
|
|
992
|
+
].join(",");
|
|
993
|
+
createDebug.enable("");
|
|
994
|
+
return namespaces;
|
|
995
|
+
}
|
|
996
|
+
function enabled(name) {
|
|
997
|
+
for (const skip of createDebug.skips) {
|
|
998
|
+
if (matchesTemplate(name, skip)) {
|
|
999
|
+
return false;
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
for (const ns of createDebug.names) {
|
|
1003
|
+
if (matchesTemplate(name, ns)) {
|
|
1004
|
+
return true;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
return false;
|
|
1008
|
+
}
|
|
1009
|
+
function coerce(val) {
|
|
1010
|
+
if (val instanceof Error) {
|
|
1011
|
+
return val.stack || val.message;
|
|
1012
|
+
}
|
|
1013
|
+
return val;
|
|
1014
|
+
}
|
|
1015
|
+
function destroy() {
|
|
1016
|
+
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
1017
|
+
}
|
|
1018
|
+
createDebug.enable(createDebug.load());
|
|
1019
|
+
return createDebug;
|
|
1020
|
+
}
|
|
1021
|
+
common = setup;
|
|
1022
|
+
return common;
|
|
1023
|
+
}
|
|
1024
|
+
var hasRequiredBrowser;
|
|
1025
|
+
function requireBrowser() {
|
|
1026
|
+
if (hasRequiredBrowser) return browser.exports;
|
|
1027
|
+
hasRequiredBrowser = 1;
|
|
1028
|
+
(function(module, exports) {
|
|
1029
|
+
var define_process_env_default = {};
|
|
1030
|
+
exports.formatArgs = formatArgs;
|
|
1031
|
+
exports.save = save;
|
|
1032
|
+
exports.load = load;
|
|
1033
|
+
exports.useColors = useColors;
|
|
1034
|
+
exports.storage = localstorage();
|
|
1035
|
+
exports.destroy = /* @__PURE__ */ (() => {
|
|
1036
|
+
let warned = false;
|
|
1037
|
+
return () => {
|
|
1038
|
+
if (!warned) {
|
|
1039
|
+
warned = true;
|
|
1040
|
+
console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
|
|
1041
|
+
}
|
|
1042
|
+
};
|
|
1043
|
+
})();
|
|
1044
|
+
exports.colors = [
|
|
1045
|
+
"#0000CC",
|
|
1046
|
+
"#0000FF",
|
|
1047
|
+
"#0033CC",
|
|
1048
|
+
"#0033FF",
|
|
1049
|
+
"#0066CC",
|
|
1050
|
+
"#0066FF",
|
|
1051
|
+
"#0099CC",
|
|
1052
|
+
"#0099FF",
|
|
1053
|
+
"#00CC00",
|
|
1054
|
+
"#00CC33",
|
|
1055
|
+
"#00CC66",
|
|
1056
|
+
"#00CC99",
|
|
1057
|
+
"#00CCCC",
|
|
1058
|
+
"#00CCFF",
|
|
1059
|
+
"#3300CC",
|
|
1060
|
+
"#3300FF",
|
|
1061
|
+
"#3333CC",
|
|
1062
|
+
"#3333FF",
|
|
1063
|
+
"#3366CC",
|
|
1064
|
+
"#3366FF",
|
|
1065
|
+
"#3399CC",
|
|
1066
|
+
"#3399FF",
|
|
1067
|
+
"#33CC00",
|
|
1068
|
+
"#33CC33",
|
|
1069
|
+
"#33CC66",
|
|
1070
|
+
"#33CC99",
|
|
1071
|
+
"#33CCCC",
|
|
1072
|
+
"#33CCFF",
|
|
1073
|
+
"#6600CC",
|
|
1074
|
+
"#6600FF",
|
|
1075
|
+
"#6633CC",
|
|
1076
|
+
"#6633FF",
|
|
1077
|
+
"#66CC00",
|
|
1078
|
+
"#66CC33",
|
|
1079
|
+
"#9900CC",
|
|
1080
|
+
"#9900FF",
|
|
1081
|
+
"#9933CC",
|
|
1082
|
+
"#9933FF",
|
|
1083
|
+
"#99CC00",
|
|
1084
|
+
"#99CC33",
|
|
1085
|
+
"#CC0000",
|
|
1086
|
+
"#CC0033",
|
|
1087
|
+
"#CC0066",
|
|
1088
|
+
"#CC0099",
|
|
1089
|
+
"#CC00CC",
|
|
1090
|
+
"#CC00FF",
|
|
1091
|
+
"#CC3300",
|
|
1092
|
+
"#CC3333",
|
|
1093
|
+
"#CC3366",
|
|
1094
|
+
"#CC3399",
|
|
1095
|
+
"#CC33CC",
|
|
1096
|
+
"#CC33FF",
|
|
1097
|
+
"#CC6600",
|
|
1098
|
+
"#CC6633",
|
|
1099
|
+
"#CC9900",
|
|
1100
|
+
"#CC9933",
|
|
1101
|
+
"#CCCC00",
|
|
1102
|
+
"#CCCC33",
|
|
1103
|
+
"#FF0000",
|
|
1104
|
+
"#FF0033",
|
|
1105
|
+
"#FF0066",
|
|
1106
|
+
"#FF0099",
|
|
1107
|
+
"#FF00CC",
|
|
1108
|
+
"#FF00FF",
|
|
1109
|
+
"#FF3300",
|
|
1110
|
+
"#FF3333",
|
|
1111
|
+
"#FF3366",
|
|
1112
|
+
"#FF3399",
|
|
1113
|
+
"#FF33CC",
|
|
1114
|
+
"#FF33FF",
|
|
1115
|
+
"#FF6600",
|
|
1116
|
+
"#FF6633",
|
|
1117
|
+
"#FF9900",
|
|
1118
|
+
"#FF9933",
|
|
1119
|
+
"#FFCC00",
|
|
1120
|
+
"#FFCC33"
|
|
1121
|
+
];
|
|
1122
|
+
function useColors() {
|
|
1123
|
+
if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) {
|
|
1124
|
+
return true;
|
|
1125
|
+
}
|
|
1126
|
+
if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
let m;
|
|
1130
|
+
return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
|
|
1131
|
+
typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
|
|
1132
|
+
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
|
1133
|
+
typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
|
|
1134
|
+
typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
|
|
1135
|
+
}
|
|
1136
|
+
function formatArgs(args) {
|
|
1137
|
+
args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff);
|
|
1138
|
+
if (!this.useColors) {
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
const c = "color: " + this.color;
|
|
1142
|
+
args.splice(1, 0, c, "color: inherit");
|
|
1143
|
+
let index = 0;
|
|
1144
|
+
let lastC = 0;
|
|
1145
|
+
args[0].replace(/%[a-zA-Z%]/g, (match) => {
|
|
1146
|
+
if (match === "%%") {
|
|
1147
|
+
return;
|
|
1148
|
+
}
|
|
1149
|
+
index++;
|
|
1150
|
+
if (match === "%c") {
|
|
1151
|
+
lastC = index;
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
args.splice(lastC, 0, c);
|
|
1155
|
+
}
|
|
1156
|
+
exports.log = console.debug || console.log || (() => {
|
|
1157
|
+
});
|
|
1158
|
+
function save(namespaces) {
|
|
1159
|
+
try {
|
|
1160
|
+
if (namespaces) {
|
|
1161
|
+
exports.storage.setItem("debug", namespaces);
|
|
1162
|
+
} else {
|
|
1163
|
+
exports.storage.removeItem("debug");
|
|
1164
|
+
}
|
|
1165
|
+
} catch (error) {
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
function load() {
|
|
1169
|
+
let r;
|
|
1170
|
+
try {
|
|
1171
|
+
r = exports.storage.getItem("debug") || exports.storage.getItem("DEBUG");
|
|
1172
|
+
} catch (error) {
|
|
1173
|
+
}
|
|
1174
|
+
if (!r && typeof process !== "undefined" && "env" in process) {
|
|
1175
|
+
r = define_process_env_default.DEBUG;
|
|
1176
|
+
}
|
|
1177
|
+
return r;
|
|
1178
|
+
}
|
|
1179
|
+
function localstorage() {
|
|
1180
|
+
try {
|
|
1181
|
+
return localStorage;
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
}
|
|
1184
|
+
}
|
|
1185
|
+
module.exports = requireCommon()(exports);
|
|
1186
|
+
const { formatters } = module.exports;
|
|
1187
|
+
formatters.j = function(v) {
|
|
1188
|
+
try {
|
|
1189
|
+
return JSON.stringify(v);
|
|
1190
|
+
} catch (error) {
|
|
1191
|
+
return "[UnexpectedJSONParseError]: " + error.message;
|
|
1192
|
+
}
|
|
1193
|
+
};
|
|
1194
|
+
})(browser, browser.exports);
|
|
1195
|
+
return browser.exports;
|
|
1196
|
+
}
|
|
1197
|
+
var browserExports = requireBrowser();
|
|
1198
|
+
const debug = /* @__PURE__ */ getDefaultExportFromCjs(browserExports);
|
|
1199
|
+
debug("cabbages");
|
|
1200
|
+
const RepoContext = createContext(
|
|
1201
|
+
null
|
|
1202
|
+
);
|
|
1203
|
+
function createDocSignal(handle) {
|
|
1204
|
+
const [signal, setSignal] = createSignal(handle()?.doc());
|
|
1205
|
+
createEffect(() => {
|
|
1206
|
+
const h = handle();
|
|
1207
|
+
function update() {
|
|
1208
|
+
setSignal(() => h?.doc());
|
|
1209
|
+
}
|
|
1210
|
+
update();
|
|
1211
|
+
if (h) {
|
|
1212
|
+
h.on("change", update);
|
|
1213
|
+
onCleanup(() => h.off("change", update));
|
|
1214
|
+
}
|
|
1215
|
+
});
|
|
1216
|
+
return signal;
|
|
1217
|
+
}
|
|
1218
|
+
function resolveElement(source) {
|
|
1219
|
+
return typeof source === "function" ? source() : source;
|
|
1220
|
+
}
|
|
1221
|
+
function subscribe(element, selector, initialValue) {
|
|
1222
|
+
const [value, setValue] = createSignal(initialValue);
|
|
1223
|
+
onMount(() => {
|
|
1224
|
+
const target = resolveElement(element);
|
|
1225
|
+
if (!target)
|
|
1226
|
+
return;
|
|
1227
|
+
const unsubscribe = subscribe$1(target, selector, setValue);
|
|
1228
|
+
onCleanup(unsubscribe);
|
|
1229
|
+
});
|
|
1230
|
+
return value;
|
|
1231
|
+
}
|
|
1232
|
+
function subscribeDoc(element, selector) {
|
|
1233
|
+
const [handle, setHandle] = createSignal(void 0);
|
|
1234
|
+
onMount(() => {
|
|
1235
|
+
const target = resolveElement(element);
|
|
1236
|
+
if (!target)
|
|
1237
|
+
return;
|
|
1238
|
+
let canceled = false;
|
|
1239
|
+
const unsubscribe = subscribe$1(target, selector, (url) => {
|
|
1240
|
+
if (!url)
|
|
1241
|
+
return;
|
|
1242
|
+
const repo = "repo" in window ? window.repo : void 0;
|
|
1243
|
+
if (!repo)
|
|
1244
|
+
return;
|
|
1245
|
+
void Promise.resolve(repo.find(url)).then((h) => {
|
|
1246
|
+
if (canceled)
|
|
1247
|
+
return;
|
|
1248
|
+
setHandle(() => h);
|
|
1249
|
+
});
|
|
1250
|
+
});
|
|
1251
|
+
onCleanup(() => {
|
|
1252
|
+
canceled = true;
|
|
1253
|
+
unsubscribe();
|
|
1254
|
+
});
|
|
1255
|
+
});
|
|
1256
|
+
const [store, setStore] = createStore({});
|
|
1257
|
+
const [ready, setReady] = createSignal(false);
|
|
1258
|
+
createEffect(() => {
|
|
1259
|
+
const h = handle();
|
|
1260
|
+
if (!h) {
|
|
1261
|
+
setReady(false);
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
const sync = () => {
|
|
1265
|
+
setStore(reconcile(h.doc() ?? {}));
|
|
1266
|
+
setReady(true);
|
|
1267
|
+
};
|
|
1268
|
+
sync();
|
|
1269
|
+
h.on("change", sync);
|
|
1270
|
+
onCleanup(() => h.off("change", sync));
|
|
1271
|
+
});
|
|
1272
|
+
const doc = () => ready() ? store : void 0;
|
|
1273
|
+
return [doc, handle];
|
|
1274
|
+
}
|
|
1275
|
+
var _tmpl$ = /* @__PURE__ */ template(`<div class=drafts-list>`), _tmpl$2 = /* @__PURE__ */ template(`<div class=drafts-panel><div class=drafts-version>v0.0.45`), _tmpl$3 = /* @__PURE__ */ template(`<div class=drafts-empty>No document selected.`), _tmpl$4 = /* @__PURE__ */ template(`<button type=button class=draft-card-return title="Return to the latest version">Return to latest`), _tmpl$5 = /* @__PURE__ */ template(`<span class=draft-card-tools>`), _tmpl$6 = /* @__PURE__ */ template(`<div class=draft-card><div class=draft-card-header title="Main version (host document)"><div class=draft-card-title>`), _tmpl$7 = /* @__PURE__ */ template(`<div class=draft-card><div class=draft-card-header title="Open draft"><div class=draft-card-title>`), _tmpl$8 = /* @__PURE__ */ template(`<div class=draft-menu-backdrop>`), _tmpl$9 = /* @__PURE__ */ template(`<div class=draft-menu-list><button type=button class=draft-menu-item>Fork`), _tmpl$0 = /* @__PURE__ */ template(`<span class=draft-menu><button type=button class=draft-card-action title="Draft actions">`), _tmpl$1 = /* @__PURE__ */ template(`<button type=button class=draft-menu-item title="Fork a new draft from the version you're viewing">`), _tmpl$10 = /* @__PURE__ */ template(`<div class=draft-menu-separator>`), _tmpl$11 = /* @__PURE__ */ template(`<button type=button class=draft-menu-item title="Merge this draft into the highlighted card and hide it">`), _tmpl$12 = /* @__PURE__ */ template(`<button type=button class="draft-menu-item draft-menu-item--danger"title="Deletes this draft and any drafts forked from it">Delete draft`), _tmpl$13 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><line x1=6 x2=6 y1=3 y2=15></line><circle cx=18 cy=6 r=3></circle><circle cx=6 cy=18 r=3></circle><path d="M18 9a9 9 0 0 1-9 9">`), _tmpl$14 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><circle cx=18 cy=18 r=3></circle><circle cx=6 cy=6 r=3></circle><path d="M6 21V9a9 9 0 0 0 9 9">`), _tmpl$15 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><circle cx=12 cy=12 r=1></circle><circle cx=19 cy=12 r=1></circle><circle cx=5 cy=12 r=1>`), _tmpl$16 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M3 6h18"></path><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"></path><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"></path><line x1=10 x2=10 y1=11 y2=17></line><line x1=14 x2=14 y1=11 y2=17>`), _tmpl$17 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path><path d="M3 3v5h5"></path><path d="M12 7v5l4 2">`), _tmpl$18 = /* @__PURE__ */ template(`<input class=draft-name-input>`), _tmpl$19 = /* @__PURE__ */ template(`<span class=draft-name title="Double-click to rename">`), _tmpl$20 = /* @__PURE__ */ template(`<button type=button class=draft-card-eye>`), _tmpl$21 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"></path><circle cx=12 cy=12 r=3>`), _tmpl$22 = /* @__PURE__ */ template(`<svg width=14 height=14 viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.45 18.45 0 0 1 5.06-5.94M9.9 4.24A9.12 9.12 0 0 1 12 4c7 0 11 8 11 8a18.5 18.5 0 0 1-2.16 3.19m-6.72-1.07a3 3 0 1 1-4.24-4.24"></path><line x1=1 y1=1 x2=23 y2=23>`), _tmpl$23 = /* @__PURE__ */ template(`<div class=draft-changes-building><span class=draft-building-spinner></span>Building history…`), _tmpl$24 = /* @__PURE__ */ template(`<div class=draft-scrubber-token><div class=draft-scrubber-line></div><div class=draft-scrubber-edge title="Drag to scrub through history — drop at the top to return to the latest version"></div><div class=draft-scrubber-dot title="Drag to scrub through history — drop at the top to return to the latest version">`), _tmpl$25 = /* @__PURE__ */ template(`<div class=draft-scrubber-connector>`), _tmpl$26 = /* @__PURE__ */ template(`<div class="draft-scrubber-token draft-scrubber-token--baseline"><div class=draft-scrubber-line></div><div class=draft-scrubber-edge title="Drag to move the diff baseline"></div><div class=draft-scrubber-baseline title="Drag to move the diff baseline">`), _tmpl$27 = /* @__PURE__ */ template(`<div class=draft-changes-body><div class=draft-scrubber></div><div class=draft-changes-rows>`), _tmpl$28 = /* @__PURE__ */ template(`<div class=draft-card-changes>`), _tmpl$29 = /* @__PURE__ */ template(`<div class=draft-changes-empty>No changes yet.`), _tmpl$30 = /* @__PURE__ */ template(`<div class=draft-scrubber-sticker title="The version being viewed — Fork below to branch from here"><span class=draft-sticker-time></span><span class=draft-sticker-spacer>`), _tmpl$31 = /* @__PURE__ */ template(`<button type=button class=draft-group-row title="View the draft as of this group"><span class=draft-group-time></span><span class=draft-group-spacer>`), _tmpl$32 = /* @__PURE__ */ template(`<div class="draft-avatar draft-avatar--extra">+`), _tmpl$33 = /* @__PURE__ */ template(`<div class=draft-avatars>`), _tmpl$34 = /* @__PURE__ */ template(`<patchwork-view tool-id=contact-inline>`, true, false, false), _tmpl$35 = /* @__PURE__ */ template(`<div class=draft-avatar>`), _tmpl$36 = /* @__PURE__ */ template(`<span class="draft-count draft-count--add">+`), _tmpl$37 = /* @__PURE__ */ template(`<span class="draft-count draft-count--del">-`), _tmpl$38 = /* @__PURE__ */ template(`<span class=draft-counts>`);
|
|
1276
|
+
const EMPTY_DRAFT_LIST = {
|
|
1277
|
+
main: {
|
|
1278
|
+
url: "",
|
|
1279
|
+
parent: null,
|
|
1280
|
+
members: [],
|
|
1281
|
+
childCount: 0,
|
|
1282
|
+
name: null,
|
|
1283
|
+
changeGroupDocUrl: null
|
|
1284
|
+
},
|
|
1285
|
+
drafts: [],
|
|
1286
|
+
actorAttributionUrl: null
|
|
1287
|
+
};
|
|
1288
|
+
const DRAFTS_VERSION = "0.0.45";
|
|
1289
|
+
console.log(`[drafts] DraftsSidebar v${DRAFTS_VERSION} loaded`);
|
|
1290
|
+
function DraftsSidebar(props) {
|
|
1291
|
+
const [hostDoc, hostDocHandle] = subscribeDoc(props.element, {
|
|
1292
|
+
type: "draft:root-doc"
|
|
1293
|
+
});
|
|
1294
|
+
const [, checkedOutHandle] = subscribeDoc(props.element, {
|
|
1295
|
+
type: "draft:checked-out"
|
|
1296
|
+
});
|
|
1297
|
+
const checkedOut = createDocSignal(checkedOutHandle);
|
|
1298
|
+
const selected = createMemo(() => checkedOut()?.checkedOut ?? null);
|
|
1299
|
+
const eyeOpen = createMemo(() => {
|
|
1300
|
+
const at = checkedOut()?.at;
|
|
1301
|
+
return !!at && Object.values(at).some((e) => e.from !== void 0);
|
|
1302
|
+
});
|
|
1303
|
+
const isPinned = createMemo(() => {
|
|
1304
|
+
const at = checkedOut()?.at;
|
|
1305
|
+
return !!at && Object.values(at).some((e) => e.to !== void 0);
|
|
1306
|
+
});
|
|
1307
|
+
const [scrubber, setScrubber] = createSignal(null);
|
|
1308
|
+
const [baseliner, setBaseliner] = createSignal(null);
|
|
1309
|
+
const list = subscribe(props.element, {
|
|
1310
|
+
type: "draft:list"
|
|
1311
|
+
}, EMPTY_DRAFT_LIST);
|
|
1312
|
+
createEffect(() => {
|
|
1313
|
+
const url = list().actorAttributionUrl;
|
|
1314
|
+
const repo = getRepo();
|
|
1315
|
+
if (!url || !repo) return;
|
|
1316
|
+
let disposed = false;
|
|
1317
|
+
let off = null;
|
|
1318
|
+
void repo.find(url).then((handle) => {
|
|
1319
|
+
if (disposed) return;
|
|
1320
|
+
const update = () => {
|
|
1321
|
+
const actors = handle.doc()?.actors;
|
|
1322
|
+
if (actors && Object.keys(actors).length > 0) {
|
|
1323
|
+
setActorContacts((prev) => ({
|
|
1324
|
+
...prev,
|
|
1325
|
+
...actors
|
|
1326
|
+
}));
|
|
1327
|
+
}
|
|
1328
|
+
};
|
|
1329
|
+
handle.on("change", update);
|
|
1330
|
+
off = () => handle.off("change", update);
|
|
1331
|
+
update();
|
|
1332
|
+
}, (err) => {
|
|
1333
|
+
console.warn("[drafts] failed to load actor attribution:", url, err);
|
|
1334
|
+
});
|
|
1335
|
+
onCleanup(() => {
|
|
1336
|
+
disposed = true;
|
|
1337
|
+
off?.();
|
|
1338
|
+
});
|
|
1339
|
+
});
|
|
1340
|
+
const isMainSelected = createMemo(() => selected() === null);
|
|
1341
|
+
const isFolder = createMemo(() => hostDoc()?.["@patchwork"]?.type === "folder");
|
|
1342
|
+
const selectDraft = (url) => {
|
|
1343
|
+
const handle = checkedOutHandle();
|
|
1344
|
+
if (!handle) return;
|
|
1345
|
+
setScrubber(null);
|
|
1346
|
+
setBaseliner(null);
|
|
1347
|
+
handle.change((d) => {
|
|
1348
|
+
d.checkedOut = url;
|
|
1349
|
+
d.at = null;
|
|
1350
|
+
});
|
|
1351
|
+
};
|
|
1352
|
+
const getRepo = () => "repo" in window ? window.repo : void 0;
|
|
1353
|
+
let scrubSeq = 0;
|
|
1354
|
+
const applyPins = (draftUrl, members) => {
|
|
1355
|
+
const handle = checkedOutHandle();
|
|
1356
|
+
const repo = getRepo();
|
|
1357
|
+
if (!handle || !repo) return;
|
|
1358
|
+
const head = scrubber();
|
|
1359
|
+
if (!head) return;
|
|
1360
|
+
const bl = baseliner();
|
|
1361
|
+
const base = bl ? {
|
|
1362
|
+
beforeTime: bl.time
|
|
1363
|
+
} : "none";
|
|
1364
|
+
const seq = ++scrubSeq;
|
|
1365
|
+
void (async () => {
|
|
1366
|
+
const checkpoint = await computeCheckpoint(repo, members, head.head, base);
|
|
1367
|
+
if (seq !== scrubSeq) return;
|
|
1368
|
+
handle.change((d) => {
|
|
1369
|
+
d.checkedOut = draftUrl;
|
|
1370
|
+
d.at = checkpoint;
|
|
1371
|
+
});
|
|
1372
|
+
})();
|
|
1373
|
+
};
|
|
1374
|
+
const onScrub = (draftUrl, members, scrub) => {
|
|
1375
|
+
setScrubber(scrub);
|
|
1376
|
+
applyPins(draftUrl, members);
|
|
1377
|
+
};
|
|
1378
|
+
const onBaselineScrub = (draftUrl, members, base) => {
|
|
1379
|
+
setBaseliner(base);
|
|
1380
|
+
applyPins(draftUrl, members);
|
|
1381
|
+
};
|
|
1382
|
+
const membersFor = (draftUrl) => draftUrl ? list().drafts.find((s) => s.url === draftUrl)?.members ?? [] : list().main.members;
|
|
1383
|
+
const forkBaselines = (draftUrl) => {
|
|
1384
|
+
const at = {};
|
|
1385
|
+
for (const member of membersFor(draftUrl)) {
|
|
1386
|
+
if (member.clonedAt) at[member.url] = {
|
|
1387
|
+
from: member.clonedAt
|
|
1388
|
+
};
|
|
1389
|
+
}
|
|
1390
|
+
return at;
|
|
1391
|
+
};
|
|
1392
|
+
const clearCheckpoint = () => {
|
|
1393
|
+
const handle = checkedOutHandle();
|
|
1394
|
+
if (!handle) return;
|
|
1395
|
+
setScrubber(null);
|
|
1396
|
+
setBaseliner(null);
|
|
1397
|
+
const draftUrl = selected();
|
|
1398
|
+
const baselines = eyeOpen() && draftUrl ? forkBaselines(draftUrl) : null;
|
|
1399
|
+
handle.change((d) => {
|
|
1400
|
+
d.at = baselines && Object.keys(baselines).length > 0 ? baselines : null;
|
|
1401
|
+
});
|
|
1402
|
+
};
|
|
1403
|
+
const toggleEye = () => {
|
|
1404
|
+
const handle = checkedOutHandle();
|
|
1405
|
+
const repo = getRepo();
|
|
1406
|
+
if (!handle || !repo) return;
|
|
1407
|
+
if (eyeOpen()) {
|
|
1408
|
+
setBaseliner(null);
|
|
1409
|
+
handle.change((d) => {
|
|
1410
|
+
if (!d.at) return;
|
|
1411
|
+
const urls = Object.keys(d.at);
|
|
1412
|
+
if (!urls.some((u) => d.at?.[u]?.to !== void 0)) {
|
|
1413
|
+
d.at = null;
|
|
1414
|
+
return;
|
|
1415
|
+
}
|
|
1416
|
+
for (const url of urls) {
|
|
1417
|
+
const entry = d.at[url];
|
|
1418
|
+
if (!entry) continue;
|
|
1419
|
+
if (entry.to === void 0) delete d.at[url];
|
|
1420
|
+
else delete entry.from;
|
|
1421
|
+
}
|
|
1422
|
+
});
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
const draftUrl = selected();
|
|
1426
|
+
const s = scrubber();
|
|
1427
|
+
if (isPinned() && s) {
|
|
1428
|
+
setBaseliner({
|
|
1429
|
+
groupId: s.groupId,
|
|
1430
|
+
offset: BASELINE_GROUP_START,
|
|
1431
|
+
time: s.groupStartTime
|
|
1432
|
+
});
|
|
1433
|
+
const members = membersFor(draftUrl);
|
|
1434
|
+
const seq = ++scrubSeq;
|
|
1435
|
+
void (async () => {
|
|
1436
|
+
const checkpoint = await computeCheckpoint(repo, members, s.head, {
|
|
1437
|
+
beforeTime: s.groupStartTime
|
|
1438
|
+
});
|
|
1439
|
+
if (seq !== scrubSeq) return;
|
|
1440
|
+
handle.change((d) => {
|
|
1441
|
+
d.at = checkpoint;
|
|
1442
|
+
});
|
|
1443
|
+
})();
|
|
1444
|
+
return;
|
|
1445
|
+
}
|
|
1446
|
+
if (isPinned()) {
|
|
1447
|
+
const clonedAt = new Map(membersFor(draftUrl).map((m) => [m.url, m.clonedAt]));
|
|
1448
|
+
handle.change((d) => {
|
|
1449
|
+
if (!d.at) return;
|
|
1450
|
+
for (const url of Object.keys(d.at)) {
|
|
1451
|
+
const entry = d.at[url];
|
|
1452
|
+
if (!entry) continue;
|
|
1453
|
+
entry.from = clonedAt.get(url) ?? encodeHeads([]);
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
return;
|
|
1457
|
+
}
|
|
1458
|
+
if (!draftUrl) return;
|
|
1459
|
+
const baselines = forkBaselines(draftUrl);
|
|
1460
|
+
if (Object.keys(baselines).length === 0) return;
|
|
1461
|
+
handle.change((d) => {
|
|
1462
|
+
d.at = baselines;
|
|
1463
|
+
});
|
|
1464
|
+
};
|
|
1465
|
+
createEffect(() => {
|
|
1466
|
+
const at = checkedOut()?.at;
|
|
1467
|
+
const draftUrl = selected();
|
|
1468
|
+
const handle = checkedOutHandle();
|
|
1469
|
+
if (!at || !draftUrl || !handle) return;
|
|
1470
|
+
const entries = Object.values(at);
|
|
1471
|
+
if (entries.length === 0) return;
|
|
1472
|
+
if (entries.some((e) => e.to !== void 0)) return;
|
|
1473
|
+
if (!entries.some((e) => e.from !== void 0)) return;
|
|
1474
|
+
const missing = membersFor(draftUrl).filter((m) => m.clonedAt !== null && !at[m.url]);
|
|
1475
|
+
if (missing.length === 0) return;
|
|
1476
|
+
handle.change((d) => {
|
|
1477
|
+
if (!d.at) return;
|
|
1478
|
+
for (const m of missing) {
|
|
1479
|
+
if (m.clonedAt) d.at[m.url] = {
|
|
1480
|
+
from: m.clonedAt
|
|
1481
|
+
};
|
|
1482
|
+
}
|
|
1483
|
+
});
|
|
1484
|
+
});
|
|
1485
|
+
const onForkSelection = async (atVersion) => {
|
|
1486
|
+
if (isFolder()) return;
|
|
1487
|
+
const docHandle = hostDocHandle();
|
|
1488
|
+
if (!docHandle) return;
|
|
1489
|
+
const repo = getRepo();
|
|
1490
|
+
if (!repo) {
|
|
1491
|
+
console.warn("[drafts] window.repo is not set");
|
|
1492
|
+
return;
|
|
1493
|
+
}
|
|
1494
|
+
const parentUrl = selected();
|
|
1495
|
+
const members = membersFor(parentUrl);
|
|
1496
|
+
const head = atVersion ? scrubber()?.head ?? null : null;
|
|
1497
|
+
const clones = {};
|
|
1498
|
+
if (head) {
|
|
1499
|
+
const checkpoint = await computeCheckpoint(repo, members, head, "none");
|
|
1500
|
+
for (const member of members) {
|
|
1501
|
+
const to = checkpoint[member.url]?.to;
|
|
1502
|
+
if (!to) continue;
|
|
1503
|
+
let handle = null;
|
|
1504
|
+
try {
|
|
1505
|
+
handle = await repo.find(member.cloneUrl ?? member.url);
|
|
1506
|
+
const clone = cloneAtVersion(repo, handle, to);
|
|
1507
|
+
clones[member.url] = {
|
|
1508
|
+
cloneUrl: clone.url,
|
|
1509
|
+
clonedAt: to
|
|
1510
|
+
};
|
|
1511
|
+
} catch (err) {
|
|
1512
|
+
reportForkFailure(handle ? collectForkDiagnostic(handle, member, to) : null, err);
|
|
1513
|
+
}
|
|
1514
|
+
}
|
|
1515
|
+
} else if (parentUrl) {
|
|
1516
|
+
for (const member of members) {
|
|
1517
|
+
try {
|
|
1518
|
+
const source = await repo.find(member.cloneUrl ?? member.url);
|
|
1519
|
+
const clonedAt = source.heads();
|
|
1520
|
+
const clone = repo.clone(source);
|
|
1521
|
+
clones[member.url] = {
|
|
1522
|
+
cloneUrl: clone.url,
|
|
1523
|
+
clonedAt
|
|
1524
|
+
};
|
|
1525
|
+
} catch (err) {
|
|
1526
|
+
console.warn("[drafts] failed to fork member:", member, err);
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
}
|
|
1530
|
+
const mainDraft = await ensureMainDraft(repo, docHandle);
|
|
1531
|
+
const parentHandle = parentUrl ? await repo.find(parentUrl) : mainDraft;
|
|
1532
|
+
let draftNumber = 1;
|
|
1533
|
+
mainDraft.change((d) => {
|
|
1534
|
+
d.draftCounter = (d.draftCounter ?? 0) + 1;
|
|
1535
|
+
draftNumber = d.draftCounter;
|
|
1536
|
+
});
|
|
1537
|
+
const draft = repo.create({
|
|
1538
|
+
"@patchwork": {
|
|
1539
|
+
type: "draft"
|
|
1540
|
+
},
|
|
1541
|
+
name: `Draft ${draftNumber}`,
|
|
1542
|
+
parent: parentHandle.url,
|
|
1543
|
+
drafts: [],
|
|
1544
|
+
clones
|
|
1545
|
+
});
|
|
1546
|
+
parentHandle.change((d) => {
|
|
1547
|
+
d.drafts.push(draft.url);
|
|
1548
|
+
});
|
|
1549
|
+
selectDraft(draft.url);
|
|
1550
|
+
};
|
|
1551
|
+
const onRename = async (url, name) => {
|
|
1552
|
+
const repo = getRepo();
|
|
1553
|
+
if (!repo) return;
|
|
1554
|
+
let handle;
|
|
1555
|
+
if (url === null) {
|
|
1556
|
+
const docHandle = hostDocHandle();
|
|
1557
|
+
if (!docHandle) return;
|
|
1558
|
+
handle = await ensureMainDraft(repo, docHandle);
|
|
1559
|
+
} else {
|
|
1560
|
+
handle = await repo.find(url);
|
|
1561
|
+
}
|
|
1562
|
+
handle.change((d) => {
|
|
1563
|
+
if (name) d.name = name;
|
|
1564
|
+
else delete d.name;
|
|
1565
|
+
});
|
|
1566
|
+
};
|
|
1567
|
+
const selectedSummary = createMemo(() => list().drafts.find((s) => s.url === selected()) ?? null);
|
|
1568
|
+
const mergeParentUrl = createMemo(() => selectedSummary()?.parent ?? null);
|
|
1569
|
+
const mergeTargetName = createMemo(() => {
|
|
1570
|
+
const parentUrl = mergeParentUrl();
|
|
1571
|
+
if (parentUrl === null || parentUrl === list().main.url) {
|
|
1572
|
+
return list().main.name ?? "Main";
|
|
1573
|
+
}
|
|
1574
|
+
return list().drafts.find((s) => s.url === parentUrl)?.name ?? "Draft";
|
|
1575
|
+
});
|
|
1576
|
+
const forkAtLabel = createMemo(() => {
|
|
1577
|
+
if (!isPinned()) return null;
|
|
1578
|
+
const time = scrubber()?.head.time;
|
|
1579
|
+
if (!time) return null;
|
|
1580
|
+
return `Fork from ${formatVersionTime(time)}`;
|
|
1581
|
+
});
|
|
1582
|
+
const [mergeHighlight, setMergeHighlight] = createSignal(false);
|
|
1583
|
+
const draftDepth = (summary) => {
|
|
1584
|
+
const byUrl = new Map(list().drafts.map((s) => [s.url, s]));
|
|
1585
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1586
|
+
let depth = 0;
|
|
1587
|
+
let parent = summary.parent;
|
|
1588
|
+
while (parent && byUrl.has(parent) && !seen.has(parent)) {
|
|
1589
|
+
seen.add(parent);
|
|
1590
|
+
depth++;
|
|
1591
|
+
parent = byUrl.get(parent).parent;
|
|
1592
|
+
}
|
|
1593
|
+
return depth;
|
|
1594
|
+
};
|
|
1595
|
+
const onMergeDraft = async () => {
|
|
1596
|
+
const draftUrl = selected();
|
|
1597
|
+
if (!draftUrl) return;
|
|
1598
|
+
const parentUrl = mergeParentUrl();
|
|
1599
|
+
if (!window.confirm(`Merge this draft into "${mergeTargetName()}"?`)) return;
|
|
1600
|
+
const repo = getRepo();
|
|
1601
|
+
if (!repo) {
|
|
1602
|
+
console.warn("[drafts] window.repo is not set");
|
|
1603
|
+
return;
|
|
1604
|
+
}
|
|
1605
|
+
const draftHandle = await repo.find(draftUrl);
|
|
1606
|
+
await mergeDraft(repo, draftHandle);
|
|
1607
|
+
selectDraft(parentUrl && parentUrl !== list().main.url ? parentUrl : null);
|
|
1608
|
+
};
|
|
1609
|
+
const onDeleteDraft = async () => {
|
|
1610
|
+
const draftUrl = selected();
|
|
1611
|
+
if (!draftUrl) return;
|
|
1612
|
+
const childCount = selectedSummary()?.childCount ?? 0;
|
|
1613
|
+
const warning = childCount > 0 ? "Delete this draft? Drafts forked from it will be deleted too. This can't be undone." : "Delete this draft? This can't be undone.";
|
|
1614
|
+
if (!window.confirm(warning)) return;
|
|
1615
|
+
const repo = getRepo();
|
|
1616
|
+
if (!repo) {
|
|
1617
|
+
console.warn("[drafts] window.repo is not set");
|
|
1618
|
+
return;
|
|
1619
|
+
}
|
|
1620
|
+
const parentUrl = mergeParentUrl();
|
|
1621
|
+
let parentHandle;
|
|
1622
|
+
if (parentUrl) {
|
|
1623
|
+
parentHandle = await repo.find(parentUrl);
|
|
1624
|
+
} else {
|
|
1625
|
+
const docHandle = hostDocHandle();
|
|
1626
|
+
if (!docHandle) return;
|
|
1627
|
+
parentHandle = await ensureMainDraft(repo, docHandle);
|
|
1628
|
+
}
|
|
1629
|
+
parentHandle.change((d) => {
|
|
1630
|
+
const i = d.drafts.indexOf(draftUrl);
|
|
1631
|
+
if (i >= 0) d.drafts.splice(i, 1);
|
|
1632
|
+
});
|
|
1633
|
+
selectDraft(parentUrl && parentUrl !== list().main.url ? parentUrl : null);
|
|
1634
|
+
};
|
|
1635
|
+
return (() => {
|
|
1636
|
+
var _el$ = _tmpl$2(), _el$3 = _el$.firstChild;
|
|
1637
|
+
insert(_el$, createComponent(Show, {
|
|
1638
|
+
get when() {
|
|
1639
|
+
return hostDoc();
|
|
1640
|
+
},
|
|
1641
|
+
get fallback() {
|
|
1642
|
+
return _tmpl$3();
|
|
1643
|
+
},
|
|
1644
|
+
get children() {
|
|
1645
|
+
var _el$2 = _tmpl$();
|
|
1646
|
+
insert(_el$2, createComponent(MainCard, {
|
|
1647
|
+
get hostDocUrl() {
|
|
1648
|
+
return hostDocHandle()?.url;
|
|
1649
|
+
},
|
|
1650
|
+
get isSelected() {
|
|
1651
|
+
return isMainSelected();
|
|
1652
|
+
},
|
|
1653
|
+
members: () => list().main.members,
|
|
1654
|
+
get changeGroupDocUrl() {
|
|
1655
|
+
return list().main.changeGroupDocUrl;
|
|
1656
|
+
},
|
|
1657
|
+
get name() {
|
|
1658
|
+
return list().main.name;
|
|
1659
|
+
},
|
|
1660
|
+
onRename: (name) => void onRename(null, name),
|
|
1661
|
+
onSelect: () => selectDraft(null),
|
|
1662
|
+
onScrub: (scrub) => onScrub(null, list().main.members, scrub),
|
|
1663
|
+
scrubber: () => isMainSelected() ? scrubber() : null,
|
|
1664
|
+
onBaselineScrub: (base) => onBaselineScrub(null, list().main.members, base),
|
|
1665
|
+
baseliner: () => isMainSelected() ? baseliner() : null,
|
|
1666
|
+
checkpoint: () => isMainSelected() ? checkedOut()?.at ?? null : null,
|
|
1667
|
+
get hasCheckpoint() {
|
|
1668
|
+
return memo(() => !!isMainSelected())() && isPinned();
|
|
1669
|
+
},
|
|
1670
|
+
onReturnToLatest: clearCheckpoint,
|
|
1671
|
+
get eyeOpen() {
|
|
1672
|
+
return memo(() => !!isMainSelected())() && eyeOpen();
|
|
1673
|
+
},
|
|
1674
|
+
get eyeDisabled() {
|
|
1675
|
+
return !isPinned();
|
|
1676
|
+
},
|
|
1677
|
+
onToggleEye: toggleEye,
|
|
1678
|
+
get forkDisabled() {
|
|
1679
|
+
return isFolder();
|
|
1680
|
+
},
|
|
1681
|
+
onFork: () => void onForkSelection(false),
|
|
1682
|
+
get forkAtLabel() {
|
|
1683
|
+
return forkAtLabel();
|
|
1684
|
+
},
|
|
1685
|
+
onForkAt: () => void onForkSelection(true),
|
|
1686
|
+
get isMergeTarget() {
|
|
1687
|
+
return memo(() => !!mergeHighlight())() && mergeParentUrl() === list().main.url;
|
|
1688
|
+
}
|
|
1689
|
+
}), null);
|
|
1690
|
+
insert(_el$2, createComponent(For, {
|
|
1691
|
+
get each() {
|
|
1692
|
+
return list().drafts;
|
|
1693
|
+
},
|
|
1694
|
+
children: (summary) => createComponent(DraftCard, {
|
|
1695
|
+
get url() {
|
|
1696
|
+
return summary.url;
|
|
1697
|
+
},
|
|
1698
|
+
get members() {
|
|
1699
|
+
return summary.members;
|
|
1700
|
+
},
|
|
1701
|
+
get changeGroupDocUrl() {
|
|
1702
|
+
return summary.changeGroupDocUrl;
|
|
1703
|
+
},
|
|
1704
|
+
get mainDocUrl() {
|
|
1705
|
+
return hostDocHandle()?.url;
|
|
1706
|
+
},
|
|
1707
|
+
get isSelected() {
|
|
1708
|
+
return selected() === summary.url;
|
|
1709
|
+
},
|
|
1710
|
+
get name() {
|
|
1711
|
+
return summary.name;
|
|
1712
|
+
},
|
|
1713
|
+
get depth() {
|
|
1714
|
+
return draftDepth(summary);
|
|
1715
|
+
},
|
|
1716
|
+
onRename: (name) => void onRename(summary.url, name),
|
|
1717
|
+
onSelect: selectDraft,
|
|
1718
|
+
onScrub: (scrub) => onScrub(summary.url, summary.members, scrub),
|
|
1719
|
+
scrubber: () => selected() === summary.url ? scrubber() : null,
|
|
1720
|
+
onBaselineScrub: (base) => onBaselineScrub(summary.url, summary.members, base),
|
|
1721
|
+
baseliner: () => selected() === summary.url ? baseliner() : null,
|
|
1722
|
+
checkpoint: () => selected() === summary.url ? checkedOut()?.at ?? null : null,
|
|
1723
|
+
get hasCheckpoint() {
|
|
1724
|
+
return memo(() => selected() === summary.url)() && isPinned();
|
|
1725
|
+
},
|
|
1726
|
+
onReturnToLatest: clearCheckpoint,
|
|
1727
|
+
get eyeOpen() {
|
|
1728
|
+
return memo(() => selected() === summary.url)() && eyeOpen();
|
|
1729
|
+
},
|
|
1730
|
+
eyeDisabled: false,
|
|
1731
|
+
onToggleEye: toggleEye,
|
|
1732
|
+
onFork: () => void onForkSelection(false),
|
|
1733
|
+
get forkAtLabel() {
|
|
1734
|
+
return forkAtLabel();
|
|
1735
|
+
},
|
|
1736
|
+
onForkAt: () => void onForkSelection(true),
|
|
1737
|
+
get mergeLabel() {
|
|
1738
|
+
return `Merge into "${mergeTargetName()}"`;
|
|
1739
|
+
},
|
|
1740
|
+
onMerge: () => void onMergeDraft(),
|
|
1741
|
+
onMergeHover: setMergeHighlight,
|
|
1742
|
+
onDelete: () => void onDeleteDraft(),
|
|
1743
|
+
get isMergeTarget() {
|
|
1744
|
+
return memo(() => !!mergeHighlight())() && mergeParentUrl() === summary.url;
|
|
1745
|
+
}
|
|
1746
|
+
})
|
|
1747
|
+
}), null);
|
|
1748
|
+
return _el$2;
|
|
1749
|
+
}
|
|
1750
|
+
}), _el$3);
|
|
1751
|
+
return _el$;
|
|
1752
|
+
})();
|
|
1753
|
+
}
|
|
1754
|
+
async function mergeDraft(repo, draftHandle) {
|
|
1755
|
+
const doc = draftHandle.doc();
|
|
1756
|
+
const parentHandle = await findMergeTarget(repo, doc?.parent);
|
|
1757
|
+
const parentClones = parentHandle?.doc()?.clones ?? {};
|
|
1758
|
+
const entries = Object.entries(doc?.clones ?? {});
|
|
1759
|
+
for (const [originalUrl, entry] of entries) {
|
|
1760
|
+
const targetUrl = parentClones[originalUrl]?.cloneUrl ?? originalUrl;
|
|
1761
|
+
if (entry.cloneUrl === targetUrl) continue;
|
|
1762
|
+
const [target, clone] = await Promise.all([repo.find(targetUrl), repo.find(entry.cloneUrl)]);
|
|
1763
|
+
target.merge(clone);
|
|
1764
|
+
const mergedAt = target.heads();
|
|
1765
|
+
draftHandle.change((d) => {
|
|
1766
|
+
const e = d.clones[originalUrl];
|
|
1767
|
+
if (e) e.mergedAt = mergedAt;
|
|
1768
|
+
});
|
|
1769
|
+
}
|
|
1770
|
+
draftHandle.change((d) => {
|
|
1771
|
+
d.mergedAt = Date.now();
|
|
1772
|
+
});
|
|
1773
|
+
if (parentHandle) {
|
|
1774
|
+
const children = (draftHandle.doc()?.drafts ?? []).filter(isValidAutomergeUrl);
|
|
1775
|
+
for (const childUrl of children) {
|
|
1776
|
+
try {
|
|
1777
|
+
const child = await repo.find(childUrl);
|
|
1778
|
+
child.change((d) => {
|
|
1779
|
+
d.parent = parentHandle.url;
|
|
1780
|
+
});
|
|
1781
|
+
parentHandle.change((d) => {
|
|
1782
|
+
if (!d.drafts.includes(childUrl)) d.drafts.push(childUrl);
|
|
1783
|
+
});
|
|
1784
|
+
draftHandle.change((d) => {
|
|
1785
|
+
const i = d.drafts.indexOf(childUrl);
|
|
1786
|
+
if (i >= 0) d.drafts.splice(i, 1);
|
|
1787
|
+
});
|
|
1788
|
+
} catch (err) {
|
|
1789
|
+
console.warn("[drafts] failed to re-parent child draft after merge:", childUrl, err);
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
}
|
|
1794
|
+
async function findMergeTarget(repo, parentUrl) {
|
|
1795
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1796
|
+
let cursor = parentUrl;
|
|
1797
|
+
while (cursor && isValidAutomergeUrl(cursor) && !seen.has(cursor)) {
|
|
1798
|
+
seen.add(cursor);
|
|
1799
|
+
try {
|
|
1800
|
+
const candidate = await repo.find(cursor);
|
|
1801
|
+
if (candidate.doc()?.mergedAt === void 0) return candidate;
|
|
1802
|
+
cursor = candidate.doc()?.parent;
|
|
1803
|
+
} catch (err) {
|
|
1804
|
+
console.warn("[drafts] failed to load ancestor draft for merge; falling back to merging into the originals:", cursor, err);
|
|
1805
|
+
return null;
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
return null;
|
|
1809
|
+
}
|
|
1810
|
+
function cloneAtVersion(repo, handle, to) {
|
|
1811
|
+
const doc = handle.doc();
|
|
1812
|
+
const pinHeads = decodeHeads(to);
|
|
1813
|
+
const metas = Automerge.getChangesMetaSince(doc, []);
|
|
1814
|
+
const byHash = new Map(metas.map((m) => [m.hash, m]));
|
|
1815
|
+
const closure = /* @__PURE__ */ new Set();
|
|
1816
|
+
const stack = [...pinHeads];
|
|
1817
|
+
while (stack.length > 0) {
|
|
1818
|
+
const hash = stack.pop();
|
|
1819
|
+
if (closure.has(hash)) continue;
|
|
1820
|
+
const meta = byHash.get(hash);
|
|
1821
|
+
if (!meta) {
|
|
1822
|
+
throw new Error(`[drafts] change ${hash} is not in the doc's history metadata`);
|
|
1823
|
+
}
|
|
1824
|
+
closure.add(hash);
|
|
1825
|
+
stack.push(...meta.deps);
|
|
1826
|
+
}
|
|
1827
|
+
const bundle = Automerge.saveBundle(doc, [...closure]);
|
|
1828
|
+
const pinned = Automerge.loadIncremental(Automerge.init(), bundle);
|
|
1829
|
+
const gotHeads = [...Automerge.getHeads(pinned)].sort();
|
|
1830
|
+
const wantHeads = [...pinHeads].sort();
|
|
1831
|
+
if (JSON.stringify(gotHeads) !== JSON.stringify(wantHeads)) {
|
|
1832
|
+
throw new Error(`[drafts] version clone heads mismatch: wanted ${wantHeads}, got ${gotHeads}`);
|
|
1833
|
+
}
|
|
1834
|
+
const clone = repo.create();
|
|
1835
|
+
clone.update(() => pinned);
|
|
1836
|
+
return clone;
|
|
1837
|
+
}
|
|
1838
|
+
function collectForkDiagnostic(handle, member, to) {
|
|
1839
|
+
const diagnostic = {
|
|
1840
|
+
draftsVersion: DRAFTS_VERSION,
|
|
1841
|
+
memberUrl: member.url,
|
|
1842
|
+
sourceUrl: member.cloneUrl ?? member.url,
|
|
1843
|
+
memberClonedAt: member.clonedAt,
|
|
1844
|
+
to,
|
|
1845
|
+
toHex: [],
|
|
1846
|
+
currentHeads: null,
|
|
1847
|
+
hasHeads: null,
|
|
1848
|
+
missingDeps: null,
|
|
1849
|
+
stats: null,
|
|
1850
|
+
automerge: null,
|
|
1851
|
+
pinnedChanges: [],
|
|
1852
|
+
fragments: null,
|
|
1853
|
+
probeErrors: []
|
|
1854
|
+
};
|
|
1855
|
+
const probe = (name, run) => {
|
|
1856
|
+
try {
|
|
1857
|
+
run();
|
|
1858
|
+
} catch (err) {
|
|
1859
|
+
diagnostic.probeErrors.push(`${name}: ${String(err)}`);
|
|
1860
|
+
}
|
|
1861
|
+
};
|
|
1862
|
+
probe("decodeHeads", () => {
|
|
1863
|
+
diagnostic.toHex = decodeHeads(to);
|
|
1864
|
+
});
|
|
1865
|
+
const doc = handle.doc();
|
|
1866
|
+
probe("getHeads", () => {
|
|
1867
|
+
diagnostic.currentHeads = Automerge.getHeads(doc);
|
|
1868
|
+
});
|
|
1869
|
+
probe("hasHeads", () => {
|
|
1870
|
+
diagnostic.hasHeads = Automerge.hasHeads(doc, diagnostic.toHex);
|
|
1871
|
+
});
|
|
1872
|
+
probe("getMissingDeps", () => {
|
|
1873
|
+
diagnostic.missingDeps = Automerge.getMissingDeps(doc, diagnostic.toHex);
|
|
1874
|
+
});
|
|
1875
|
+
probe("stats", () => {
|
|
1876
|
+
const s = Automerge.stats(doc);
|
|
1877
|
+
diagnostic.stats = {
|
|
1878
|
+
numChanges: s.numChanges,
|
|
1879
|
+
numOps: s.numOps
|
|
1880
|
+
};
|
|
1881
|
+
});
|
|
1882
|
+
probe("releaseInfo", () => {
|
|
1883
|
+
const info = Automerge.releaseInfo();
|
|
1884
|
+
diagnostic.automerge = {
|
|
1885
|
+
jsGitHead: info.js.gitHead,
|
|
1886
|
+
wasmGitHead: info.wasm?.gitHead ?? null,
|
|
1887
|
+
wasmVersion: info.wasm?.cargoPackageVersion ?? null
|
|
1888
|
+
};
|
|
1889
|
+
});
|
|
1890
|
+
probe("pinnedChanges", () => {
|
|
1891
|
+
const topo = Automerge.topoHistoryTraversal(doc);
|
|
1892
|
+
const metas = Automerge.getChangesMetaSince(doc, []);
|
|
1893
|
+
const metaByHash = new Map(metas.map((m) => [m.hash, m]));
|
|
1894
|
+
diagnostic.pinnedChanges = diagnostic.toHex.map((hash) => {
|
|
1895
|
+
const meta = metaByHash.get(hash);
|
|
1896
|
+
const topoIndex = topo.indexOf(hash);
|
|
1897
|
+
return {
|
|
1898
|
+
hash,
|
|
1899
|
+
known: !!meta || topoIndex >= 0,
|
|
1900
|
+
topoIndex: topoIndex >= 0 ? topoIndex : null,
|
|
1901
|
+
time: meta?.time ?? null,
|
|
1902
|
+
actor: meta?.actor ?? null,
|
|
1903
|
+
seq: meta?.seq ?? null,
|
|
1904
|
+
deps: meta?.deps ?? null,
|
|
1905
|
+
isCurrentHead: diagnostic.currentHeads?.includes(hash) ?? false
|
|
1906
|
+
};
|
|
1907
|
+
});
|
|
1908
|
+
});
|
|
1909
|
+
probe("fragments", () => {
|
|
1910
|
+
const topo = Automerge.topoHistoryTraversal(doc);
|
|
1911
|
+
const topoIndex = new Map(topo.map((h, i) => [h, i]));
|
|
1912
|
+
const pinned = new Set(diagnostic.toHex);
|
|
1913
|
+
diagnostic.fragments = Automerge.getFragmentMetadata(doc).map((f) => {
|
|
1914
|
+
let min = Infinity;
|
|
1915
|
+
let max = -Infinity;
|
|
1916
|
+
let containsPin = false;
|
|
1917
|
+
for (const h of f.members) {
|
|
1918
|
+
const i = topoIndex.get(h);
|
|
1919
|
+
if (i !== void 0) {
|
|
1920
|
+
if (i < min) min = i;
|
|
1921
|
+
if (i > max) max = i;
|
|
1922
|
+
}
|
|
1923
|
+
if (pinned.has(h)) containsPin = true;
|
|
1924
|
+
}
|
|
1925
|
+
return {
|
|
1926
|
+
level: f.level,
|
|
1927
|
+
head: f.head,
|
|
1928
|
+
memberCount: f.members.length,
|
|
1929
|
+
topoRange: min <= max ? [min, max] : null,
|
|
1930
|
+
containsPin
|
|
1931
|
+
};
|
|
1932
|
+
});
|
|
1933
|
+
});
|
|
1934
|
+
probe("saveDoc", () => {
|
|
1935
|
+
const bytes = Automerge.save(doc);
|
|
1936
|
+
let binary = "";
|
|
1937
|
+
const CHUNK = 32768;
|
|
1938
|
+
for (let i = 0; i < bytes.length; i += CHUNK) {
|
|
1939
|
+
binary += String.fromCharCode(...bytes.subarray(i, i + CHUNK));
|
|
1940
|
+
}
|
|
1941
|
+
lastForkRepro = {
|
|
1942
|
+
url: member.url,
|
|
1943
|
+
toHex: diagnostic.toHex,
|
|
1944
|
+
docBase64: btoa(binary)
|
|
1945
|
+
};
|
|
1946
|
+
});
|
|
1947
|
+
return diagnostic;
|
|
1948
|
+
}
|
|
1949
|
+
let lastForkRepro = null;
|
|
1950
|
+
function reportForkFailure(diagnostic, err) {
|
|
1951
|
+
const failure = {
|
|
1952
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1953
|
+
stack: err instanceof Error ? err.stack : void 0
|
|
1954
|
+
};
|
|
1955
|
+
if (!diagnostic) {
|
|
1956
|
+
console.error("[drafts][fork-diagnostic] failed before diagnostics could be gathered:", failure);
|
|
1957
|
+
return;
|
|
1958
|
+
}
|
|
1959
|
+
diagnostic.failure = failure;
|
|
1960
|
+
if (lastForkRepro && lastForkRepro.url === diagnostic.memberUrl) {
|
|
1961
|
+
window.__draftsForkRepro = lastForkRepro;
|
|
1962
|
+
}
|
|
1963
|
+
console.error("[drafts][fork-diagnostic] failed to fork member at version — paste this block back:\n" + JSON.stringify(diagnostic, null, 2) + "\n[drafts][fork-diagnostic] the failing doc's bytes are on window.__draftsForkRepro — to save them for an offline repro, run:\n const r = window.__draftsForkRepro;\n const bytes = Uint8Array.from(atob(r.docBase64), c => c.charCodeAt(0));\n const a = document.createElement('a');\n a.href = URL.createObjectURL(new Blob([bytes]));\n a.download = 'fork-repro.automerge'; a.click();\n console.log('pin heads:', r.toHex);");
|
|
1964
|
+
}
|
|
1965
|
+
function MainCard(props) {
|
|
1966
|
+
const [menuOpen, setMenuOpen] = createSignal(false);
|
|
1967
|
+
return (() => {
|
|
1968
|
+
var _el$5 = _tmpl$6(), _el$6 = _el$5.firstChild, _el$7 = _el$6.firstChild;
|
|
1969
|
+
addEventListener(_el$6, "click", props.onSelect, true);
|
|
1970
|
+
insert(_el$7, createComponent(DraftName, {
|
|
1971
|
+
get name() {
|
|
1972
|
+
return props.name;
|
|
1973
|
+
},
|
|
1974
|
+
fallback: "Main",
|
|
1975
|
+
get onRename() {
|
|
1976
|
+
return props.onRename;
|
|
1977
|
+
}
|
|
1978
|
+
}), null);
|
|
1979
|
+
insert(_el$7, createComponent(Show, {
|
|
1980
|
+
get when() {
|
|
1981
|
+
return props.hasCheckpoint;
|
|
1982
|
+
},
|
|
1983
|
+
get children() {
|
|
1984
|
+
var _el$8 = _tmpl$4();
|
|
1985
|
+
_el$8.$$click = (e) => {
|
|
1986
|
+
e.stopPropagation();
|
|
1987
|
+
props.onReturnToLatest();
|
|
1988
|
+
};
|
|
1989
|
+
return _el$8;
|
|
1990
|
+
}
|
|
1991
|
+
}), null);
|
|
1992
|
+
insert(_el$7, createComponent(Show, {
|
|
1993
|
+
get when() {
|
|
1994
|
+
return props.isSelected;
|
|
1995
|
+
},
|
|
1996
|
+
get children() {
|
|
1997
|
+
var _el$9 = _tmpl$5();
|
|
1998
|
+
insert(_el$9, createComponent(EyeToggle, {
|
|
1999
|
+
get open() {
|
|
2000
|
+
return props.eyeOpen;
|
|
2001
|
+
},
|
|
2002
|
+
get disabled() {
|
|
2003
|
+
return props.eyeDisabled;
|
|
2004
|
+
},
|
|
2005
|
+
get onToggle() {
|
|
2006
|
+
return props.onToggleEye;
|
|
2007
|
+
}
|
|
2008
|
+
}), null);
|
|
2009
|
+
insert(_el$9, createComponent(CardMenu, {
|
|
2010
|
+
get forkDisabled() {
|
|
2011
|
+
return props.forkDisabled;
|
|
2012
|
+
},
|
|
2013
|
+
get onFork() {
|
|
2014
|
+
return props.onFork;
|
|
2015
|
+
},
|
|
2016
|
+
get forkAtLabel() {
|
|
2017
|
+
return props.forkAtLabel;
|
|
2018
|
+
},
|
|
2019
|
+
get onForkAt() {
|
|
2020
|
+
return props.onForkAt;
|
|
2021
|
+
},
|
|
2022
|
+
onOpenChange: setMenuOpen
|
|
2023
|
+
}), null);
|
|
2024
|
+
return _el$9;
|
|
2025
|
+
}
|
|
2026
|
+
}), null);
|
|
2027
|
+
insert(_el$5, createComponent(Show, {
|
|
2028
|
+
get when() {
|
|
2029
|
+
return props.isSelected;
|
|
2030
|
+
},
|
|
2031
|
+
get children() {
|
|
2032
|
+
return createComponent(DraftChangesList, {
|
|
2033
|
+
get members() {
|
|
2034
|
+
return props.members;
|
|
2035
|
+
},
|
|
2036
|
+
get changeGroupDocUrl() {
|
|
2037
|
+
return props.changeGroupDocUrl;
|
|
2038
|
+
},
|
|
2039
|
+
get mainDocUrl() {
|
|
2040
|
+
return props.hostDocUrl;
|
|
2041
|
+
},
|
|
2042
|
+
get onScrub() {
|
|
2043
|
+
return props.onScrub;
|
|
2044
|
+
},
|
|
2045
|
+
get scrubber() {
|
|
2046
|
+
return props.scrubber;
|
|
2047
|
+
},
|
|
2048
|
+
get onBaselineScrub() {
|
|
2049
|
+
return props.onBaselineScrub;
|
|
2050
|
+
},
|
|
2051
|
+
get baseliner() {
|
|
2052
|
+
return props.baseliner;
|
|
2053
|
+
},
|
|
2054
|
+
eyeOpen: () => props.eyeOpen,
|
|
2055
|
+
get checkpoint() {
|
|
2056
|
+
return props.checkpoint;
|
|
2057
|
+
},
|
|
2058
|
+
get onReturnToLatest() {
|
|
2059
|
+
return props.onReturnToLatest;
|
|
2060
|
+
}
|
|
2061
|
+
});
|
|
2062
|
+
}
|
|
2063
|
+
}), null);
|
|
2064
|
+
effect((_p$) => {
|
|
2065
|
+
var _v$ = props.isSelected ? "" : void 0, _v$2 = menuOpen() ? "" : void 0, _v$3 = props.isMergeTarget ? "" : void 0;
|
|
2066
|
+
_v$ !== _p$.e && setAttribute(_el$5, "data-selected", _p$.e = _v$);
|
|
2067
|
+
_v$2 !== _p$.t && setAttribute(_el$5, "data-menu-open", _p$.t = _v$2);
|
|
2068
|
+
_v$3 !== _p$.a && setAttribute(_el$5, "data-merge-target", _p$.a = _v$3);
|
|
2069
|
+
return _p$;
|
|
2070
|
+
}, {
|
|
2071
|
+
e: void 0,
|
|
2072
|
+
t: void 0,
|
|
2073
|
+
a: void 0
|
|
2074
|
+
});
|
|
2075
|
+
return _el$5;
|
|
2076
|
+
})();
|
|
2077
|
+
}
|
|
2078
|
+
function DraftCard(props) {
|
|
2079
|
+
const [menuOpen, setMenuOpen] = createSignal(false);
|
|
2080
|
+
return (() => {
|
|
2081
|
+
var _el$0 = _tmpl$7(), _el$1 = _el$0.firstChild, _el$10 = _el$1.firstChild;
|
|
2082
|
+
_el$1.$$click = () => props.onSelect(props.url);
|
|
2083
|
+
insert(_el$10, createComponent(DraftName, {
|
|
2084
|
+
get name() {
|
|
2085
|
+
return props.name;
|
|
2086
|
+
},
|
|
2087
|
+
fallback: "Draft",
|
|
2088
|
+
get onRename() {
|
|
2089
|
+
return props.onRename;
|
|
2090
|
+
}
|
|
2091
|
+
}), null);
|
|
2092
|
+
insert(_el$10, createComponent(Show, {
|
|
2093
|
+
get when() {
|
|
2094
|
+
return props.hasCheckpoint;
|
|
2095
|
+
},
|
|
2096
|
+
get children() {
|
|
2097
|
+
var _el$11 = _tmpl$4();
|
|
2098
|
+
_el$11.$$click = (e) => {
|
|
2099
|
+
e.stopPropagation();
|
|
2100
|
+
props.onReturnToLatest();
|
|
2101
|
+
};
|
|
2102
|
+
return _el$11;
|
|
2103
|
+
}
|
|
2104
|
+
}), null);
|
|
2105
|
+
insert(_el$10, createComponent(Show, {
|
|
2106
|
+
get when() {
|
|
2107
|
+
return props.isSelected;
|
|
2108
|
+
},
|
|
2109
|
+
get children() {
|
|
2110
|
+
var _el$12 = _tmpl$5();
|
|
2111
|
+
insert(_el$12, createComponent(EyeToggle, {
|
|
2112
|
+
get open() {
|
|
2113
|
+
return props.eyeOpen;
|
|
2114
|
+
},
|
|
2115
|
+
get disabled() {
|
|
2116
|
+
return props.eyeDisabled;
|
|
2117
|
+
},
|
|
2118
|
+
get onToggle() {
|
|
2119
|
+
return props.onToggleEye;
|
|
2120
|
+
}
|
|
2121
|
+
}), null);
|
|
2122
|
+
insert(_el$12, createComponent(CardMenu, {
|
|
2123
|
+
get onFork() {
|
|
2124
|
+
return props.onFork;
|
|
2125
|
+
},
|
|
2126
|
+
get forkAtLabel() {
|
|
2127
|
+
return props.forkAtLabel;
|
|
2128
|
+
},
|
|
2129
|
+
get onForkAt() {
|
|
2130
|
+
return props.onForkAt;
|
|
2131
|
+
},
|
|
2132
|
+
get merge() {
|
|
2133
|
+
return {
|
|
2134
|
+
label: props.mergeLabel,
|
|
2135
|
+
onMerge: props.onMerge,
|
|
2136
|
+
onHoverTarget: props.onMergeHover
|
|
2137
|
+
};
|
|
2138
|
+
},
|
|
2139
|
+
get onDelete() {
|
|
2140
|
+
return props.onDelete;
|
|
2141
|
+
},
|
|
2142
|
+
onOpenChange: setMenuOpen
|
|
2143
|
+
}), null);
|
|
2144
|
+
return _el$12;
|
|
2145
|
+
}
|
|
2146
|
+
}), null);
|
|
2147
|
+
insert(_el$0, createComponent(Show, {
|
|
2148
|
+
get when() {
|
|
2149
|
+
return props.isSelected;
|
|
2150
|
+
},
|
|
2151
|
+
get children() {
|
|
2152
|
+
return createComponent(DraftChangesList, {
|
|
2153
|
+
members: () => props.members,
|
|
2154
|
+
get changeGroupDocUrl() {
|
|
2155
|
+
return props.changeGroupDocUrl;
|
|
2156
|
+
},
|
|
2157
|
+
get mainDocUrl() {
|
|
2158
|
+
return props.mainDocUrl;
|
|
2159
|
+
},
|
|
2160
|
+
get onScrub() {
|
|
2161
|
+
return props.onScrub;
|
|
2162
|
+
},
|
|
2163
|
+
get scrubber() {
|
|
2164
|
+
return props.scrubber;
|
|
2165
|
+
},
|
|
2166
|
+
get onBaselineScrub() {
|
|
2167
|
+
return props.onBaselineScrub;
|
|
2168
|
+
},
|
|
2169
|
+
get baseliner() {
|
|
2170
|
+
return props.baseliner;
|
|
2171
|
+
},
|
|
2172
|
+
eyeOpen: () => props.eyeOpen,
|
|
2173
|
+
get checkpoint() {
|
|
2174
|
+
return props.checkpoint;
|
|
2175
|
+
},
|
|
2176
|
+
get onReturnToLatest() {
|
|
2177
|
+
return props.onReturnToLatest;
|
|
2178
|
+
}
|
|
2179
|
+
});
|
|
2180
|
+
}
|
|
2181
|
+
}), null);
|
|
2182
|
+
effect((_p$) => {
|
|
2183
|
+
var _v$4 = props.isSelected ? "" : void 0, _v$5 = menuOpen() ? "" : void 0, _v$6 = props.isMergeTarget ? "" : void 0, _v$7 = `${Math.min(props.depth + 1, 5)}rem`;
|
|
2184
|
+
_v$4 !== _p$.e && setAttribute(_el$0, "data-selected", _p$.e = _v$4);
|
|
2185
|
+
_v$5 !== _p$.t && setAttribute(_el$0, "data-menu-open", _p$.t = _v$5);
|
|
2186
|
+
_v$6 !== _p$.a && setAttribute(_el$0, "data-merge-target", _p$.a = _v$6);
|
|
2187
|
+
_v$7 !== _p$.o && setStyleProperty(_el$0, "margin-left", _p$.o = _v$7);
|
|
2188
|
+
return _p$;
|
|
2189
|
+
}, {
|
|
2190
|
+
e: void 0,
|
|
2191
|
+
t: void 0,
|
|
2192
|
+
a: void 0,
|
|
2193
|
+
o: void 0
|
|
2194
|
+
});
|
|
2195
|
+
return _el$0;
|
|
2196
|
+
})();
|
|
2197
|
+
}
|
|
2198
|
+
function CardMenu(props) {
|
|
2199
|
+
const [open, setOpenSignal] = createSignal(false);
|
|
2200
|
+
const setOpen = (v) => {
|
|
2201
|
+
setOpenSignal(v);
|
|
2202
|
+
props.onOpenChange(v);
|
|
2203
|
+
if (!v) props.merge?.onHoverTarget(false);
|
|
2204
|
+
};
|
|
2205
|
+
onCleanup(() => setOpen(false));
|
|
2206
|
+
createEffect(() => {
|
|
2207
|
+
if (!open()) return;
|
|
2208
|
+
const onKey = (e) => {
|
|
2209
|
+
if (e.key === "Escape") setOpen(false);
|
|
2210
|
+
};
|
|
2211
|
+
document.addEventListener("keydown", onKey);
|
|
2212
|
+
onCleanup(() => document.removeEventListener("keydown", onKey));
|
|
2213
|
+
});
|
|
2214
|
+
const pick = (e, action) => {
|
|
2215
|
+
e.stopPropagation();
|
|
2216
|
+
setOpen(false);
|
|
2217
|
+
action();
|
|
2218
|
+
};
|
|
2219
|
+
return (() => {
|
|
2220
|
+
var _el$13 = _tmpl$0(), _el$14 = _el$13.firstChild;
|
|
2221
|
+
_el$14.$$click = (e) => {
|
|
2222
|
+
e.stopPropagation();
|
|
2223
|
+
setOpen(!open());
|
|
2224
|
+
};
|
|
2225
|
+
insert(_el$14, createComponent(EllipsisIcon, {}));
|
|
2226
|
+
insert(_el$13, createComponent(Show, {
|
|
2227
|
+
get when() {
|
|
2228
|
+
return open();
|
|
2229
|
+
},
|
|
2230
|
+
get children() {
|
|
2231
|
+
return [(() => {
|
|
2232
|
+
var _el$15 = _tmpl$8();
|
|
2233
|
+
_el$15.$$click = (e) => {
|
|
2234
|
+
e.stopPropagation();
|
|
2235
|
+
setOpen(false);
|
|
2236
|
+
};
|
|
2237
|
+
return _el$15;
|
|
2238
|
+
})(), (() => {
|
|
2239
|
+
var _el$16 = _tmpl$9(), _el$17 = _el$16.firstChild, _el$18 = _el$17.firstChild;
|
|
2240
|
+
_el$17.$$click = (e) => pick(e, props.onFork);
|
|
2241
|
+
insert(_el$17, createComponent(GitBranchIcon, {}), _el$18);
|
|
2242
|
+
insert(_el$16, createComponent(Show, {
|
|
2243
|
+
get when() {
|
|
2244
|
+
return props.forkAtLabel;
|
|
2245
|
+
},
|
|
2246
|
+
children: (label) => (() => {
|
|
2247
|
+
var _el$19 = _tmpl$1();
|
|
2248
|
+
_el$19.$$click = (e) => pick(e, props.onForkAt);
|
|
2249
|
+
insert(_el$19, createComponent(HistoryIcon, {}), null);
|
|
2250
|
+
insert(_el$19, label, null);
|
|
2251
|
+
return _el$19;
|
|
2252
|
+
})()
|
|
2253
|
+
}), null);
|
|
2254
|
+
insert(_el$16, createComponent(Show, {
|
|
2255
|
+
get when() {
|
|
2256
|
+
return props.merge;
|
|
2257
|
+
},
|
|
2258
|
+
children: (merge) => [_tmpl$10(), (() => {
|
|
2259
|
+
var _el$21 = _tmpl$11();
|
|
2260
|
+
_el$21.addEventListener("mouseleave", () => merge().onHoverTarget(false));
|
|
2261
|
+
_el$21.addEventListener("mouseenter", () => merge().onHoverTarget(true));
|
|
2262
|
+
_el$21.$$click = (e) => pick(e, merge().onMerge);
|
|
2263
|
+
insert(_el$21, createComponent(GitMergeIcon, {}), null);
|
|
2264
|
+
insert(_el$21, () => merge().label, null);
|
|
2265
|
+
return _el$21;
|
|
2266
|
+
})()]
|
|
2267
|
+
}), null);
|
|
2268
|
+
insert(_el$16, createComponent(Show, {
|
|
2269
|
+
get when() {
|
|
2270
|
+
return props.onDelete;
|
|
2271
|
+
},
|
|
2272
|
+
children: (onDelete) => [_tmpl$10(), (() => {
|
|
2273
|
+
var _el$23 = _tmpl$12(), _el$24 = _el$23.firstChild;
|
|
2274
|
+
_el$23.$$click = (e) => pick(e, onDelete());
|
|
2275
|
+
insert(_el$23, createComponent(TrashIcon, {}), _el$24);
|
|
2276
|
+
return _el$23;
|
|
2277
|
+
})()]
|
|
2278
|
+
}), null);
|
|
2279
|
+
effect((_p$) => {
|
|
2280
|
+
var _v$8 = props.forkDisabled, _v$9 = props.forkDisabled ? "Drafts aren't supported for folders yet" : "Fork a new draft from the latest version";
|
|
2281
|
+
_v$8 !== _p$.e && (_el$17.disabled = _p$.e = _v$8);
|
|
2282
|
+
_v$9 !== _p$.t && setAttribute(_el$17, "title", _p$.t = _v$9);
|
|
2283
|
+
return _p$;
|
|
2284
|
+
}, {
|
|
2285
|
+
e: void 0,
|
|
2286
|
+
t: void 0
|
|
2287
|
+
});
|
|
2288
|
+
return _el$16;
|
|
2289
|
+
})()];
|
|
2290
|
+
}
|
|
2291
|
+
}), null);
|
|
2292
|
+
effect(() => setAttribute(_el$14, "data-active", open() ? "" : void 0));
|
|
2293
|
+
return _el$13;
|
|
2294
|
+
})();
|
|
2295
|
+
}
|
|
2296
|
+
function formatVersionTime(ms2) {
|
|
2297
|
+
const date = new Date(ms2);
|
|
2298
|
+
const sameYear = date.getFullYear() === (/* @__PURE__ */ new Date()).getFullYear();
|
|
2299
|
+
return date.toLocaleString(void 0, {
|
|
2300
|
+
month: "short",
|
|
2301
|
+
day: "numeric",
|
|
2302
|
+
year: sameYear ? void 0 : "numeric",
|
|
2303
|
+
hour: "numeric",
|
|
2304
|
+
minute: "2-digit"
|
|
2305
|
+
});
|
|
2306
|
+
}
|
|
2307
|
+
function GitBranchIcon() {
|
|
2308
|
+
return _tmpl$13();
|
|
2309
|
+
}
|
|
2310
|
+
function GitMergeIcon() {
|
|
2311
|
+
return _tmpl$14();
|
|
2312
|
+
}
|
|
2313
|
+
function EllipsisIcon() {
|
|
2314
|
+
return _tmpl$15();
|
|
2315
|
+
}
|
|
2316
|
+
function TrashIcon() {
|
|
2317
|
+
return _tmpl$16();
|
|
2318
|
+
}
|
|
2319
|
+
function HistoryIcon() {
|
|
2320
|
+
return _tmpl$17();
|
|
2321
|
+
}
|
|
2322
|
+
function DraftName(props) {
|
|
2323
|
+
const [editing, setEditing] = createSignal(false);
|
|
2324
|
+
return createComponent(Show, {
|
|
2325
|
+
get when() {
|
|
2326
|
+
return editing();
|
|
2327
|
+
},
|
|
2328
|
+
get fallback() {
|
|
2329
|
+
return (() => {
|
|
2330
|
+
var _el$31 = _tmpl$19();
|
|
2331
|
+
_el$31.$$dblclick = () => setEditing(true);
|
|
2332
|
+
insert(_el$31, () => props.name ?? props.fallback);
|
|
2333
|
+
return _el$31;
|
|
2334
|
+
})();
|
|
2335
|
+
},
|
|
2336
|
+
get children() {
|
|
2337
|
+
var _el$30 = _tmpl$18();
|
|
2338
|
+
_el$30.addEventListener("blur", (e) => {
|
|
2339
|
+
if (!editing()) return;
|
|
2340
|
+
setEditing(false);
|
|
2341
|
+
const value = e.currentTarget.value.trim();
|
|
2342
|
+
if (value !== (props.name ?? "")) props.onRename(value || null);
|
|
2343
|
+
});
|
|
2344
|
+
_el$30.$$keydown = (e) => {
|
|
2345
|
+
if (e.key === "Enter") e.currentTarget.blur();
|
|
2346
|
+
if (e.key === "Escape") setEditing(false);
|
|
2347
|
+
};
|
|
2348
|
+
_el$30.$$dblclick = (e) => e.stopPropagation();
|
|
2349
|
+
_el$30.$$click = (e) => e.stopPropagation();
|
|
2350
|
+
use((el) => setTimeout(() => el.select()), _el$30);
|
|
2351
|
+
effect(() => setAttribute(_el$30, "placeholder", props.fallback));
|
|
2352
|
+
effect(() => _el$30.value = props.name ?? "");
|
|
2353
|
+
return _el$30;
|
|
2354
|
+
}
|
|
2355
|
+
});
|
|
2356
|
+
}
|
|
2357
|
+
function EyeToggle(props) {
|
|
2358
|
+
return (() => {
|
|
2359
|
+
var _el$32 = _tmpl$20();
|
|
2360
|
+
_el$32.$$click = (e) => {
|
|
2361
|
+
e.stopPropagation();
|
|
2362
|
+
if (props.disabled) return;
|
|
2363
|
+
props.onToggle();
|
|
2364
|
+
};
|
|
2365
|
+
insert(_el$32, createComponent(Show, {
|
|
2366
|
+
get when() {
|
|
2367
|
+
return props.open;
|
|
2368
|
+
},
|
|
2369
|
+
get fallback() {
|
|
2370
|
+
return createComponent(EyeOffIcon, {});
|
|
2371
|
+
},
|
|
2372
|
+
get children() {
|
|
2373
|
+
return createComponent(EyeIcon, {});
|
|
2374
|
+
}
|
|
2375
|
+
}));
|
|
2376
|
+
effect((_p$) => {
|
|
2377
|
+
var _v$0 = props.open ? "" : void 0, _v$1 = props.disabled ? "" : void 0, _v$10 = props.disabled, _v$11 = props.disabled ? "Nothing to compare yet — select a version in the timeline first" : props.open ? "Hide changes" : "Show what changed";
|
|
2378
|
+
_v$0 !== _p$.e && setAttribute(_el$32, "data-active", _p$.e = _v$0);
|
|
2379
|
+
_v$1 !== _p$.t && setAttribute(_el$32, "data-disabled", _p$.t = _v$1);
|
|
2380
|
+
_v$10 !== _p$.a && setAttribute(_el$32, "aria-disabled", _p$.a = _v$10);
|
|
2381
|
+
_v$11 !== _p$.o && setAttribute(_el$32, "title", _p$.o = _v$11);
|
|
2382
|
+
return _p$;
|
|
2383
|
+
}, {
|
|
2384
|
+
e: void 0,
|
|
2385
|
+
t: void 0,
|
|
2386
|
+
a: void 0,
|
|
2387
|
+
o: void 0
|
|
2388
|
+
});
|
|
2389
|
+
return _el$32;
|
|
2390
|
+
})();
|
|
2391
|
+
}
|
|
2392
|
+
function EyeIcon() {
|
|
2393
|
+
return _tmpl$21();
|
|
2394
|
+
}
|
|
2395
|
+
function EyeOffIcon() {
|
|
2396
|
+
return _tmpl$22();
|
|
2397
|
+
}
|
|
2398
|
+
const BASELINE_GROUP_START = -1;
|
|
2399
|
+
function DraftChangesList(props) {
|
|
2400
|
+
const repo = "repo" in window ? window.repo : void 0;
|
|
2401
|
+
const [changeGroupHandle, setChangeGroupHandle] = createSignal();
|
|
2402
|
+
createEffect(() => {
|
|
2403
|
+
const url = props.changeGroupDocUrl;
|
|
2404
|
+
setChangeGroupHandle(void 0);
|
|
2405
|
+
if (!url || !repo) return;
|
|
2406
|
+
let disposed = false;
|
|
2407
|
+
void repo.find(url).then((handle) => {
|
|
2408
|
+
if (!disposed) setChangeGroupHandle(handle);
|
|
2409
|
+
}, (err) => {
|
|
2410
|
+
console.warn("[drafts] failed to load change-group doc:", url, err);
|
|
2411
|
+
});
|
|
2412
|
+
onCleanup(() => {
|
|
2413
|
+
disposed = true;
|
|
2414
|
+
});
|
|
2415
|
+
});
|
|
2416
|
+
const changeGroupDoc = createDocSignal(changeGroupHandle);
|
|
2417
|
+
const timeGroups = createMemo(() => Object.values(changeGroupDoc()?.groups ?? {}).filter((g) => g.additions > 0 || g.deletions > 0).sort((a, b) => b.endTime - a.endTime || (a.id < b.id ? -1 : 1)));
|
|
2418
|
+
const [sources, setSources] = createSignal(null);
|
|
2419
|
+
const [createdAt, setCreatedAt] = createSignal(void 0);
|
|
2420
|
+
createEffect(() => {
|
|
2421
|
+
const list = props.members();
|
|
2422
|
+
const mainDocUrl = props.mainDocUrl;
|
|
2423
|
+
if (!repo) return;
|
|
2424
|
+
let disposed = false;
|
|
2425
|
+
setSources(null);
|
|
2426
|
+
void (async () => {
|
|
2427
|
+
const next = [];
|
|
2428
|
+
for (const member of list) {
|
|
2429
|
+
try {
|
|
2430
|
+
const handle = await repo.find(member.cloneUrl ?? member.url);
|
|
2431
|
+
next.push({
|
|
2432
|
+
member,
|
|
2433
|
+
handle
|
|
2434
|
+
});
|
|
2435
|
+
} catch (err) {
|
|
2436
|
+
console.warn("[drafts] failed to resolve member for scrubbing:", member, err);
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
const created = await getDocCreationTime(repo, mainDocUrl);
|
|
2440
|
+
if (disposed) return;
|
|
2441
|
+
setCreatedAt(created);
|
|
2442
|
+
setSources(next);
|
|
2443
|
+
})();
|
|
2444
|
+
onCleanup(() => {
|
|
2445
|
+
disposed = true;
|
|
2446
|
+
});
|
|
2447
|
+
});
|
|
2448
|
+
const isBuilding = createMemo(() => {
|
|
2449
|
+
const consumed = changeGroupDoc()?.computedThrough ?? {};
|
|
2450
|
+
const srcs = sources();
|
|
2451
|
+
if (!srcs || srcs.length === 0) return false;
|
|
2452
|
+
return srcs.some(({
|
|
2453
|
+
member,
|
|
2454
|
+
handle
|
|
2455
|
+
}) => {
|
|
2456
|
+
const heads = handle.heads();
|
|
2457
|
+
return heads ? !sameHeads(heads, consumed[member.url]) : false;
|
|
2458
|
+
});
|
|
2459
|
+
});
|
|
2460
|
+
const scanCache = /* @__PURE__ */ new Map();
|
|
2461
|
+
const resolveGroupChanges = (group) => {
|
|
2462
|
+
const key = `${group.id}:${group.changeCount}`;
|
|
2463
|
+
const hit = scanCache.get(key);
|
|
2464
|
+
if (hit) return hit;
|
|
2465
|
+
const srcs = sources();
|
|
2466
|
+
if (!srcs) return null;
|
|
2467
|
+
const cutoff = createdAt();
|
|
2468
|
+
const rows = [];
|
|
2469
|
+
for (const {
|
|
2470
|
+
member,
|
|
2471
|
+
handle
|
|
2472
|
+
} of srcs) {
|
|
2473
|
+
const doc = handle.doc();
|
|
2474
|
+
if (!doc) continue;
|
|
2475
|
+
try {
|
|
2476
|
+
const since = member.clonedAt ? decodeHeads(member.clonedAt) : [];
|
|
2477
|
+
const metas = Automerge.getChangesMetaSince(doc, since);
|
|
2478
|
+
metas.forEach((meta, seq) => {
|
|
2479
|
+
if (cutoff !== void 0 && meta.time && meta.time < cutoff) return;
|
|
2480
|
+
if (meta.time < group.startTime || meta.time > group.endTime) return;
|
|
2481
|
+
rows.push({
|
|
2482
|
+
docUrl: member.url,
|
|
2483
|
+
doc,
|
|
2484
|
+
hash: meta.hash,
|
|
2485
|
+
time: meta.time,
|
|
2486
|
+
deps: meta.deps,
|
|
2487
|
+
seq
|
|
2488
|
+
});
|
|
2489
|
+
});
|
|
2490
|
+
} catch (err) {
|
|
2491
|
+
console.warn("[drafts] failed to scan changes for member:", member, err);
|
|
2492
|
+
}
|
|
2493
|
+
}
|
|
2494
|
+
rows.sort((a, b) => b.time - a.time || b.seq - a.seq);
|
|
2495
|
+
scanCache.set(key, rows);
|
|
2496
|
+
return rows;
|
|
2497
|
+
};
|
|
2498
|
+
const buildHead = (group, offset) => {
|
|
2499
|
+
if (offset <= 0) {
|
|
2500
|
+
return {
|
|
2501
|
+
groupId: group.id,
|
|
2502
|
+
offset: 0,
|
|
2503
|
+
head: {
|
|
2504
|
+
docUrl: group.newestMemberUrl,
|
|
2505
|
+
hash: group.newestHash,
|
|
2506
|
+
time: group.endTime
|
|
2507
|
+
},
|
|
2508
|
+
groupStartTime: group.startTime
|
|
2509
|
+
};
|
|
2510
|
+
}
|
|
2511
|
+
const rows = resolveGroupChanges(group);
|
|
2512
|
+
if (!rows || rows.length === 0) return null;
|
|
2513
|
+
const row = rows[Math.min(offset, rows.length - 1)];
|
|
2514
|
+
return {
|
|
2515
|
+
groupId: group.id,
|
|
2516
|
+
offset,
|
|
2517
|
+
head: {
|
|
2518
|
+
docUrl: row.docUrl,
|
|
2519
|
+
hash: row.hash,
|
|
2520
|
+
time: row.time
|
|
2521
|
+
},
|
|
2522
|
+
groupStartTime: group.startTime
|
|
2523
|
+
};
|
|
2524
|
+
};
|
|
2525
|
+
const resolveOffset = (group, offset) => offset === BASELINE_GROUP_START ? Math.max(0, group.changeCount - 1) : offset;
|
|
2526
|
+
const timeAt = (group, offset) => {
|
|
2527
|
+
const resolved = resolveOffset(group, offset);
|
|
2528
|
+
if (resolved <= 0) return group.endTime;
|
|
2529
|
+
const rows = resolveGroupChanges(group);
|
|
2530
|
+
if (!rows || rows.length === 0) return group.startTime;
|
|
2531
|
+
return rows[Math.min(resolved, rows.length - 1)].time;
|
|
2532
|
+
};
|
|
2533
|
+
const linearIndex = (groupId, offset) => {
|
|
2534
|
+
let acc = 0;
|
|
2535
|
+
for (const b of bands()) {
|
|
2536
|
+
const count = Math.max(1, b.group.changeCount);
|
|
2537
|
+
if (b.group.id === groupId) {
|
|
2538
|
+
return acc + Math.min(Math.max(0, resolveOffset(b.group, offset)), count - 1);
|
|
2539
|
+
}
|
|
2540
|
+
acc += count;
|
|
2541
|
+
}
|
|
2542
|
+
return acc + Math.max(0, offset);
|
|
2543
|
+
};
|
|
2544
|
+
const scrubTo = (group, offset) => {
|
|
2545
|
+
const head = buildHead(group, offset);
|
|
2546
|
+
if (!head) return;
|
|
2547
|
+
if (props.eyeOpen()) {
|
|
2548
|
+
const bl = props.baseliner();
|
|
2549
|
+
if (bl && linearIndex(head.groupId, head.offset) > linearIndex(bl.groupId, bl.offset)) {
|
|
2550
|
+
props.onBaselineScrub({
|
|
2551
|
+
groupId: group.id,
|
|
2552
|
+
offset: head.offset,
|
|
2553
|
+
time: head.head.time
|
|
2554
|
+
});
|
|
2555
|
+
}
|
|
2556
|
+
}
|
|
2557
|
+
props.onScrub(head);
|
|
2558
|
+
};
|
|
2559
|
+
const selectGroup = (group) => {
|
|
2560
|
+
if (props.eyeOpen()) {
|
|
2561
|
+
props.onBaselineScrub({
|
|
2562
|
+
groupId: group.id,
|
|
2563
|
+
offset: BASELINE_GROUP_START,
|
|
2564
|
+
time: group.startTime
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
scrubTo(group, 0);
|
|
2568
|
+
};
|
|
2569
|
+
const baselineScrubTo = (group, offset) => {
|
|
2570
|
+
const head = props.scrubber();
|
|
2571
|
+
if (!head) return;
|
|
2572
|
+
if (linearIndex(group.id, offset) < linearIndex(head.groupId, head.offset)) {
|
|
2573
|
+
props.onBaselineScrub({
|
|
2574
|
+
groupId: head.groupId,
|
|
2575
|
+
offset: head.offset,
|
|
2576
|
+
time: head.head.time
|
|
2577
|
+
});
|
|
2578
|
+
return;
|
|
2579
|
+
}
|
|
2580
|
+
props.onBaselineScrub({
|
|
2581
|
+
groupId: group.id,
|
|
2582
|
+
offset,
|
|
2583
|
+
time: timeAt(group, offset)
|
|
2584
|
+
});
|
|
2585
|
+
};
|
|
2586
|
+
const groupForScrub = (s) => timeGroups().find((g) => g.id === s.groupId) ?? timeGroups().find((g) => s.head.time >= g.startTime && s.head.time <= g.endTime) ?? null;
|
|
2587
|
+
const rowEls = /* @__PURE__ */ new Map();
|
|
2588
|
+
const [rowsEl, setRowsEl] = createSignal();
|
|
2589
|
+
const [measureTick, setMeasureTick] = createSignal(0);
|
|
2590
|
+
createEffect(() => {
|
|
2591
|
+
const el = rowsEl();
|
|
2592
|
+
if (!el) return;
|
|
2593
|
+
const observer = new ResizeObserver(() => setMeasureTick((t) => t + 1));
|
|
2594
|
+
observer.observe(el);
|
|
2595
|
+
onCleanup(() => observer.disconnect());
|
|
2596
|
+
});
|
|
2597
|
+
createEffect(() => {
|
|
2598
|
+
timeGroups();
|
|
2599
|
+
requestAnimationFrame(() => setMeasureTick((t) => t + 1));
|
|
2600
|
+
});
|
|
2601
|
+
const bands = createMemo(() => {
|
|
2602
|
+
measureTick();
|
|
2603
|
+
const out = [];
|
|
2604
|
+
for (const group of timeGroups()) {
|
|
2605
|
+
const el = rowEls.get(group.id);
|
|
2606
|
+
if (el) {
|
|
2607
|
+
out.push({
|
|
2608
|
+
group,
|
|
2609
|
+
top: el.offsetTop,
|
|
2610
|
+
height: el.offsetHeight
|
|
2611
|
+
});
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2614
|
+
return out;
|
|
2615
|
+
});
|
|
2616
|
+
const yForPosition = (band, offset) => {
|
|
2617
|
+
const count = Math.max(1, band.group.changeCount);
|
|
2618
|
+
return band.top + Math.min(offset, count - 1) / count * band.height;
|
|
2619
|
+
};
|
|
2620
|
+
const positionForY = (y) => {
|
|
2621
|
+
const bs = bands();
|
|
2622
|
+
if (bs.length === 0) return null;
|
|
2623
|
+
for (const b of bs) {
|
|
2624
|
+
if (y < b.top) return {
|
|
2625
|
+
group: b.group,
|
|
2626
|
+
offset: 0
|
|
2627
|
+
};
|
|
2628
|
+
if (y < b.top + b.height) {
|
|
2629
|
+
const count = Math.max(1, b.group.changeCount);
|
|
2630
|
+
const offset = Math.min(Math.round((y - b.top) / b.height * count), count - 1);
|
|
2631
|
+
return {
|
|
2632
|
+
group: b.group,
|
|
2633
|
+
offset
|
|
2634
|
+
};
|
|
2635
|
+
}
|
|
2636
|
+
}
|
|
2637
|
+
const last = bs[bs.length - 1];
|
|
2638
|
+
return {
|
|
2639
|
+
group: last.group,
|
|
2640
|
+
offset: Math.max(0, last.group.changeCount - 1)
|
|
2641
|
+
};
|
|
2642
|
+
};
|
|
2643
|
+
const tokenGeometry = createMemo(() => {
|
|
2644
|
+
const bs = bands();
|
|
2645
|
+
if (bs.length === 0) return null;
|
|
2646
|
+
const s = props.scrubber();
|
|
2647
|
+
if (!s) return {
|
|
2648
|
+
top: bs[0].top
|
|
2649
|
+
};
|
|
2650
|
+
const group = groupForScrub(s);
|
|
2651
|
+
const band = group ? bs.find((b) => b.group.id === group.id) : void 0;
|
|
2652
|
+
if (!band) return {
|
|
2653
|
+
top: bs[0].top
|
|
2654
|
+
};
|
|
2655
|
+
return {
|
|
2656
|
+
top: yForPosition(band, s.offset)
|
|
2657
|
+
};
|
|
2658
|
+
});
|
|
2659
|
+
const baselineGeometry = createMemo(() => {
|
|
2660
|
+
if (!props.eyeOpen() || !props.scrubber()) return null;
|
|
2661
|
+
const b = props.baseliner();
|
|
2662
|
+
if (!b) return null;
|
|
2663
|
+
const bs = bands();
|
|
2664
|
+
const band = bs.find((x) => x.group.id === b.groupId) ?? bs.find((x) => b.time >= x.group.startTime && b.time <= x.group.endTime);
|
|
2665
|
+
if (!band) return null;
|
|
2666
|
+
return {
|
|
2667
|
+
top: yForPosition(band, resolveOffset(band.group, b.offset))
|
|
2668
|
+
};
|
|
2669
|
+
});
|
|
2670
|
+
let trackEl;
|
|
2671
|
+
const yInTrack = (ev) => {
|
|
2672
|
+
const rect = trackEl.getBoundingClientRect();
|
|
2673
|
+
return ev.clientY - rect.top;
|
|
2674
|
+
};
|
|
2675
|
+
const beginDrag = (ev) => {
|
|
2676
|
+
if (!trackEl || bands().length === 0) return;
|
|
2677
|
+
ev.preventDefault();
|
|
2678
|
+
ev.stopPropagation();
|
|
2679
|
+
const grabOffset = yInTrack(ev) - (tokenGeometry()?.top ?? 0);
|
|
2680
|
+
const s = props.scrubber();
|
|
2681
|
+
let last = s ? `${s.groupId}:${s.offset}` : null;
|
|
2682
|
+
const onMove = (e) => {
|
|
2683
|
+
const pos = positionForY(yInTrack(e) - grabOffset);
|
|
2684
|
+
if (!pos) return;
|
|
2685
|
+
const key = `${pos.group.id}:${pos.offset}`;
|
|
2686
|
+
if (key === last) return;
|
|
2687
|
+
last = key;
|
|
2688
|
+
const first = bands()[0];
|
|
2689
|
+
if (first && pos.group.id === first.group.id && pos.offset === 0) {
|
|
2690
|
+
props.onReturnToLatest();
|
|
2691
|
+
} else {
|
|
2692
|
+
scrubTo(pos.group, pos.offset);
|
|
2693
|
+
}
|
|
2694
|
+
};
|
|
2695
|
+
const target = ev.currentTarget;
|
|
2696
|
+
target.setPointerCapture(ev.pointerId);
|
|
2697
|
+
const onUp = () => {
|
|
2698
|
+
target.removeEventListener("pointermove", onMove);
|
|
2699
|
+
target.removeEventListener("pointerup", onUp);
|
|
2700
|
+
target.removeEventListener("pointercancel", onUp);
|
|
2701
|
+
};
|
|
2702
|
+
target.addEventListener("pointermove", onMove);
|
|
2703
|
+
target.addEventListener("pointerup", onUp);
|
|
2704
|
+
target.addEventListener("pointercancel", onUp);
|
|
2705
|
+
};
|
|
2706
|
+
const beginBaselineDrag = (ev) => {
|
|
2707
|
+
if (!trackEl || bands().length === 0) return;
|
|
2708
|
+
ev.preventDefault();
|
|
2709
|
+
ev.stopPropagation();
|
|
2710
|
+
const grabOffset = yInTrack(ev) - (baselineGeometry()?.top ?? 0);
|
|
2711
|
+
let last = null;
|
|
2712
|
+
const onMove = (e) => {
|
|
2713
|
+
const pos = positionForY(yInTrack(e) - grabOffset);
|
|
2714
|
+
if (!pos) return;
|
|
2715
|
+
const key = `${pos.group.id}:${pos.offset}`;
|
|
2716
|
+
if (key === last) return;
|
|
2717
|
+
last = key;
|
|
2718
|
+
baselineScrubTo(pos.group, pos.offset);
|
|
2719
|
+
};
|
|
2720
|
+
const target = ev.currentTarget;
|
|
2721
|
+
target.setPointerCapture(ev.pointerId);
|
|
2722
|
+
const onUp = () => {
|
|
2723
|
+
target.removeEventListener("pointermove", onMove);
|
|
2724
|
+
target.removeEventListener("pointerup", onUp);
|
|
2725
|
+
target.removeEventListener("pointercancel", onUp);
|
|
2726
|
+
};
|
|
2727
|
+
target.addEventListener("pointermove", onMove);
|
|
2728
|
+
target.addEventListener("pointerup", onUp);
|
|
2729
|
+
target.addEventListener("pointercancel", onUp);
|
|
2730
|
+
};
|
|
2731
|
+
const headChange = createMemo(() => {
|
|
2732
|
+
const s = props.scrubber();
|
|
2733
|
+
if (!s || s.offset === 0) return null;
|
|
2734
|
+
const group = groupForScrub(s);
|
|
2735
|
+
if (!group || s.head.hash === group.newestHash) return null;
|
|
2736
|
+
const rows = resolveGroupChanges(group);
|
|
2737
|
+
if (!rows || rows.length === 0) return null;
|
|
2738
|
+
return rows.find((r) => r.hash === s.head.hash && r.docUrl === s.head.docUrl) ?? rows[Math.min(s.offset, rows.length - 1)] ?? null;
|
|
2739
|
+
});
|
|
2740
|
+
const headCounts = createMemo(() => {
|
|
2741
|
+
const change = headChange();
|
|
2742
|
+
if (!change) return null;
|
|
2743
|
+
if (props.eyeOpen()) {
|
|
2744
|
+
const at = props.checkpoint();
|
|
2745
|
+
const srcs = sources();
|
|
2746
|
+
if (at && srcs) {
|
|
2747
|
+
let additions = 0;
|
|
2748
|
+
let deletions = 0;
|
|
2749
|
+
let spans = 0;
|
|
2750
|
+
for (const {
|
|
2751
|
+
member,
|
|
2752
|
+
handle
|
|
2753
|
+
} of srcs) {
|
|
2754
|
+
const entry = at[member.url];
|
|
2755
|
+
if (!entry?.from || !entry.to) continue;
|
|
2756
|
+
const doc = handle.doc();
|
|
2757
|
+
if (!doc) continue;
|
|
2758
|
+
spans++;
|
|
2759
|
+
const counts = computeRangeEditCounts(doc, decodeHeads(entry.from), decodeHeads(entry.to));
|
|
2760
|
+
additions += counts.additions;
|
|
2761
|
+
deletions += counts.deletions;
|
|
2762
|
+
}
|
|
2763
|
+
if (spans > 0) return {
|
|
2764
|
+
additions,
|
|
2765
|
+
deletions
|
|
2766
|
+
};
|
|
2767
|
+
}
|
|
2768
|
+
}
|
|
2769
|
+
return computeEditCounts(change.doc, change.hash, change.deps);
|
|
2770
|
+
});
|
|
2771
|
+
return (() => {
|
|
2772
|
+
var _el$35 = _tmpl$28();
|
|
2773
|
+
insert(_el$35, createComponent(Show, {
|
|
2774
|
+
get when() {
|
|
2775
|
+
return timeGroups().length > 0;
|
|
2776
|
+
},
|
|
2777
|
+
get fallback() {
|
|
2778
|
+
return createComponent(Show, {
|
|
2779
|
+
get when() {
|
|
2780
|
+
return isBuilding();
|
|
2781
|
+
},
|
|
2782
|
+
get fallback() {
|
|
2783
|
+
return _tmpl$29();
|
|
2784
|
+
},
|
|
2785
|
+
get children() {
|
|
2786
|
+
return _tmpl$23();
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
},
|
|
2790
|
+
get children() {
|
|
2791
|
+
var _el$36 = _tmpl$27(), _el$37 = _el$36.firstChild, _el$38 = _el$37.nextSibling;
|
|
2792
|
+
var _ref$ = trackEl;
|
|
2793
|
+
typeof _ref$ === "function" ? use(_ref$, _el$37) : trackEl = _el$37;
|
|
2794
|
+
use(setRowsEl, _el$38);
|
|
2795
|
+
insert(_el$38, createComponent(For, {
|
|
2796
|
+
get each() {
|
|
2797
|
+
return timeGroups();
|
|
2798
|
+
},
|
|
2799
|
+
children: (group) => createComponent(TimeGroupRow, {
|
|
2800
|
+
group,
|
|
2801
|
+
rowRef: (el) => rowEls.set(group.id, el),
|
|
2802
|
+
onSelect: () => selectGroup(group)
|
|
2803
|
+
})
|
|
2804
|
+
}), null);
|
|
2805
|
+
insert(_el$38, createComponent(Show, {
|
|
2806
|
+
get when() {
|
|
2807
|
+
return isBuilding();
|
|
2808
|
+
},
|
|
2809
|
+
get children() {
|
|
2810
|
+
return _tmpl$23();
|
|
2811
|
+
}
|
|
2812
|
+
}), null);
|
|
2813
|
+
insert(_el$36, createComponent(Show, {
|
|
2814
|
+
get when() {
|
|
2815
|
+
return tokenGeometry();
|
|
2816
|
+
},
|
|
2817
|
+
get children() {
|
|
2818
|
+
var _el$40 = _tmpl$24(), _el$41 = _el$40.firstChild, _el$42 = _el$41.nextSibling, _el$43 = _el$42.nextSibling;
|
|
2819
|
+
_el$42.$$pointerdown = beginDrag;
|
|
2820
|
+
_el$43.$$pointerdown = beginDrag;
|
|
2821
|
+
insert(_el$40, createComponent(Show, {
|
|
2822
|
+
get when() {
|
|
2823
|
+
return headChange();
|
|
2824
|
+
},
|
|
2825
|
+
children: (change) => (() => {
|
|
2826
|
+
var _el$51 = _tmpl$30(), _el$52 = _el$51.firstChild;
|
|
2827
|
+
_el$52.nextSibling;
|
|
2828
|
+
insert(_el$52, () => formatTime(change().time));
|
|
2829
|
+
insert(_el$51, createComponent(EditCounts, {
|
|
2830
|
+
get additions() {
|
|
2831
|
+
return headCounts()?.additions ?? 0;
|
|
2832
|
+
},
|
|
2833
|
+
get deletions() {
|
|
2834
|
+
return headCounts()?.deletions ?? 0;
|
|
2835
|
+
}
|
|
2836
|
+
}), null);
|
|
2837
|
+
return _el$51;
|
|
2838
|
+
})()
|
|
2839
|
+
}), null);
|
|
2840
|
+
effect((_$p) => setStyleProperty(_el$40, "top", `${tokenGeometry().top}px`));
|
|
2841
|
+
return _el$40;
|
|
2842
|
+
}
|
|
2843
|
+
}), null);
|
|
2844
|
+
insert(_el$36, createComponent(Show, {
|
|
2845
|
+
get when() {
|
|
2846
|
+
return baselineGeometry();
|
|
2847
|
+
},
|
|
2848
|
+
get children() {
|
|
2849
|
+
return [(() => {
|
|
2850
|
+
var _el$44 = _tmpl$25();
|
|
2851
|
+
effect((_p$) => {
|
|
2852
|
+
var _v$12 = `${Math.min(tokenGeometry()?.top ?? 0, baselineGeometry().top)}px`, _v$13 = `${Math.abs((tokenGeometry()?.top ?? 0) - baselineGeometry().top)}px`;
|
|
2853
|
+
_v$12 !== _p$.e && setStyleProperty(_el$44, "top", _p$.e = _v$12);
|
|
2854
|
+
_v$13 !== _p$.t && setStyleProperty(_el$44, "height", _p$.t = _v$13);
|
|
2855
|
+
return _p$;
|
|
2856
|
+
}, {
|
|
2857
|
+
e: void 0,
|
|
2858
|
+
t: void 0
|
|
2859
|
+
});
|
|
2860
|
+
return _el$44;
|
|
2861
|
+
})(), (() => {
|
|
2862
|
+
var _el$45 = _tmpl$26(), _el$46 = _el$45.firstChild, _el$47 = _el$46.nextSibling, _el$48 = _el$47.nextSibling;
|
|
2863
|
+
_el$47.$$pointerdown = beginBaselineDrag;
|
|
2864
|
+
_el$48.$$pointerdown = beginBaselineDrag;
|
|
2865
|
+
effect((_$p) => setStyleProperty(_el$45, "top", `${baselineGeometry().top}px`));
|
|
2866
|
+
return _el$45;
|
|
2867
|
+
})()];
|
|
2868
|
+
}
|
|
2869
|
+
}), null);
|
|
2870
|
+
return _el$36;
|
|
2871
|
+
}
|
|
2872
|
+
}));
|
|
2873
|
+
return _el$35;
|
|
2874
|
+
})();
|
|
2875
|
+
}
|
|
2876
|
+
function TimeGroupRow(props) {
|
|
2877
|
+
return (() => {
|
|
2878
|
+
var _el$54 = _tmpl$31(), _el$55 = _el$54.firstChild;
|
|
2879
|
+
_el$55.nextSibling;
|
|
2880
|
+
addEventListener(_el$54, "click", props.onSelect, true);
|
|
2881
|
+
var _ref$2 = props.rowRef;
|
|
2882
|
+
typeof _ref$2 === "function" ? use(_ref$2, _el$54) : props.rowRef = _el$54;
|
|
2883
|
+
insert(_el$54, createComponent(AuthorAvatars, {
|
|
2884
|
+
get actors() {
|
|
2885
|
+
return props.group.actors;
|
|
2886
|
+
}
|
|
2887
|
+
}), _el$55);
|
|
2888
|
+
insert(_el$55, () => formatTime(props.group.endTime));
|
|
2889
|
+
insert(_el$54, createComponent(EditCounts, {
|
|
2890
|
+
get additions() {
|
|
2891
|
+
return props.group.additions;
|
|
2892
|
+
},
|
|
2893
|
+
get deletions() {
|
|
2894
|
+
return props.group.deletions;
|
|
2895
|
+
}
|
|
2896
|
+
}), null);
|
|
2897
|
+
return _el$54;
|
|
2898
|
+
})();
|
|
2899
|
+
}
|
|
2900
|
+
function AuthorAvatars(props) {
|
|
2901
|
+
const authors = createMemo(() => resolveAuthors(props.actors));
|
|
2902
|
+
const visible = () => authors().slice(0, 3);
|
|
2903
|
+
const extra = () => Math.max(0, authors().length - 3);
|
|
2904
|
+
return (() => {
|
|
2905
|
+
var _el$57 = _tmpl$33();
|
|
2906
|
+
insert(_el$57, createComponent(For, {
|
|
2907
|
+
get each() {
|
|
2908
|
+
return visible();
|
|
2909
|
+
},
|
|
2910
|
+
children: (author, i) => (() => {
|
|
2911
|
+
var _el$60 = _tmpl$35();
|
|
2912
|
+
insert(_el$60, createComponent(Show, {
|
|
2913
|
+
get when() {
|
|
2914
|
+
return author.contactUrl;
|
|
2915
|
+
},
|
|
2916
|
+
get fallback() {
|
|
2917
|
+
return getInitials(author.key);
|
|
2918
|
+
},
|
|
2919
|
+
get children() {
|
|
2920
|
+
var _el$61 = _tmpl$34();
|
|
2921
|
+
_el$61._$owner = getOwner();
|
|
2922
|
+
effect(() => _el$61.docUrl = author.contactUrl);
|
|
2923
|
+
return _el$61;
|
|
2924
|
+
}
|
|
2925
|
+
}));
|
|
2926
|
+
effect((_p$) => {
|
|
2927
|
+
var _v$14 = author.contactUrl ? void 0 : author.key, _v$15 = author.contactUrl ? void 0 : authorColor(author.key), _v$16 = i() === 0 ? "0" : "-4px", _v$17 = String(visible().length - i());
|
|
2928
|
+
_v$14 !== _p$.e && setAttribute(_el$60, "title", _p$.e = _v$14);
|
|
2929
|
+
_v$15 !== _p$.t && setStyleProperty(_el$60, "background", _p$.t = _v$15);
|
|
2930
|
+
_v$16 !== _p$.a && setStyleProperty(_el$60, "margin-left", _p$.a = _v$16);
|
|
2931
|
+
_v$17 !== _p$.o && setStyleProperty(_el$60, "z-index", _p$.o = _v$17);
|
|
2932
|
+
return _p$;
|
|
2933
|
+
}, {
|
|
2934
|
+
e: void 0,
|
|
2935
|
+
t: void 0,
|
|
2936
|
+
a: void 0,
|
|
2937
|
+
o: void 0
|
|
2938
|
+
});
|
|
2939
|
+
return _el$60;
|
|
2940
|
+
})()
|
|
2941
|
+
}), null);
|
|
2942
|
+
insert(_el$57, createComponent(Show, {
|
|
2943
|
+
get when() {
|
|
2944
|
+
return extra() > 0;
|
|
2945
|
+
},
|
|
2946
|
+
get children() {
|
|
2947
|
+
var _el$58 = _tmpl$32();
|
|
2948
|
+
_el$58.firstChild;
|
|
2949
|
+
insert(_el$58, extra, null);
|
|
2950
|
+
return _el$58;
|
|
2951
|
+
}
|
|
2952
|
+
}), null);
|
|
2953
|
+
return _el$57;
|
|
2954
|
+
})();
|
|
2955
|
+
}
|
|
2956
|
+
function EditCounts(props) {
|
|
2957
|
+
return (() => {
|
|
2958
|
+
var _el$62 = _tmpl$38();
|
|
2959
|
+
insert(_el$62, createComponent(Show, {
|
|
2960
|
+
get when() {
|
|
2961
|
+
return props.additions > 0;
|
|
2962
|
+
},
|
|
2963
|
+
get children() {
|
|
2964
|
+
var _el$63 = _tmpl$36();
|
|
2965
|
+
_el$63.firstChild;
|
|
2966
|
+
insert(_el$63, () => props.additions, null);
|
|
2967
|
+
return _el$63;
|
|
2968
|
+
}
|
|
2969
|
+
}), null);
|
|
2970
|
+
insert(_el$62, createComponent(Show, {
|
|
2971
|
+
get when() {
|
|
2972
|
+
return props.deletions > 0;
|
|
2973
|
+
},
|
|
2974
|
+
get children() {
|
|
2975
|
+
var _el$65 = _tmpl$37();
|
|
2976
|
+
_el$65.firstChild;
|
|
2977
|
+
insert(_el$65, () => props.deletions, null);
|
|
2978
|
+
return _el$65;
|
|
2979
|
+
}
|
|
2980
|
+
}), null);
|
|
2981
|
+
return _el$62;
|
|
2982
|
+
})();
|
|
2983
|
+
}
|
|
2984
|
+
async function computeCheckpoint(repo, members, head, base) {
|
|
2985
|
+
const checkpoint = {};
|
|
2986
|
+
for (const member of members) {
|
|
2987
|
+
try {
|
|
2988
|
+
const handle = await repo.find(member.cloneUrl ?? member.url);
|
|
2989
|
+
const doc = handle.doc();
|
|
2990
|
+
if (!doc) continue;
|
|
2991
|
+
const since = member.clonedAt ? decodeHeads(member.clonedAt) : [];
|
|
2992
|
+
const metas = Automerge.getChangesMetaSince(doc, since);
|
|
2993
|
+
let to;
|
|
2994
|
+
if (member.url === head.docUrl) {
|
|
2995
|
+
to = encodeHeads([head.hash]);
|
|
2996
|
+
} else {
|
|
2997
|
+
let pinnedIndex = -1;
|
|
2998
|
+
let bestTime = -Infinity;
|
|
2999
|
+
metas.forEach((m, i) => {
|
|
3000
|
+
if (m.time <= head.time && m.time >= bestTime) {
|
|
3001
|
+
bestTime = m.time;
|
|
3002
|
+
pinnedIndex = i;
|
|
3003
|
+
}
|
|
3004
|
+
});
|
|
3005
|
+
if (pinnedIndex < 0) continue;
|
|
3006
|
+
to = encodeHeads([metas[pinnedIndex].hash]);
|
|
3007
|
+
}
|
|
3008
|
+
if (base === "none") {
|
|
3009
|
+
checkpoint[member.url] = {
|
|
3010
|
+
to
|
|
3011
|
+
};
|
|
3012
|
+
continue;
|
|
3013
|
+
}
|
|
3014
|
+
let fromIndex = -1;
|
|
3015
|
+
let fromTime = -Infinity;
|
|
3016
|
+
metas.forEach((m, i) => {
|
|
3017
|
+
if (m.time < base.beforeTime && m.time >= fromTime) {
|
|
3018
|
+
fromTime = m.time;
|
|
3019
|
+
fromIndex = i;
|
|
3020
|
+
}
|
|
3021
|
+
});
|
|
3022
|
+
const from = fromIndex >= 0 ? encodeHeads([metas[fromIndex].hash]) : member.clonedAt ?? encodeHeads([]);
|
|
3023
|
+
checkpoint[member.url] = {
|
|
3024
|
+
from,
|
|
3025
|
+
to
|
|
3026
|
+
};
|
|
3027
|
+
} catch (err) {
|
|
3028
|
+
console.warn("[drafts] failed to compute checkpoint for member:", member, err);
|
|
3029
|
+
}
|
|
3030
|
+
}
|
|
3031
|
+
return checkpoint;
|
|
3032
|
+
}
|
|
3033
|
+
const [actorContacts, setActorContacts] = createSignal({});
|
|
3034
|
+
function resolveAuthors(actors) {
|
|
3035
|
+
const attribution = actorContacts();
|
|
3036
|
+
const out = [];
|
|
3037
|
+
const seen = /* @__PURE__ */ new Set();
|
|
3038
|
+
for (const actor of actors) {
|
|
3039
|
+
const contactUrl = attribution[actor] ?? null;
|
|
3040
|
+
const key = contactUrl ?? actor;
|
|
3041
|
+
if (seen.has(key)) continue;
|
|
3042
|
+
seen.add(key);
|
|
3043
|
+
out.push({
|
|
3044
|
+
key,
|
|
3045
|
+
contactUrl
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
return out;
|
|
3049
|
+
}
|
|
3050
|
+
function authorColor(authorId) {
|
|
3051
|
+
let hash = 0;
|
|
3052
|
+
for (let i = 0; i < authorId.length; i++) {
|
|
3053
|
+
hash = authorId.charCodeAt(i) + ((hash << 5) - hash);
|
|
3054
|
+
}
|
|
3055
|
+
const h = Math.abs(hash) % 360;
|
|
3056
|
+
return `hsl(${h}, 45%, 63%)`;
|
|
3057
|
+
}
|
|
3058
|
+
function getInitials(authorId) {
|
|
3059
|
+
return authorId.slice(0, 2).toUpperCase();
|
|
3060
|
+
}
|
|
3061
|
+
const [nowMs, setNowMs] = createSignal(Date.now());
|
|
3062
|
+
setInterval(() => setNowMs(Date.now()), 3e4);
|
|
3063
|
+
const RELATIVE_TIME = new Intl.RelativeTimeFormat(void 0, {
|
|
3064
|
+
numeric: "auto"
|
|
3065
|
+
});
|
|
3066
|
+
function formatTime(timeSeconds) {
|
|
3067
|
+
if (!timeSeconds) return "";
|
|
3068
|
+
const date = new Date(timeSeconds * 1e3);
|
|
3069
|
+
const seconds = Math.max(0, Math.floor((nowMs() - date.getTime()) / 1e3));
|
|
3070
|
+
const minutes = Math.floor(seconds / 60);
|
|
3071
|
+
const hours = Math.floor(minutes / 60);
|
|
3072
|
+
const days = Math.floor(hours / 24);
|
|
3073
|
+
if (minutes < 1) return "now";
|
|
3074
|
+
if (hours < 1) return RELATIVE_TIME.format(-minutes, "minute");
|
|
3075
|
+
if (days < 1) return RELATIVE_TIME.format(-hours, "hour");
|
|
3076
|
+
if (days < 7) return RELATIVE_TIME.format(-days, "day");
|
|
3077
|
+
const day = date.toLocaleDateString(void 0, {
|
|
3078
|
+
month: "short",
|
|
3079
|
+
day: "numeric",
|
|
3080
|
+
year: date.getFullYear() === new Date(nowMs()).getFullYear() ? void 0 : "numeric"
|
|
3081
|
+
});
|
|
3082
|
+
const time = date.toLocaleTimeString(void 0, {
|
|
3083
|
+
hour: "2-digit",
|
|
3084
|
+
minute: "2-digit",
|
|
3085
|
+
hourCycle: "h23"
|
|
3086
|
+
});
|
|
3087
|
+
return `${day} ${time}`;
|
|
3088
|
+
}
|
|
3089
|
+
delegateEvents(["click", "dblclick", "keydown", "pointerdown"]);
|
|
3090
|
+
const renderDraftsSidebar = (_handle, element) => {
|
|
3091
|
+
return render(() => createComponent(RepoContext.Provider, {
|
|
3092
|
+
get value() {
|
|
3093
|
+
return element.repo;
|
|
3094
|
+
},
|
|
3095
|
+
get children() {
|
|
3096
|
+
return createComponent(DraftsSidebar, {
|
|
3097
|
+
element
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
}), element);
|
|
3101
|
+
};
|
|
3102
|
+
export {
|
|
3103
|
+
renderDraftsSidebar
|
|
3104
|
+
};
|
|
3105
|
+
//# sourceMappingURL=main-BR_tW0sp.js.map
|