@narumitw/pi-sync 0.50.3 → 0.51.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/chunks/{chunk-6LP3MCR2.ts → chunk-2JZFHNLM.ts} +3 -5
- package/dist/chunks/chunk-2JZFHNLM.ts.map +7 -0
- package/dist/chunks/{chunk-LCRI3ABW.ts → chunk-2RZSQWBH.ts} +8 -19
- package/dist/chunks/chunk-2RZSQWBH.ts.map +7 -0
- package/dist/chunks/{chunk-IJ6FJFA5.ts → chunk-4AIHXRKE.ts} +11 -17
- package/dist/chunks/chunk-4AIHXRKE.ts.map +7 -0
- package/dist/chunks/{chunk-C7XY5NF5.ts → chunk-4MPIH46N.ts} +29 -51
- package/dist/chunks/chunk-4MPIH46N.ts.map +7 -0
- package/dist/chunks/{chunk-5B3FXBLE.ts → chunk-6546TS54.ts} +16 -20
- package/dist/chunks/chunk-6546TS54.ts.map +7 -0
- package/dist/chunks/{chunk-Q2TUM5X6.ts → chunk-6NT3YFGY.ts} +2 -2
- package/dist/chunks/chunk-6NT3YFGY.ts.map +7 -0
- package/dist/chunks/{chunk-2B2ELUF5.ts → chunk-7D5SF7P3.ts} +1 -1
- package/dist/chunks/chunk-7D5SF7P3.ts.map +7 -0
- package/dist/chunks/{chunk-YDWUZVPN.ts → chunk-7KHKWL3Q.ts} +28 -47
- package/dist/chunks/chunk-7KHKWL3Q.ts.map +7 -0
- package/dist/chunks/{chunk-ZI5MXMEX.ts → chunk-BVVP33U5.ts} +2 -4
- package/dist/chunks/chunk-BVVP33U5.ts.map +7 -0
- package/dist/chunks/{chunk-SZWBGPID.ts → chunk-DM4X4FIA.ts} +8 -13
- package/dist/chunks/chunk-DM4X4FIA.ts.map +7 -0
- package/dist/chunks/{chunk-SL64RLXR.ts → chunk-DZUSTGQV.ts} +8 -16
- package/dist/chunks/chunk-DZUSTGQV.ts.map +7 -0
- package/dist/chunks/{chunk-YHEMENKV.ts → chunk-E42GXD6D.ts} +13 -19
- package/dist/chunks/chunk-E42GXD6D.ts.map +7 -0
- package/dist/chunks/{chunk-Z4DQF6L3.ts → chunk-ECYCNZOE.ts} +1 -1
- package/dist/chunks/chunk-ECYCNZOE.ts.map +7 -0
- package/dist/chunks/{chunk-OEX4ABY6.ts → chunk-EEJPYTWE.ts} +11 -21
- package/dist/chunks/chunk-EEJPYTWE.ts.map +7 -0
- package/dist/chunks/{chunk-CPEHGXJV.ts → chunk-G4RUXRWZ.ts} +3 -5
- package/dist/chunks/chunk-G4RUXRWZ.ts.map +7 -0
- package/dist/chunks/{chunk-T2B5IU74.ts → chunk-HGJH56KR.ts} +4 -4
- package/dist/chunks/chunk-HGJH56KR.ts.map +7 -0
- package/dist/chunks/{chunk-JAAKLL6M.ts → chunk-IVXEDPAD.ts} +10 -34
- package/dist/chunks/chunk-IVXEDPAD.ts.map +7 -0
- package/dist/chunks/chunk-K3RIEL6J.ts +20 -0
- package/dist/chunks/chunk-K3RIEL6J.ts.map +7 -0
- package/dist/chunks/{chunk-LTBWGSBV.ts → chunk-MSSW6F3R.ts} +1 -1
- package/dist/chunks/chunk-MSSW6F3R.ts.map +7 -0
- package/dist/chunks/{chunk-T3HH3UEJ.ts → chunk-OVDTF5CH.ts} +4 -10
- package/dist/chunks/chunk-OVDTF5CH.ts.map +7 -0
- package/dist/chunks/{chunk-NJFUIVYL.ts → chunk-PWMWOJP4.ts} +1 -1
- package/dist/chunks/chunk-PWMWOJP4.ts.map +7 -0
- package/dist/chunks/{chunk-HUDQATCL.ts → chunk-Q32CQWPM.ts} +6 -8
- package/dist/chunks/chunk-Q32CQWPM.ts.map +7 -0
- package/dist/chunks/{chunk-ETTRJDNP.ts → chunk-QLSN7AGX.ts} +6 -13
- package/dist/chunks/chunk-QLSN7AGX.ts.map +7 -0
- package/dist/chunks/{chunk-THMF4BVL.ts → chunk-SWUXA3MH.ts} +1 -1
- package/dist/chunks/chunk-SWUXA3MH.ts.map +7 -0
- package/dist/chunks/{chunk-TEN2ZBJV.ts → chunk-T2SJD3N7.ts} +2 -4
- package/dist/chunks/chunk-T2SJD3N7.ts.map +7 -0
- package/dist/chunks/{chunk-AUSDLD6F.ts → chunk-T56RNRZJ.ts} +10 -26
- package/dist/chunks/chunk-T56RNRZJ.ts.map +7 -0
- package/dist/chunks/{chunk-DHVFS4QK.ts → chunk-UA7RXOIT.ts} +37 -90
- package/dist/chunks/chunk-UA7RXOIT.ts.map +7 -0
- package/dist/chunks/{chunk-D5X27LCA.ts → chunk-UETBKTDI.ts} +1 -1
- package/dist/chunks/chunk-UETBKTDI.ts.map +7 -0
- package/dist/chunks/{chunk-CHH26D4F.ts → chunk-YFADF5ZQ.ts} +7 -6
- package/dist/chunks/chunk-YFADF5ZQ.ts.map +7 -0
- package/dist/chunks/{chunk-CCSN3F47.ts → chunk-YPXGU5OR.ts} +4 -4
- package/dist/chunks/chunk-YPXGU5OR.ts.map +7 -0
- package/dist/chunks/{file-selection-FTTKAOYX.ts → file-selection-RAVHRYFG.ts} +14 -26
- package/dist/chunks/file-selection-RAVHRYFG.ts.map +7 -0
- package/dist/chunks/{git-backend-GMVS4PD4.ts → git-backend-IAANDL3K.ts} +41 -104
- package/dist/chunks/git-backend-IAANDL3K.ts.map +7 -0
- package/dist/chunks/manager-result-dispatcher-OTUMQM26.ts +23 -0
- package/dist/chunks/{manager-ui-IPAOAWZA.ts → manager-ui-763NEKJY.ts} +124 -251
- package/dist/chunks/manager-ui-763NEKJY.ts.map +7 -0
- package/dist/chunks/remote-selection-ui-TZTJF32Y.ts +22 -0
- package/dist/chunks/{s3-backend-66O4FGBH.ts → s3-backend-XGXUOPUN.ts} +23 -54
- package/dist/chunks/s3-backend-XGXUOPUN.ts.map +7 -0
- package/dist/chunks/{setup-switch-IS2PEZ3J.ts → setup-switch-5G2VNJHL.ts} +9 -9
- package/dist/chunks/{setup-wizard-JUJZYNNM.ts → setup-wizard-YIEWGX32.ts} +12 -12
- package/dist/chunks/setup-wizard-YIEWGX32.ts.map +7 -0
- package/dist/chunks/{snapshot-HKEWCVK6.ts → snapshot-JL5DFSXI.ts} +5 -5
- package/dist/chunks/sync-inspection-P3ENJLNF.ts +20 -0
- package/dist/chunks/{sync-operations-LBX3GKCM.ts → sync-operations-AR76EB6C.ts} +85 -201
- package/dist/chunks/sync-operations-AR76EB6C.ts.map +7 -0
- package/dist/chunks/{sync-state-Q2FTXTW6.ts → sync-state-VHJUZ4YG.ts} +6 -6
- package/dist/chunks/{webdav-backend-ECIKD7SO.ts → webdav-backend-3EGUONAT.ts} +20 -51
- package/dist/chunks/webdav-backend-3EGUONAT.ts.map +7 -0
- package/dist/index.ts +49 -74
- package/dist/index.ts.map +3 -3
- package/docs/settings.md +9 -0
- package/package.json +61 -62
- package/src/backends/backend-factory.ts +15 -15
- package/src/backends/backend-identity.ts +31 -31
- package/src/backends/backend-types.ts +42 -42
- package/src/backends/git/git-backend.ts +714 -788
- package/src/backends/git/git-config.ts +104 -111
- package/src/backends/git/git-runner.ts +230 -247
- package/src/backends/git/git-storage.ts +228 -245
- package/src/backends/s3/s3-backend.ts +408 -441
- package/src/backends/s3/s3-client.ts +288 -333
- package/src/backends/sync-backend.ts +65 -65
- package/src/backends/webdav/webdav-backend.ts +542 -589
- package/src/backends/webdav/webdav-client.ts +292 -316
- package/src/backends/webdav/webdav-config.ts +57 -68
- package/src/commands/command-execution.ts +270 -289
- package/src/commands/command-handler.ts +160 -163
- package/src/commands/command-types.ts +13 -13
- package/src/commands/command.ts +190 -196
- package/src/commands/setup-completions.ts +1 -1
- package/src/paths.ts +47 -47
- package/src/settings/config-errors.ts +4 -5
- package/src/settings/config-file.ts +473 -506
- package/src/settings/config.ts +171 -174
- package/src/settings/settings-management.ts +191 -216
- package/src/settings/settings-store.ts +63 -77
- package/src/settings/settings-types.ts +78 -75
- package/src/settings/settings-validation.ts +328 -364
- package/src/snapshot/session-paths.ts +41 -55
- package/src/snapshot/snapshot-apply.ts +197 -229
- package/src/snapshot/snapshot-codec.ts +29 -31
- package/src/snapshot/snapshot-paths.ts +4 -6
- package/src/snapshot/snapshot-transaction.ts +161 -170
- package/src/snapshot/snapshot-types.ts +24 -24
- package/src/snapshot/snapshot.ts +310 -343
- package/src/state/json-file.ts +11 -11
- package/src/state/lock.ts +226 -242
- package/src/state/lockfile-fs.ts +20 -20
- package/src/state/operation-availability.ts +36 -41
- package/src/state/state-directory.ts +171 -173
- package/src/state/state-types.ts +15 -15
- package/src/state/sync-state-store.ts +23 -23
- package/src/sync/automatic-sync.ts +75 -80
- package/src/sync/remote-snapshot.ts +52 -60
- package/src/sync/setup-switch.ts +99 -114
- package/src/sync/signals.ts +3 -5
- package/src/sync/startup-check.ts +97 -111
- package/src/sync/sync-decision.ts +76 -80
- package/src/sync/sync-error-guidance.ts +41 -48
- package/src/sync/sync-errors.ts +10 -10
- package/src/sync/sync-inspection.ts +29 -33
- package/src/sync/sync-loaders.ts +28 -36
- package/src/sync/sync-mutations.ts +510 -622
- package/src/sync/sync-operations.ts +7 -7
- package/src/sync/sync-policy.ts +296 -326
- package/src/sync/sync-queries.ts +175 -194
- package/src/sync/sync-state.ts +74 -100
- package/src/sync-extension.ts +89 -101
- package/src/sync.ts +26 -26
- package/src/types.ts +37 -37
- package/src/ui/automatic-sync-summary.ts +1 -3
- package/src/ui/cancellable-operation.ts +89 -101
- package/src/ui/file-selection.ts +250 -274
- package/src/ui/manager-attention.ts +51 -63
- package/src/ui/manager-recovery.ts +48 -57
- package/src/ui/manager-result-dispatcher.ts +62 -76
- package/src/ui/manager-state.ts +200 -232
- package/src/ui/manager-ui.ts +264 -362
- package/src/ui/remote-selection-ui.ts +547 -588
- package/src/ui/secret-input.ts +197 -205
- package/src/ui/settings-ui.ts +175 -172
- package/src/ui/setup/git-ui.ts +232 -261
- package/src/ui/setup/s3-credentials-ui.ts +58 -65
- package/src/ui/setup/s3-ui.ts +171 -195
- package/src/ui/setup/setup-actions.ts +91 -101
- package/src/ui/setup/setup-location-ui.ts +121 -137
- package/src/ui/setup/setup-name-ui.ts +27 -27
- package/src/ui/setup/setup-prompts.ts +65 -65
- package/src/ui/setup/setup-review.ts +58 -59
- package/src/ui/setup/setup-switcher.ts +156 -160
- package/src/ui/setup/setup-wizard.ts +28 -28
- package/src/ui/setup/text-input.ts +54 -58
- package/src/ui/setup/webdav-ui.ts +246 -293
- package/src/ui/storage-connections-ui.ts +317 -369
- package/src/ui/sync-attention.ts +148 -164
- package/src/ui/sync-format.ts +110 -121
- package/src/ui/sync-resolution-ui.ts +147 -153
- package/src/ui/sync-setups-ui.ts +218 -238
- package/src/ui/sync-status.ts +17 -0
- package/src/ui/terminal-text.ts +2 -2
- package/dist/chunks/chunk-2B2ELUF5.ts.map +0 -7
- package/dist/chunks/chunk-5B3FXBLE.ts.map +0 -7
- package/dist/chunks/chunk-6LP3MCR2.ts.map +0 -7
- package/dist/chunks/chunk-AUSDLD6F.ts.map +0 -7
- package/dist/chunks/chunk-C7XY5NF5.ts.map +0 -7
- package/dist/chunks/chunk-CCSN3F47.ts.map +0 -7
- package/dist/chunks/chunk-CHH26D4F.ts.map +0 -7
- package/dist/chunks/chunk-CPEHGXJV.ts.map +0 -7
- package/dist/chunks/chunk-D5X27LCA.ts.map +0 -7
- package/dist/chunks/chunk-DHVFS4QK.ts.map +0 -7
- package/dist/chunks/chunk-ETTRJDNP.ts.map +0 -7
- package/dist/chunks/chunk-HUDQATCL.ts.map +0 -7
- package/dist/chunks/chunk-IJ6FJFA5.ts.map +0 -7
- package/dist/chunks/chunk-JAAKLL6M.ts.map +0 -7
- package/dist/chunks/chunk-LCRI3ABW.ts.map +0 -7
- package/dist/chunks/chunk-LTBWGSBV.ts.map +0 -7
- package/dist/chunks/chunk-NJFUIVYL.ts.map +0 -7
- package/dist/chunks/chunk-OEX4ABY6.ts.map +0 -7
- package/dist/chunks/chunk-Q2TUM5X6.ts.map +0 -7
- package/dist/chunks/chunk-SL64RLXR.ts.map +0 -7
- package/dist/chunks/chunk-SZWBGPID.ts.map +0 -7
- package/dist/chunks/chunk-T2B5IU74.ts.map +0 -7
- package/dist/chunks/chunk-T3HH3UEJ.ts.map +0 -7
- package/dist/chunks/chunk-TEN2ZBJV.ts.map +0 -7
- package/dist/chunks/chunk-THMF4BVL.ts.map +0 -7
- package/dist/chunks/chunk-YDWUZVPN.ts.map +0 -7
- package/dist/chunks/chunk-YHEMENKV.ts.map +0 -7
- package/dist/chunks/chunk-Z4DQF6L3.ts.map +0 -7
- package/dist/chunks/chunk-ZI5MXMEX.ts.map +0 -7
- package/dist/chunks/file-selection-FTTKAOYX.ts.map +0 -7
- package/dist/chunks/git-backend-GMVS4PD4.ts.map +0 -7
- package/dist/chunks/manager-result-dispatcher-ASKGOSW5.ts +0 -22
- package/dist/chunks/manager-ui-IPAOAWZA.ts.map +0 -7
- package/dist/chunks/remote-selection-ui-QDNR7WEI.ts +0 -21
- package/dist/chunks/s3-backend-66O4FGBH.ts.map +0 -7
- package/dist/chunks/setup-wizard-JUJZYNNM.ts.map +0 -7
- package/dist/chunks/sync-inspection-CST2KL65.ts +0 -20
- package/dist/chunks/sync-operations-LBX3GKCM.ts.map +0 -7
- package/dist/chunks/webdav-backend-ECIKD7SO.ts.map +0 -7
- /package/dist/chunks/{manager-result-dispatcher-ASKGOSW5.ts.map → manager-result-dispatcher-OTUMQM26.ts.map} +0 -0
- /package/dist/chunks/{remote-selection-ui-QDNR7WEI.ts.map → remote-selection-ui-TZTJF32Y.ts.map} +0 -0
- /package/dist/chunks/{setup-switch-IS2PEZ3J.ts.map → setup-switch-5G2VNJHL.ts.map} +0 -0
- /package/dist/chunks/{snapshot-HKEWCVK6.ts.map → snapshot-JL5DFSXI.ts.map} +0 -0
- /package/dist/chunks/{sync-inspection-CST2KL65.ts.map → sync-inspection-P3ENJLNF.ts.map} +0 -0
- /package/dist/chunks/{sync-state-Q2FTXTW6.ts.map → sync-state-VHJUZ4YG.ts.map} +0 -0
package/src/ui/sync-setups-ui.ts
CHANGED
|
@@ -7,271 +7,251 @@ import { syncErrorGuidance } from "../sync/sync-error-guidance.js";
|
|
|
7
7
|
import { syncIncludeSelection } from "../sync/sync-policy.js";
|
|
8
8
|
import { safeTerminalText } from "./terminal-text.js";
|
|
9
9
|
|
|
10
|
-
export async function countValidSyncSetups(
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
if (valid) count += 1;
|
|
26
|
-
}
|
|
27
|
-
return count;
|
|
10
|
+
export async function countValidSyncSetups(setups: Record<string, unknown> | undefined, signal?: AbortSignal) {
|
|
11
|
+
let count = 0;
|
|
12
|
+
for (const name of Object.keys(setups ?? {})) {
|
|
13
|
+
throwIfAborted(signal);
|
|
14
|
+
let valid = false;
|
|
15
|
+
try {
|
|
16
|
+
await loadConfig(name);
|
|
17
|
+
valid = true;
|
|
18
|
+
} catch {
|
|
19
|
+
// Invalid setups stay visible in management but are not switchable.
|
|
20
|
+
}
|
|
21
|
+
throwIfAborted(signal);
|
|
22
|
+
if (valid) count += 1;
|
|
23
|
+
}
|
|
24
|
+
return count;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
27
|
function throwIfAborted(signal?: AbortSignal) {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
? signal.reason
|
|
34
|
-
: new DOMException("The operation was aborted", "AbortError");
|
|
28
|
+
if (!signal?.aborted) return;
|
|
29
|
+
throw signal.reason instanceof Error ? signal.reason : new DOMException("The operation was aborted", "AbortError");
|
|
35
30
|
}
|
|
36
31
|
|
|
37
32
|
type SyncSetupActions = {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
add(signal?: AbortSignal): Promise<void>;
|
|
34
|
+
edit(name: string, signal?: AbortSignal): Promise<void>;
|
|
35
|
+
makeCurrent(name: string, signal?: AbortSignal): Promise<"exit" | undefined>;
|
|
36
|
+
remove(name: string, signal?: AbortSignal): Promise<void>;
|
|
42
37
|
};
|
|
43
38
|
|
|
44
39
|
interface SetupMenuState {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
40
|
+
setups: Record<string, unknown>;
|
|
41
|
+
active?: string;
|
|
42
|
+
selected?: {
|
|
43
|
+
name: string;
|
|
44
|
+
detail: string[];
|
|
45
|
+
valid: boolean;
|
|
46
|
+
removeUnavailable: boolean;
|
|
47
|
+
};
|
|
53
48
|
}
|
|
54
49
|
|
|
55
50
|
export async function showSyncSetups(
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
51
|
+
ctx: ExtensionCommandContext,
|
|
52
|
+
actions: SyncSetupActions,
|
|
53
|
+
signal?: AbortSignal,
|
|
59
54
|
): Promise<"exit" | undefined> {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
signal,
|
|
181
|
-
isCurrent: () => !signal?.aborted,
|
|
182
|
-
});
|
|
183
|
-
return exit ? "exit" : undefined;
|
|
55
|
+
let selectedName: string | undefined;
|
|
56
|
+
let exit = false;
|
|
57
|
+
const nameById = new Map<string, string>();
|
|
58
|
+
type Screen = "list" | "detail";
|
|
59
|
+
type Action = "add" | "select" | "make-current" | "edit" | "remove" | "back";
|
|
60
|
+
const menu = defineMenu<SetupMenuState, Screen, Action, ExtensionCommandContext>({
|
|
61
|
+
start: "list",
|
|
62
|
+
screens: {
|
|
63
|
+
list: ({ state }) => {
|
|
64
|
+
nameById.clear();
|
|
65
|
+
const names = Object.keys(state.setups).sort((left, right) => left.localeCompare(right));
|
|
66
|
+
return {
|
|
67
|
+
kind: "actions",
|
|
68
|
+
title: "Sync setups",
|
|
69
|
+
lines: ["What to sync, where to store it, and when to sync."],
|
|
70
|
+
items: [
|
|
71
|
+
{ id: "add", label: "Add sync setup", action: "add" },
|
|
72
|
+
...names.map((name, index) => {
|
|
73
|
+
const id = `setup:${index}`;
|
|
74
|
+
nameById.set(id, name);
|
|
75
|
+
return {
|
|
76
|
+
id,
|
|
77
|
+
label: `${safeTerminalText(name)}${name === state.active ? " (current)" : ""}`,
|
|
78
|
+
description: setupSummary(state.setups[name]),
|
|
79
|
+
action: "select" as const,
|
|
80
|
+
};
|
|
81
|
+
}),
|
|
82
|
+
],
|
|
83
|
+
hint: "back",
|
|
84
|
+
};
|
|
85
|
+
},
|
|
86
|
+
detail: ({ state }) => ({
|
|
87
|
+
kind: "actions",
|
|
88
|
+
title: state.selected ? `Sync setup “${safeTerminalText(state.selected.name)}”` : "Sync setup",
|
|
89
|
+
lines: state.selected?.detail ?? ["This sync setup no longer exists."],
|
|
90
|
+
items: state.selected
|
|
91
|
+
? [
|
|
92
|
+
...(!state.selected.name || state.selected.name === state.active || !state.selected.valid
|
|
93
|
+
? []
|
|
94
|
+
: [
|
|
95
|
+
{
|
|
96
|
+
id: "make-current",
|
|
97
|
+
label: "Make current…",
|
|
98
|
+
action: "make-current" as const,
|
|
99
|
+
},
|
|
100
|
+
]),
|
|
101
|
+
{ id: "edit", label: "Edit storage location…", action: "edit" },
|
|
102
|
+
...(state.selected.removeUnavailable
|
|
103
|
+
? []
|
|
104
|
+
: [
|
|
105
|
+
{
|
|
106
|
+
id: "remove",
|
|
107
|
+
label: "Remove sync setup…",
|
|
108
|
+
action: "remove" as const,
|
|
109
|
+
},
|
|
110
|
+
]),
|
|
111
|
+
{ id: "back", label: "Back", action: "back" },
|
|
112
|
+
]
|
|
113
|
+
: [{ id: "back", label: "Back", action: "back" }],
|
|
114
|
+
hint: "back",
|
|
115
|
+
}),
|
|
116
|
+
},
|
|
117
|
+
actions: {
|
|
118
|
+
add: async () => {
|
|
119
|
+
try {
|
|
120
|
+
await actions.add(signal);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (!signal?.aborted) {
|
|
123
|
+
ctx.ui.notify(`Sync setup was not added: ${menuErrorMessage(error)}`, "error");
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return { kind: "stay" };
|
|
127
|
+
},
|
|
128
|
+
select: async ({ itemId }) => {
|
|
129
|
+
selectedName = nameById.get(itemId);
|
|
130
|
+
return selectedName ? { kind: "to", screen: "detail" } : { kind: "rejected" };
|
|
131
|
+
},
|
|
132
|
+
"make-current": async () => {
|
|
133
|
+
if (!selectedName) return { kind: "rejected" };
|
|
134
|
+
try {
|
|
135
|
+
exit = (await actions.makeCurrent(selectedName, signal)) === "exit";
|
|
136
|
+
return exit ? { kind: "close" } : { kind: "stay" };
|
|
137
|
+
} catch (error) {
|
|
138
|
+
notifySetupChangeError(ctx, selectedName, error, signal);
|
|
139
|
+
return { kind: "stay" };
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
edit: async () => {
|
|
143
|
+
if (!selectedName) return { kind: "rejected" };
|
|
144
|
+
try {
|
|
145
|
+
await actions.edit(selectedName, signal);
|
|
146
|
+
} catch (error) {
|
|
147
|
+
notifySetupChangeError(ctx, selectedName, error, signal);
|
|
148
|
+
}
|
|
149
|
+
return { kind: "stay" };
|
|
150
|
+
},
|
|
151
|
+
remove: async () => {
|
|
152
|
+
if (!selectedName) return { kind: "rejected" };
|
|
153
|
+
const name = selectedName;
|
|
154
|
+
try {
|
|
155
|
+
await actions.remove(name, signal);
|
|
156
|
+
selectedName = undefined;
|
|
157
|
+
return { kind: "back" };
|
|
158
|
+
} catch (error) {
|
|
159
|
+
notifySetupChangeError(ctx, name, error, signal);
|
|
160
|
+
return { kind: "stay" };
|
|
161
|
+
}
|
|
162
|
+
},
|
|
163
|
+
back: async () => {
|
|
164
|
+
selectedName = undefined;
|
|
165
|
+
return { kind: "back" };
|
|
166
|
+
},
|
|
167
|
+
},
|
|
168
|
+
});
|
|
169
|
+
await runMenu(ctx, menu, {
|
|
170
|
+
getState: async () => loadSetupMenuState(selectedName, signal),
|
|
171
|
+
signal,
|
|
172
|
+
isCurrent: () => !signal?.aborted,
|
|
173
|
+
});
|
|
174
|
+
return exit ? "exit" : undefined;
|
|
184
175
|
}
|
|
185
176
|
|
|
186
|
-
async function loadSetupMenuState(
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
)
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
active,
|
|
228
|
-
selected: { name: selectedName, detail, valid, removeUnavailable },
|
|
229
|
-
};
|
|
177
|
+
async function loadSetupMenuState(selectedName: string | undefined, signal?: AbortSignal): Promise<SetupMenuState> {
|
|
178
|
+
const raw = await readLocalConfigObject();
|
|
179
|
+
throwIfAborted(signal);
|
|
180
|
+
const setups = ownRecord(raw?.syncSetups) ?? {};
|
|
181
|
+
const active = typeof raw?.activeSyncSetup === "string" ? raw.activeSyncSetup : undefined;
|
|
182
|
+
if (!selectedName || !ownRecord(setups[selectedName])) return { setups, active };
|
|
183
|
+
const setupCount = Object.keys(setups).length;
|
|
184
|
+
const isCurrent = selectedName === active;
|
|
185
|
+
let detail: string[];
|
|
186
|
+
let valid = true;
|
|
187
|
+
try {
|
|
188
|
+
const config = await loadConfig(selectedName);
|
|
189
|
+
throwIfAborted(signal);
|
|
190
|
+
const selection = syncIncludeSelection(config.include);
|
|
191
|
+
detail = [
|
|
192
|
+
`Status: ${isCurrent ? "Current" : "Not current"}`,
|
|
193
|
+
`Storage connection: ${safeTerminalText(config.connectionName)}`,
|
|
194
|
+
`Endpoint: ${storageEndpoint(config)}`,
|
|
195
|
+
`Storage location: ${storageLocation(config)}`,
|
|
196
|
+
`Included content: ${selection.builtIns.length} built-in groups · ${selection.custom.length} extra paths`,
|
|
197
|
+
`Sessions: ${selection.sessions ? "On — privacy-sensitive" : "Off"}`,
|
|
198
|
+
`Automatic sync: ${config.automatic ? "On" : "Off"}`,
|
|
199
|
+
isCurrent
|
|
200
|
+
? "Edit included content and automatic sync in /sync → Settings."
|
|
201
|
+
: "To edit included content or automatic sync, make this setup current, then open /sync → Settings.",
|
|
202
|
+
];
|
|
203
|
+
} catch (error) {
|
|
204
|
+
valid = false;
|
|
205
|
+
detail = [
|
|
206
|
+
`Status: Invalid${isCurrent ? " current setup" : ""}`,
|
|
207
|
+
`Reason: ${menuErrorMessage(error)}`,
|
|
208
|
+
"Make current and sync are unavailable until this setup is repaired.",
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
const removeUnavailable = isCurrent && setupCount > 1;
|
|
212
|
+
if (removeUnavailable) detail.push("Remove unavailable: switch to another setup first.");
|
|
213
|
+
return {
|
|
214
|
+
setups,
|
|
215
|
+
active,
|
|
216
|
+
selected: { name: selectedName, detail, valid, removeUnavailable },
|
|
217
|
+
};
|
|
230
218
|
}
|
|
231
219
|
|
|
232
|
-
function notifySetupChangeError(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
error: unknown,
|
|
236
|
-
signal?: AbortSignal,
|
|
237
|
-
) {
|
|
238
|
-
if (signal?.aborted) return;
|
|
239
|
-
ctx.ui.notify(
|
|
240
|
-
`Sync setup “${safeTerminalText(name)}” was not changed: ${menuErrorMessage(error)}`,
|
|
241
|
-
"error",
|
|
242
|
-
);
|
|
220
|
+
function notifySetupChangeError(ctx: ExtensionCommandContext, name: string, error: unknown, signal?: AbortSignal) {
|
|
221
|
+
if (signal?.aborted) return;
|
|
222
|
+
ctx.ui.notify(`Sync setup “${safeTerminalText(name)}” was not changed: ${menuErrorMessage(error)}`, "error");
|
|
243
223
|
}
|
|
244
224
|
|
|
245
225
|
function menuErrorMessage(error: unknown) {
|
|
246
|
-
|
|
226
|
+
return syncErrorGuidance(error);
|
|
247
227
|
}
|
|
248
228
|
|
|
249
229
|
function setupSummary(value: unknown) {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
230
|
+
const storage = ownRecord(ownRecord(value)?.storage);
|
|
231
|
+
if (!storage || typeof storage.connection !== "string") return "Invalid storage reference";
|
|
232
|
+
return safeTerminalText(
|
|
233
|
+
`${storage.connection} · ${storage.bucket ?? storage.branch ?? "WebDAV"} · ${storage.path ?? "missing path"}`,
|
|
234
|
+
);
|
|
255
235
|
}
|
|
256
236
|
|
|
257
237
|
function storageEndpoint(config: Awaited<ReturnType<typeof loadConfig>>) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
238
|
+
switch (config.backend.type) {
|
|
239
|
+
case "s3":
|
|
240
|
+
return safeTerminalText(config.backend.profile.endpoint);
|
|
241
|
+
case "git":
|
|
242
|
+
return safeTerminalText(config.backend.profile.remote);
|
|
243
|
+
case "webdav":
|
|
244
|
+
return safeTerminalText(config.backend.profile.url);
|
|
245
|
+
}
|
|
266
246
|
}
|
|
267
247
|
|
|
268
248
|
function storageLocation(config: Awaited<ReturnType<typeof loadConfig>>) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
249
|
+
switch (config.backend.type) {
|
|
250
|
+
case "s3":
|
|
251
|
+
return safeTerminalText(`${config.backend.destination.bucket}/${config.storagePath}`);
|
|
252
|
+
case "git":
|
|
253
|
+
return safeTerminalText(`Git · ${config.backend.destination.branch}:${config.storagePath}`);
|
|
254
|
+
case "webdav":
|
|
255
|
+
return safeTerminalText(`WebDAV · ${config.storagePath}`);
|
|
256
|
+
}
|
|
277
257
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
|
|
3
|
+
const STATUS_KEY = "sync";
|
|
4
|
+
|
|
5
|
+
type SyncStatusContext = Pick<ExtensionContext, "hasUI" | "sessionManager" | "ui">;
|
|
6
|
+
|
|
7
|
+
const enabledBySession = new WeakMap<ExtensionContext["sessionManager"], boolean>();
|
|
8
|
+
|
|
9
|
+
export function configureSyncStatus(ctx: SyncStatusContext, enabled: boolean) {
|
|
10
|
+
enabledBySession.set(ctx.sessionManager, enabled);
|
|
11
|
+
if (!enabled && ctx.hasUI) ctx.ui.setStatus(STATUS_KEY, undefined);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function setSyncStatus(ctx: SyncStatusContext, value: string | undefined) {
|
|
15
|
+
if (!ctx.hasUI || (value !== undefined && enabledBySession.get(ctx.sessionManager) === false)) return;
|
|
16
|
+
ctx.ui.setStatus(STATUS_KEY, value);
|
|
17
|
+
}
|
package/src/ui/terminal-text.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export function safeTerminalText(value: string) {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.
|
|
3
|
+
return value.replace(/[\u0000-\u001f\u007f-\u009f]/gu, "?");
|
|
4
4
|
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/sync/sync-errors.ts"],
|
|
4
|
-
"sourcesContent": ["import type { SyncDecision } from \"./sync-decision.js\";\n\nexport class SetupPullRequiresUiError extends Error {}\n\nexport class SyncDecisionRequiredError extends Error {\n\treadonly decision: SyncDecision;\n\n\tconstructor(decision: SyncDecision) {\n\t\tsuper(decision.directMessage);\n\t\tthis.name = \"SyncDecisionRequiredError\";\n\t\tthis.decision = decision;\n\t}\n}\n\nexport function isSyncDecisionRequiredError(error: unknown): error is SyncDecisionRequiredError {\n\treturn error instanceof SyncDecisionRequiredError;\n}\n\nexport function errorMessage(error: unknown) {\n\tconst message = error instanceof Error ? error.message : String(error);\n\t// biome-ignore lint/suspicious/noControlCharactersInRegex: Escape untrusted terminal controls.\n\treturn message.replace(/[\\u0000-\\u001f\\u007f-\\u009f]/gu, \"?\");\n}\n"],
|
|
5
|
-
"mappings": ";;;;AAEO,IAAM,2BAAN,cAAuC,MAAM;AAAC;AAE9C,IAAM,4BAAN,cAAwC,MAAM;AAAA,EAC3C;AAAA,EAET,YAAY,UAAwB;AACnC,UAAM,SAAS,aAAa;AAC5B,SAAK,OAAO;AACZ,SAAK,WAAW;AAAA,EACjB;AACD;AAEO,SAAS,4BAA4B,OAAoD;AAC/F,SAAO,iBAAiB;AACzB;AAEO,SAAS,aAAa,OAAgB;AAC5C,QAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAErE,SAAO,QAAQ,QAAQ,kCAAkC,GAAG;AAC7D;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/ui/sync-attention.ts"],
|
|
4
|
-
"sourcesContent": ["import type { ExtensionContext } from \"@earendil-works/pi-coding-agent\";\nimport { truncateToWidth } from \"@earendil-works/pi-tui\";\nimport { syncCheckConfigFingerprint, syncConfigReviewFingerprint } from \"../settings/config.js\";\nimport type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport type { StartupObservation } from \"../sync/sync-inspection.js\";\nimport {\n\tcompareSyncInclude,\n\ttype RemoteSelectionDecision,\n\tsameSyncInclude,\n} from \"../sync/sync-policy.js\";\nimport { safeTerminalText } from \"./terminal-text.js\";\n\nconst STATUS_KEY = \"sync\";\nconst WIDGET_KEY = \"sync:attention\";\n\nexport type SyncAttentionOrigin = \"sync\" | \"pull\" | \"push\";\n\nexport interface SyncAttentionState {\n\tdecision: RemoteSelectionDecision;\n\torigin: SyncAttentionOrigin;\n\toffered: boolean;\n}\n\nexport function syncAttentionMatchesConfig(attention: SyncAttentionState, config: AnySyncConfig) {\n\treturn (\n\t\tattention.decision.setupName === config.setupName &&\n\t\tattention.decision.configIdentity === syncConfigReviewFingerprint(config) &&\n\t\tsameSyncInclude(attention.decision.localInclude, config.include)\n\t);\n}\n\nexport interface SyncAttentionController {\n\tobserve(observation: StartupObservation): void;\n\tobservation(): StartupObservation | undefined;\n\tclearObservation(): void;\n\tnotifyObservation(ctx: ExtensionContext): void;\n\tset(decision: RemoteSelectionDecision, origin: SyncAttentionOrigin): void;\n\tcurrent(): SyncAttentionState | undefined;\n\tmarkOffered(): boolean;\n\tclear(ctx: ExtensionContext): void;\n\treset(ctx: ExtensionContext): void;\n\tpublish(ctx: ExtensionContext, signal?: AbortSignal): Promise<void>;\n}\n\nexport function createSyncAttentionController(): SyncAttentionController {\n\tlet state: SyncAttentionState | undefined;\n\tlet observation: StartupObservation | undefined;\n\tlet generation = 0;\n\n\treturn {\n\t\tobserve(value) {\n\t\t\tgeneration++;\n\t\t\tobservation = value;\n\t\t},\n\t\tobservation() {\n\t\t\treturn observation;\n\t\t},\n\t\tclearObservation() {\n\t\t\tgeneration++;\n\t\t\tobservation = undefined;\n\t\t},\n\t\tnotifyObservation(ctx) {\n\t\t\tif (ctx.hasUI && observation && observationNeedsAttention(observation)) {\n\t\t\t\tctx.ui.notify(observationLines(observation).join(\"\\n\"), \"warning\");\n\t\t\t}\n\t\t},\n\t\tset(decision, origin) {\n\t\t\tgeneration++;\n\t\t\tstate = { decision, origin, offered: false };\n\t\t},\n\t\tcurrent() {\n\t\t\treturn state;\n\t\t},\n\t\tmarkOffered() {\n\t\t\tif (!state || state.offered) return false;\n\t\t\tstate = { ...state, offered: true };\n\t\t\treturn true;\n\t\t},\n\t\tclear(ctx) {\n\t\t\tgeneration++;\n\t\t\tstate = undefined;\n\t\t\tclearAttentionPresentation(ctx);\n\t\t},\n\t\treset(ctx) {\n\t\t\tgeneration++;\n\t\t\tstate = undefined;\n\t\t\tobservation = undefined;\n\t\t\tclearAttentionPresentation(ctx);\n\t\t},\n\t\tasync publish(ctx, signal) {\n\t\t\tconst currentGeneration = ++generation;\n\t\t\tif (signal?.aborted || !ctx.hasUI) return;\n\t\t\tconst classification = observation ? classifyObservation(observation) : \"none\";\n\t\t\tif (!state && (classification === \"none\" || classification === \"guidance\")) {\n\t\t\t\tclearAttentionPresentation(ctx);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (!state && classification === \"status\") {\n\t\t\t\tctx.ui.setWidget(WIDGET_KEY, undefined);\n\t\t\t\tctx.ui.setStatus(\n\t\t\t\t\tSTATUS_KEY,\n\t\t\t\t\tobservation?.inspection.remoteChanged\n\t\t\t\t\t\t? \"remote changes pending\"\n\t\t\t\t\t\t: \"local changes pending\",\n\t\t\t\t);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tconst presentation = state\n\t\t\t\t? attentionPresentation(state.decision)\n\t\t\t\t: {\n\t\t\t\t\t\tstatus: \"changes to review\",\n\t\t\t\t\t\tlines: observationLines(observation as StartupObservation),\n\t\t\t\t\t};\n\t\t\tif (ctx.mode !== \"tui\") {\n\t\t\t\tctx.ui.setWidget(WIDGET_KEY, undefined);\n\t\t\t\tctx.ui.setStatus(STATUS_KEY, presentation.status);\n\t\t\t\treturn;\n\t\t\t}\n\t\t\t// Keep Kit outside the eager startup graph; module loading owns no cancellable resources.\n\t\t\tconst { EditorStatusWidget } = await import(\"./attention-widget.js\").catch((error) => {\n\t\t\t\tif (generation === currentGeneration && !signal?.aborted) clearAttentionPresentation(ctx);\n\t\t\t\tthrow error;\n\t\t\t});\n\t\t\tif (generation !== currentGeneration || signal?.aborted) return;\n\t\t\tctx.ui.setStatus(STATUS_KEY, presentation.status);\n\t\t\tctx.ui.setWidget(\n\t\t\t\tWIDGET_KEY,\n\t\t\t\t(_tui, theme) =>\n\t\t\t\t\tnew EditorStatusWidget({\n\t\t\t\t\t\ttheme,\n\t\t\t\t\t\trenderBody: (width) =>\n\t\t\t\t\t\t\tpresentation.lines.map((line, index) =>\n\t\t\t\t\t\t\t\twidth === 0\n\t\t\t\t\t\t\t\t\t? \"\"\n\t\t\t\t\t\t\t\t\t: truncateToWidth(theme.fg(index === 0 ? \"warning\" : \"muted\", line), width, \"\u2026\"),\n\t\t\t\t\t\t\t),\n\t\t\t\t\t}),\n\t\t\t);\n\t\t},\n\t};\n}\n\nfunction attentionPresentation(decision: RemoteSelectionDecision) {\n\tconst setupName = safeTerminalText(decision.setupName);\n\tconst comparison = compareSyncInclude(decision.localInclude, decision.remoteInclude);\n\tconst difference =\n\t\tcomparison.remoteOnly.length === 0 && comparison.localOnly.length === 0\n\t\t\t? \"Only list order differs\"\n\t\t\t: `Remote ${comparison.remoteOnly.length} \u00B7 Device ${comparison.localOnly.length}`;\n\treturn {\n\t\tstatus: \"review needed\",\n\t\tlines: [`Pi Sync needs review \u00B7 ${setupName}`, difference, \"No changes \u00B7 Run /sync to review\"],\n\t};\n}\n\nexport function observationMatchesConfig(observation: StartupObservation, config: AnySyncConfig) {\n\treturn observation.configIdentity === syncCheckConfigFingerprint(config);\n}\n\nexport function observationSummary(observation: StartupObservation) {\n\tconst result = observation.inspection;\n\tif (result.emptyInclude) return \"No included content selected\";\n\tif (result.selectionState?.kind === \"different\")\n\t\treturn \"Synced-content list differs; review needed\";\n\tif (result.firstSync)\n\t\treturn result.head\n\t\t\t? \"No sync baseline; review remote and local content\"\n\t\t\t: \"Remote empty; no sync baseline\";\n\tif (!result.head) return \"Remote snapshot missing; review needed\";\n\tif (result.localChanged && result.remoteChanged)\n\t\treturn \"Local and remote changed since last sync; review needed\";\n\tif (result.remoteChanged) return \"Remote changed since last sync\";\n\tif (result.localChanged) return \"Local content changed since last sync\";\n\tif (result.selectionState?.kind === \"legacy\")\n\t\treturn \"Legacy remote has no authoritative content list\";\n\treturn \"No changes detected since last sync\";\n}\n\nexport type ObservationClassification = \"none\" | \"guidance\" | \"status\" | \"review\";\n\n/** Presentation only: never discard an observation or authorize a transfer here. */\nexport function classifyObservation(observation: StartupObservation): ObservationClassification {\n\tconst result = observation.inspection;\n\t// Sync now exits before reading remote content when nothing is selected.\n\tif (result.emptyInclude) return \"guidance\";\n\t// Ordering is part of the existing transfer policy, not merely display order.\n\tif (result.selectionState?.kind === \"different\") return \"review\";\n\tif (result.firstSync) return result.head ? \"review\" : \"guidance\";\n\tif (!result.head || (result.localChanged && result.remoteChanged)) return \"review\";\n\tif (result.localChanged || result.remoteChanged) return \"status\";\n\t// Legacy metadata alone requires no scope confirmation in transfer operations.\n\treturn \"none\";\n}\n\nexport function observationNeedsAttention(observation: StartupObservation) {\n\treturn classifyObservation(observation) === \"review\";\n}\n\nfunction observationLines(observation: StartupObservation) {\n\treturn [\n\t\t`Pi Sync check \u00B7 ${safeTerminalText(observation.setupName)}`,\n\t\tobservationSummary(observation),\n\t\t\"No startup transfer \u00B7 Run /sync to review\",\n\t];\n}\n\nfunction clearAttentionPresentation(ctx: ExtensionContext) {\n\tif (!ctx.hasUI) return;\n\tctx.ui.setStatus(STATUS_KEY, undefined);\n\tctx.ui.setWidget(WIDGET_KEY, undefined);\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;AACA,SAAS,uBAAuB;AAWhC,IAAM,aAAa;AACnB,IAAM,aAAa;AAUZ,SAAS,2BAA2B,WAA+B,QAAuB;AAChG,SACC,UAAU,SAAS,cAAc,OAAO,aACxC,UAAU,SAAS,mBAAmB,4BAA4B,MAAM,KACxE,gBAAgB,UAAU,SAAS,cAAc,OAAO,OAAO;AAEjE;AAeO,SAAS,gCAAyD;AACxE,MAAI;AACJ,MAAI;AACJ,MAAI,aAAa;AAEjB,SAAO;AAAA,IACN,QAAQ,OAAO;AACd;AACA,oBAAc;AAAA,IACf;AAAA,IACA,cAAc;AACb,aAAO;AAAA,IACR;AAAA,IACA,mBAAmB;AAClB;AACA,oBAAc;AAAA,IACf;AAAA,IACA,kBAAkB,KAAK;AACtB,UAAI,IAAI,SAAS,eAAe,0BAA0B,WAAW,GAAG;AACvE,YAAI,GAAG,OAAO,iBAAiB,WAAW,EAAE,KAAK,IAAI,GAAG,SAAS;AAAA,MAClE;AAAA,IACD;AAAA,IACA,IAAI,UAAU,QAAQ;AACrB;AACA,cAAQ,EAAE,UAAU,QAAQ,SAAS,MAAM;AAAA,IAC5C;AAAA,IACA,UAAU;AACT,aAAO;AAAA,IACR;AAAA,IACA,cAAc;AACb,UAAI,CAAC,SAAS,MAAM,QAAS,QAAO;AACpC,cAAQ,EAAE,GAAG,OAAO,SAAS,KAAK;AAClC,aAAO;AAAA,IACR;AAAA,IACA,MAAM,KAAK;AACV;AACA,cAAQ;AACR,iCAA2B,GAAG;AAAA,IAC/B;AAAA,IACA,MAAM,KAAK;AACV;AACA,cAAQ;AACR,oBAAc;AACd,iCAA2B,GAAG;AAAA,IAC/B;AAAA,IACA,MAAM,QAAQ,KAAK,QAAQ;AAC1B,YAAM,oBAAoB,EAAE;AAC5B,UAAI,QAAQ,WAAW,CAAC,IAAI,MAAO;AACnC,YAAM,iBAAiB,cAAc,oBAAoB,WAAW,IAAI;AACxE,UAAI,CAAC,UAAU,mBAAmB,UAAU,mBAAmB,aAAa;AAC3E,mCAA2B,GAAG;AAC9B;AAAA,MACD;AACA,UAAI,CAAC,SAAS,mBAAmB,UAAU;AAC1C,YAAI,GAAG,UAAU,YAAY,MAAS;AACtC,YAAI,GAAG;AAAA,UACN;AAAA,UACA,aAAa,WAAW,gBACrB,2BACA;AAAA,QACJ;AACA;AAAA,MACD;AACA,YAAM,eAAe,QAClB,sBAAsB,MAAM,QAAQ,IACpC;AAAA,QACA,QAAQ;AAAA,QACR,OAAO,iBAAiB,WAAiC;AAAA,MAC1D;AACF,UAAI,IAAI,SAAS,OAAO;AACvB,YAAI,GAAG,UAAU,YAAY,MAAS;AACtC,YAAI,GAAG,UAAU,YAAY,aAAa,MAAM;AAChD;AAAA,MACD;AAEA,YAAM,EAAE,mBAAmB,IAAI,MAAM,OAAO,gCAAuB,EAAE,MAAM,CAAC,UAAU;AACrF,YAAI,eAAe,qBAAqB,CAAC,QAAQ,QAAS,4BAA2B,GAAG;AACxF,cAAM;AAAA,MACP,CAAC;AACD,UAAI,eAAe,qBAAqB,QAAQ,QAAS;AACzD,UAAI,GAAG,UAAU,YAAY,aAAa,MAAM;AAChD,UAAI,GAAG;AAAA,QACN;AAAA,QACA,CAAC,MAAM,UACN,IAAI,mBAAmB;AAAA,UACtB;AAAA,UACA,YAAY,CAAC,UACZ,aAAa,MAAM;AAAA,YAAI,CAAC,MAAM,UAC7B,UAAU,IACP,KACA,gBAAgB,MAAM,GAAG,UAAU,IAAI,YAAY,SAAS,IAAI,GAAG,OAAO,QAAG;AAAA,UACjF;AAAA,QACF,CAAC;AAAA,MACH;AAAA,IACD;AAAA,EACD;AACD;AAEA,SAAS,sBAAsB,UAAmC;AACjE,QAAM,YAAY,iBAAiB,SAAS,SAAS;AACrD,QAAM,aAAa,mBAAmB,SAAS,cAAc,SAAS,aAAa;AACnF,QAAM,aACL,WAAW,WAAW,WAAW,KAAK,WAAW,UAAU,WAAW,IACnE,4BACA,UAAU,WAAW,WAAW,MAAM,gBAAa,WAAW,UAAU,MAAM;AAClF,SAAO;AAAA,IACN,QAAQ;AAAA,IACR,OAAO,CAAC,6BAA0B,SAAS,IAAI,YAAY,qCAAkC;AAAA,EAC9F;AACD;AAEO,SAAS,yBAAyB,aAAiC,QAAuB;AAChG,SAAO,YAAY,mBAAmB,2BAA2B,MAAM;AACxE;AAEO,SAAS,mBAAmB,aAAiC;AACnE,QAAM,SAAS,YAAY;AAC3B,MAAI,OAAO,aAAc,QAAO;AAChC,MAAI,OAAO,gBAAgB,SAAS;AACnC,WAAO;AACR,MAAI,OAAO;AACV,WAAO,OAAO,OACX,sDACA;AACJ,MAAI,CAAC,OAAO,KAAM,QAAO;AACzB,MAAI,OAAO,gBAAgB,OAAO;AACjC,WAAO;AACR,MAAI,OAAO,cAAe,QAAO;AACjC,MAAI,OAAO,aAAc,QAAO;AAChC,MAAI,OAAO,gBAAgB,SAAS;AACnC,WAAO;AACR,SAAO;AACR;AAKO,SAAS,oBAAoB,aAA4D;AAC/F,QAAM,SAAS,YAAY;AAE3B,MAAI,OAAO,aAAc,QAAO;AAEhC,MAAI,OAAO,gBAAgB,SAAS,YAAa,QAAO;AACxD,MAAI,OAAO,UAAW,QAAO,OAAO,OAAO,WAAW;AACtD,MAAI,CAAC,OAAO,QAAS,OAAO,gBAAgB,OAAO,cAAgB,QAAO;AAC1E,MAAI,OAAO,gBAAgB,OAAO,cAAe,QAAO;AAExD,SAAO;AACR;AAEO,SAAS,0BAA0B,aAAiC;AAC1E,SAAO,oBAAoB,WAAW,MAAM;AAC7C;AAEA,SAAS,iBAAiB,aAAiC;AAC1D,SAAO;AAAA,IACN,sBAAmB,iBAAiB,YAAY,SAAS,CAAC;AAAA,IAC1D,mBAAmB,WAAW;AAAA,IAC9B;AAAA,EACD;AACD;AAEA,SAAS,2BAA2B,KAAuB;AAC1D,MAAI,CAAC,IAAI,MAAO;AAChB,MAAI,GAAG,UAAU,YAAY,MAAS;AACtC,MAAI,GAAG,UAAU,YAAY,MAAS;AACvC;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/backends/webdav/webdav-config.ts", "../../src/backends/backend-identity.ts"],
|
|
4
|
-
"sourcesContent": ["const DEFAULT_PATH = \"pi-sync\";\n\nexport function normalizeWebDavIdentityUrl(value: string) {\n\ttry {\n\t\tconst url = new URL(value.trim());\n\t\turl.username = \"\";\n\t\turl.password = \"\";\n\t\turl.search = \"\";\n\t\turl.hash = \"\";\n\t\turl.pathname = `${url.pathname.replace(/\\/+$/u, \"\")}/`;\n\t\treturn url.toString();\n\t} catch {\n\t\treturn value.trim();\n\t}\n}\n\nexport function normalizeWebDavUrl(value: string | undefined) {\n\tconst normalized = normalizeOptionalString(value);\n\tif (!normalized) return undefined;\n\tlet url: URL;\n\ttry {\n\t\turl = new URL(normalized);\n\t} catch {\n\t\tthrow new Error(\"Invalid pi-sync WebDAV URL.\");\n\t}\n\tif (url.username || url.password || url.search || url.hash) {\n\t\tthrow new Error(\n\t\t\t\"Invalid pi-sync WebDAV URL: credentials, query, and fragment are not allowed.\",\n\t\t);\n\t}\n\tconst loopback =\n\t\turl.hostname === \"127.0.0.1\" ||\n\t\turl.hostname === \"localhost\" ||\n\t\turl.hostname === \"[::1]\" ||\n\t\turl.hostname === \"::1\";\n\tif (url.protocol !== \"https:\" && !(url.protocol === \"http:\" && loopback)) {\n\t\tthrow new Error(\"Invalid pi-sync WebDAV URL: HTTPS is required except for loopback.\");\n\t}\n\turl.pathname = `${url.pathname.replace(/\\/+$/u, \"\")}/`;\n\treturn url.toString();\n}\n\nexport function normalizeWebDavPath(value: string | undefined) {\n\tif (value?.trim() === \".\" || value?.trim() === \"./\") return \"./\";\n\tconst normalized = trimSlashes(normalizeOptionalString(value) ?? DEFAULT_PATH);\n\tif (\n\t\t!normalized ||\n\t\tnormalized.includes(\"\\\\\") ||\n\t\thasControlCharacter(normalized) ||\n\t\tnormalized.split(\"/\").some((segment) => !segment || segment === \".\" || segment === \"..\")\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync WebDAV path.\");\n\t}\n\treturn normalized;\n}\n\nexport function validateWebDavNamespace(value: string) {\n\tif (\n\t\tvalue === \".\" ||\n\t\tvalue === \"..\" ||\n\t\tvalue.includes(\"/\") ||\n\t\tvalue.includes(\"\\\\\") ||\n\t\thasControlCharacter(value)\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync WebDAV namespace.\");\n\t}\n}\n\nexport function validateWebDavCredentials(username: string, password?: string) {\n\tif (\n\t\tusername.includes(\":\") ||\n\t\thasControlCharacter(username) ||\n\t\t(password !== undefined && hasControlCharacter(password))\n\t) {\n\t\tthrow new Error(\"Invalid pi-sync WebDAV credentials.\");\n\t}\n}\n\nfunction normalizeOptionalString(value: string | undefined) {\n\tconst normalized = value?.trim();\n\treturn normalized || undefined;\n}\n\nfunction trimSlashes(value: string) {\n\treturn value.replace(/^\\/+|\\/+$/gu, \"\");\n}\n\nfunction hasControlCharacter(value: string) {\n\treturn [...value].some((character) => {\n\t\tconst code = character.codePointAt(0) ?? 0;\n\t\treturn code < 0x20 || (code >= 0x7f && code <= 0x9f);\n\t});\n}\n", "import type { AnySyncConfig } from \"../settings/settings-types.js\";\nimport { normalizeGitRemoteIdentity } from \"./git/git-config.js\";\nimport { normalizeWebDavIdentityUrl } from \"./webdav/webdav-config.js\";\n\nexport function backendIdentityCoordinates(config: AnySyncConfig) {\n\tswitch (config.backend.type) {\n\t\tcase \"s3\":\n\t\t\treturn JSON.stringify([\n\t\t\t\t\"s3\",\n\t\t\t\tnormalizeEndpointIdentity(config.backend.profile.endpoint),\n\t\t\t\tconfig.backend.destination.bucket,\n\t\t\t\tconfig.storagePath,\n\t\t\t]);\n\t\tcase \"git\":\n\t\t\treturn JSON.stringify([\n\t\t\t\t\"git\",\n\t\t\t\tnormalizeGitRemoteIdentity(config.backend.profile.remote),\n\t\t\t\tconfig.backend.destination.branch,\n\t\t\t\tconfig.storagePath,\n\t\t\t]);\n\t\tcase \"webdav\":\n\t\t\treturn JSON.stringify([\n\t\t\t\t\"webdav\",\n\t\t\t\tnormalizeWebDavIdentityUrl(config.backend.profile.url),\n\t\t\t\tconfig.backend.profile.username,\n\t\t\t\tconfig.storagePath,\n\t\t\t]);\n\t}\n}\n\nexport function normalizeEndpointIdentity(endpoint: string) {\n\ttry {\n\t\tconst url = new URL(endpoint.trim());\n\t\turl.hostname = url.hostname.toLowerCase();\n\t\turl.pathname = url.pathname.replace(/\\/+$/gu, \"\");\n\t\treturn url.toString().replace(/\\/$/u, \"\");\n\t} catch {\n\t\treturn endpoint.trim();\n\t}\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/sync/sync-state.ts"],
|
|
4
|
-
"sourcesContent": ["import type { RemoteHead } from \"../backends/sync-backend.js\";\nimport { toPosix } from \"../paths.js\";\nimport {\n\tcanonicalSnapshotPathForConfig,\n\tfilterSnapshotForConfigPolicy,\n\tisConfiguredSnapshotPath,\n\tisSessionPath,\n} from \"../snapshot/snapshot.js\";\nimport type { Snapshot } from \"../snapshot/snapshot-types.js\";\nimport type { SyncState } from \"../state/state-types.js\";\nimport {\n\tcustomIncludePathsByLower,\n\tincludeFromSelectionConfig,\n\tnormalizeSyncInclude,\n\ttype SyncSelectionConfig,\n} from \"./sync-policy.js\";\n\ntype SyncPolicyConfig = SyncSelectionConfig;\n\nexport function hasLocalChanges(local: Snapshot, state: SyncState, config: SyncPolicyConfig) {\n\treturn !sameHashes(fileHashMap(local), stateHashMapForConfig(state, config));\n}\n\nexport function remoteChangedSinceState(\n\thead: RemoteHead | undefined,\n\tstate: SyncState,\n\tconfig: SyncPolicyConfig,\n\tsameRevision: (left: string, right: string) => boolean,\n) {\n\tif (!head) return Boolean(state.lastAppliedSnapshot);\n\tif (head.snapshotId !== state.lastAppliedSnapshot) return true;\n\tif (state.lastRemoteRevision && !sameRevision(head.revision, state.lastRemoteRevision))\n\t\treturn true;\n\tif (syncIncludeChanged(state, config)) return true;\n\treturn (\n\t\tincludeFromSelectionConfig(config).includes(\"sessions\") &&\n\t\t!state.include?.includes(\"sessions\") &&\n\t\thead.syncSessions\n\t);\n}\n\nexport function hasRemoteChanges(\n\tremote: Snapshot,\n\tstate: SyncState,\n\tconfig: SyncPolicyConfig,\n\tignoredPaths = new Set<string>(),\n) {\n\tif (remote.id === state.lastAppliedSnapshot && !syncPolicyChanged(state, config)) return false;\n\treturn !snapshotHashesMatchState(\n\t\tfilterSnapshotForConfigPolicy(remote, config),\n\t\tstate,\n\t\tconfig,\n\t\tignoredPaths,\n\t);\n}\n\nexport function sameHashes(left: Record<string, string>, right: Record<string, string>) {\n\tconst keys = new Set([...Object.keys(left), ...Object.keys(right)]);\n\tfor (const key of keys) if (left[key] !== right[key]) return false;\n\treturn true;\n}\n\nexport function fileHashMap(snapshot: Snapshot) {\n\treturn Object.fromEntries(snapshot.files.map((file) => [file.path, file.sha256]));\n}\n\nfunction stateHashMapForConfig(state: SyncState, config: SyncPolicyConfig) {\n\tconst includePaths = customIncludePathsByLower(includeFromSelectionConfig(config));\n\treturn Object.fromEntries(\n\t\tObject.entries(state.lastFileHashes)\n\t\t\t.filter(([filePath]) => isConfiguredSnapshotPath(filePath, config))\n\t\t\t.map(([filePath, hash]) => [canonicalSnapshotPathForConfig(filePath, includePaths), hash]),\n\t);\n}\n\nexport function snapshotHashesMatchState(\n\tsnapshot: Snapshot,\n\tstate: SyncState,\n\tconfig: SyncPolicyConfig,\n\tignoredPaths = new Set<string>(),\n) {\n\treturn sameHashes(\n\t\twithoutHashPaths(fileHashMap(snapshot), ignoredPaths),\n\t\twithoutHashPaths(stateHashMapForConfig(state, config), ignoredPaths),\n\t);\n}\n\nexport function snapshotsMatch(left: Snapshot, right: Snapshot) {\n\treturn (\n\t\tleft.syncSessions === right.syncSessions && sameHashes(fileHashMap(left), fileHashMap(right))\n\t);\n}\n\nfunction withoutHashPaths(hashes: Record<string, string>, ignoredPaths: Set<string>) {\n\tif (ignoredPaths.size === 0) return hashes;\n\treturn Object.fromEntries(\n\t\tObject.entries(hashes).filter(([filePath]) => !ignoredPaths.has(toPosix(filePath))),\n\t);\n}\n\nexport function syncPolicyChanged(state: SyncState, config: SyncPolicyConfig) {\n\treturn syncIncludeChanged(state, config);\n}\n\nexport function shouldRefreshSyncedState(\n\tremote: Snapshot,\n\thead: RemoteHead | undefined,\n\tstate: SyncState,\n\tconfig: SyncPolicyConfig,\n\tsameRevision: (left: string, right: string) => boolean,\n) {\n\treturn (\n\t\tremote.id !== state.lastAppliedSnapshot ||\n\t\tBoolean(\n\t\t\thead && (!state.lastRemoteRevision || !sameRevision(head.revision, state.lastRemoteRevision)),\n\t\t) ||\n\t\tsyncPolicyChanged(state, config)\n\t);\n}\n\nfunction syncIncludeChanged(state: SyncState, config: SyncPolicyConfig) {\n\tconst stored = state.include\n\t\t? normalizeSyncInclude(state.include)\n\t\t: includeFromSelectionConfig(state);\n\tconst current = includeFromSelectionConfig(config);\n\treturn stored.length !== current.length || stored.some((item, index) => item !== current[index]);\n}\n\nexport function settingsHashMap(snapshot: Snapshot) {\n\treturn Object.fromEntries(\n\t\tsnapshot.files\n\t\t\t.filter((file) => !isSessionPath(file.path))\n\t\t\t.map((file) => [file.path, file.sha256]),\n\t);\n}\n\nexport function sessionHashMap(snapshot: Snapshot) {\n\treturn Object.fromEntries(\n\t\tsnapshot.files\n\t\t\t.filter((file) => isSessionPath(file.path))\n\t\t\t.map((file) => [file.path, file.sha256]),\n\t);\n}\n\nexport function settingsHashMapFromState(state: SyncState) {\n\treturn Object.fromEntries(\n\t\tObject.entries(state.lastFileHashes).filter(([filePath]) => !isSessionPath(filePath)),\n\t);\n}\n\nexport function settingsHashesMatchState(remote: Snapshot, state: SyncState) {\n\treturn sameHashes(settingsHashMap(remote), settingsHashMapFromState(state));\n}\n\nexport function canPullRemoteSettingsOnFirstSync(local: Snapshot, remote: Snapshot) {\n\tconst remoteSettings = settingsHashMap(remote);\n\treturn Object.entries(settingsHashMap(local)).every(\n\t\t([filePath, hash]) => remoteSettings[filePath] === hash,\n\t);\n}\n\nexport function canPullRemoteSessionsOnFirstSync(local: Snapshot, remote: Snapshot) {\n\tconst localSessions = sessionHashMap(local);\n\tconst remoteSessions = sessionHashMap(remote);\n\treturn Object.entries(localSessions).every(\n\t\t([filePath, hash]) => remoteSessions[filePath] === hash,\n\t);\n}\n"],
|
|
5
|
-
"mappings": ";;;;;;;;;;;;;;;;AAmBO,SAAS,gBAAgB,OAAiB,OAAkB,QAA0B;AAC5F,SAAO,CAAC,WAAW,YAAY,KAAK,GAAG,sBAAsB,OAAO,MAAM,CAAC;AAC5E;AAEO,SAAS,wBACf,MACA,OACA,QACA,cACC;AACD,MAAI,CAAC,KAAM,QAAO,QAAQ,MAAM,mBAAmB;AACnD,MAAI,KAAK,eAAe,MAAM,oBAAqB,QAAO;AAC1D,MAAI,MAAM,sBAAsB,CAAC,aAAa,KAAK,UAAU,MAAM,kBAAkB;AACpF,WAAO;AACR,MAAI,mBAAmB,OAAO,MAAM,EAAG,QAAO;AAC9C,SACC,2BAA2B,MAAM,EAAE,SAAS,UAAU,KACtD,CAAC,MAAM,SAAS,SAAS,UAAU,KACnC,KAAK;AAEP;AAEO,SAAS,iBACf,QACA,OACA,QACA,eAAe,oBAAI,IAAY,GAC9B;AACD,MAAI,OAAO,OAAO,MAAM,uBAAuB,CAAC,kBAAkB,OAAO,MAAM,EAAG,QAAO;AACzF,SAAO,CAAC;AAAA,IACP,8BAA8B,QAAQ,MAAM;AAAA,IAC5C;AAAA,IACA;AAAA,IACA;AAAA,EACD;AACD;AAEO,SAAS,WAAW,MAA8B,OAA+B;AACvF,QAAM,OAAO,oBAAI,IAAI,CAAC,GAAG,OAAO,KAAK,IAAI,GAAG,GAAG,OAAO,KAAK,KAAK,CAAC,CAAC;AAClE,aAAW,OAAO,KAAM,KAAI,KAAK,GAAG,MAAM,MAAM,GAAG,EAAG,QAAO;AAC7D,SAAO;AACR;AAEO,SAAS,YAAY,UAAoB;AAC/C,SAAO,OAAO,YAAY,SAAS,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC,CAAC;AACjF;AAEA,SAAS,sBAAsB,OAAkB,QAA0B;AAC1E,QAAM,eAAe,0BAA0B,2BAA2B,MAAM,CAAC;AACjF,SAAO,OAAO;AAAA,IACb,OAAO,QAAQ,MAAM,cAAc,EACjC,OAAO,CAAC,CAAC,QAAQ,MAAM,yBAAyB,UAAU,MAAM,CAAC,EACjE,IAAI,CAAC,CAAC,UAAU,IAAI,MAAM,CAAC,+BAA+B,UAAU,YAAY,GAAG,IAAI,CAAC;AAAA,EAC3F;AACD;AAEO,SAAS,yBACf,UACA,OACA,QACA,eAAe,oBAAI,IAAY,GAC9B;AACD,SAAO;AAAA,IACN,iBAAiB,YAAY,QAAQ,GAAG,YAAY;AAAA,IACpD,iBAAiB,sBAAsB,OAAO,MAAM,GAAG,YAAY;AAAA,EACpE;AACD;AAEO,SAAS,eAAe,MAAgB,OAAiB;AAC/D,SACC,KAAK,iBAAiB,MAAM,gBAAgB,WAAW,YAAY,IAAI,GAAG,YAAY,KAAK,CAAC;AAE9F;AAEA,SAAS,iBAAiB,QAAgC,cAA2B;AACpF,MAAI,aAAa,SAAS,EAAG,QAAO;AACpC,SAAO,OAAO;AAAA,IACb,OAAO,QAAQ,MAAM,EAAE,OAAO,CAAC,CAAC,QAAQ,MAAM,CAAC,aAAa,IAAI,QAAQ,QAAQ,CAAC,CAAC;AAAA,EACnF;AACD;AAEO,SAAS,kBAAkB,OAAkB,QAA0B;AAC7E,SAAO,mBAAmB,OAAO,MAAM;AACxC;AAEO,SAAS,yBACf,QACA,MACA,OACA,QACA,cACC;AACD,SACC,OAAO,OAAO,MAAM,uBACpB;AAAA,IACC,SAAS,CAAC,MAAM,sBAAsB,CAAC,aAAa,KAAK,UAAU,MAAM,kBAAkB;AAAA,EAC5F,KACA,kBAAkB,OAAO,MAAM;AAEjC;AAEA,SAAS,mBAAmB,OAAkB,QAA0B;AACvE,QAAM,SAAS,MAAM,UAClB,qBAAqB,MAAM,OAAO,IAClC,2BAA2B,KAAK;AACnC,QAAM,UAAU,2BAA2B,MAAM;AACjD,SAAO,OAAO,WAAW,QAAQ,UAAU,OAAO,KAAK,CAAC,MAAM,UAAU,SAAS,QAAQ,KAAK,CAAC;AAChG;AAEO,SAAS,gBAAgB,UAAoB;AACnD,SAAO,OAAO;AAAA,IACb,SAAS,MACP,OAAO,CAAC,SAAS,CAAC,cAAc,KAAK,IAAI,CAAC,EAC1C,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,EACzC;AACD;AAEO,SAAS,eAAe,UAAoB;AAClD,SAAO,OAAO;AAAA,IACb,SAAS,MACP,OAAO,CAAC,SAAS,cAAc,KAAK,IAAI,CAAC,EACzC,IAAI,CAAC,SAAS,CAAC,KAAK,MAAM,KAAK,MAAM,CAAC;AAAA,EACzC;AACD;AAEO,SAAS,yBAAyB,OAAkB;AAC1D,SAAO,OAAO;AAAA,IACb,OAAO,QAAQ,MAAM,cAAc,EAAE,OAAO,CAAC,CAAC,QAAQ,MAAM,CAAC,cAAc,QAAQ,CAAC;AAAA,EACrF;AACD;AAEO,SAAS,yBAAyB,QAAkB,OAAkB;AAC5E,SAAO,WAAW,gBAAgB,MAAM,GAAG,yBAAyB,KAAK,CAAC;AAC3E;AAEO,SAAS,iCAAiC,OAAiB,QAAkB;AACnF,QAAM,iBAAiB,gBAAgB,MAAM;AAC7C,SAAO,OAAO,QAAQ,gBAAgB,KAAK,CAAC,EAAE;AAAA,IAC7C,CAAC,CAAC,UAAU,IAAI,MAAM,eAAe,QAAQ,MAAM;AAAA,EACpD;AACD;AAEO,SAAS,iCAAiC,OAAiB,QAAkB;AACnF,QAAM,gBAAgB,eAAe,KAAK;AAC1C,QAAM,iBAAiB,eAAe,MAAM;AAC5C,SAAO,OAAO,QAAQ,aAAa,EAAE;AAAA,IACpC,CAAC,CAAC,UAAU,IAAI,MAAM,eAAe,QAAQ,MAAM;AAAA,EACpD;AACD;",
|
|
6
|
-
"names": []
|
|
7
|
-
}
|