@pagepocket/cli 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/archive.js +45 -160
- package/dist/commands/strategy/ls.js +13 -6
- package/dist/commands/view.js +1 -1
- package/dist/services/strategy/strategy-normalize.js +38 -0
- package/dist/services/strategy/strategy-pack-store.js +1 -2
- package/dist/services/units/unit-validate.js +0 -3
- package/dist/units/network-observer-unit.js +1 -1
- package/dist/utils/archive-strategy.js +138 -0
- package/dist/vendor/content-reader.css +1 -0
- package/dist/vendor/content-reader.iife.js +60 -0
- package/dist/view-main-content.js +40 -0
- package/dist/view.js +71 -33
- package/package.json +20 -21
package/dist/commands/archive.js
CHANGED
|
@@ -1,139 +1,11 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import { pathToFileURL } from "node:url";
|
|
3
1
|
import { Args, Command, Flags } from "@oclif/core";
|
|
4
2
|
import { ga, ns, PagePocket } from "@pagepocket/lib";
|
|
5
3
|
import chalk from "chalk";
|
|
4
|
+
import ora from "ora";
|
|
6
5
|
import { ConfigService } from "../services/config-service.js";
|
|
7
6
|
import { loadConfiguredPlugins } from "../services/load-configured-plugins.js";
|
|
8
|
-
import { readBuiltinStrategy, listBuiltinStrategyNames } from "../services/strategy/builtin-strategy-registry.js";
|
|
9
|
-
import { normalizeStrategyUnits } from "../services/strategy/strategy-normalize.js";
|
|
10
7
|
import { StrategyService } from "../services/strategy/strategy-service.js";
|
|
11
|
-
import {
|
|
12
|
-
import { isUnitLike, resolveUnitConstructor } from "../services/units/unit-validate.js";
|
|
13
|
-
import { parsePinnedSpec } from "../services/user-packages/parse-pinned-spec.js";
|
|
14
|
-
import { uniq } from "../utils/array.js";
|
|
15
|
-
import { withSpinner } from "../utils/with-spinner.js";
|
|
16
|
-
const buildMissingStrategyError = (input) => {
|
|
17
|
-
const available = uniq([...input.installedNames, ...listBuiltinStrategyNames()])
|
|
18
|
-
.filter((name) => name.trim().length > 0)
|
|
19
|
-
.sort((left, right) => left.localeCompare(right));
|
|
20
|
-
const suffix = available.length > 0
|
|
21
|
-
? ` Available strategies: ${available.join(", ")}`
|
|
22
|
-
: " No strategies found.";
|
|
23
|
-
return new Error(`Strategy not found: ${input.strategyName}.${suffix}`);
|
|
24
|
-
};
|
|
25
|
-
const resolveStrategy = (input) => {
|
|
26
|
-
const installedNames = input.strategyService.listInstalledStrategyNames();
|
|
27
|
-
if (installedNames.includes(input.strategyName)) {
|
|
28
|
-
return {
|
|
29
|
-
name: input.strategyName,
|
|
30
|
-
strategyFile: input.strategyService.readStrategy(input.strategyName),
|
|
31
|
-
source: "installed"
|
|
32
|
-
};
|
|
33
|
-
}
|
|
34
|
-
const builtin = readBuiltinStrategy(input.strategyName);
|
|
35
|
-
if (builtin) {
|
|
36
|
-
return {
|
|
37
|
-
name: input.strategyName,
|
|
38
|
-
strategyFile: builtin,
|
|
39
|
-
source: "builtin"
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
throw buildMissingStrategyError({
|
|
43
|
-
strategyName: input.strategyName,
|
|
44
|
-
installedNames
|
|
45
|
-
});
|
|
46
|
-
};
|
|
47
|
-
const assertNoDuplicateUnitIds = (input) => {
|
|
48
|
-
const seen = new Set();
|
|
49
|
-
const duplicateUnit = input.units.find((unit) => {
|
|
50
|
-
if (seen.has(unit.id)) {
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
seen.add(unit.id);
|
|
54
|
-
return false;
|
|
55
|
-
});
|
|
56
|
-
if (duplicateUnit) {
|
|
57
|
-
throw new Error(`Duplicate unit id detected in strategy ${input.strategyName}: ${duplicateUnit.id}`);
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
const loadInstalledStrategyUnits = async (input) => {
|
|
61
|
-
const strategyService = new StrategyService(input.configService);
|
|
62
|
-
const unitStore = new UnitStore(input.configService);
|
|
63
|
-
strategyService.ensureConfigFileExists();
|
|
64
|
-
const strategyFile = strategyService.readStrategy(input.strategyName);
|
|
65
|
-
const normalized = normalizeStrategyUnits(strategyFile);
|
|
66
|
-
const installed = unitStore.readInstalledDependencyVersions();
|
|
67
|
-
const drift = normalized.flatMap((unit) => {
|
|
68
|
-
const pinned = parsePinnedSpec(unit.ref);
|
|
69
|
-
const installedVersion = installed[pinned.name];
|
|
70
|
-
if (!installedVersion || installedVersion !== pinned.version) {
|
|
71
|
-
return [
|
|
72
|
-
{
|
|
73
|
-
name: pinned.name,
|
|
74
|
-
pinned: pinned.version,
|
|
75
|
-
installed: installedVersion
|
|
76
|
-
}
|
|
77
|
-
];
|
|
78
|
-
}
|
|
79
|
-
return [];
|
|
80
|
-
});
|
|
81
|
-
if (drift.length > 0) {
|
|
82
|
-
const details = drift
|
|
83
|
-
.map((driftItem) => {
|
|
84
|
-
const installedText = typeof driftItem.installed === "string" ? driftItem.installed : "(not installed)";
|
|
85
|
-
return `${driftItem.name}: pinned ${driftItem.pinned}, installed ${installedText}`;
|
|
86
|
-
})
|
|
87
|
-
.join("; ");
|
|
88
|
-
throw new Error(`Strategy drift detected (${input.strategyName}). ${details}. ` +
|
|
89
|
-
`Fix: pp strategy update ${input.strategyName} OR pp strategy pin ${input.strategyName}`);
|
|
90
|
-
}
|
|
91
|
-
const units = await Promise.all(normalized.map(async (unit) => unitStore.instantiateFromRef(unit.ref, unit.args)));
|
|
92
|
-
assertNoDuplicateUnitIds({
|
|
93
|
-
strategyName: input.strategyName,
|
|
94
|
-
units
|
|
95
|
-
});
|
|
96
|
-
return units;
|
|
97
|
-
};
|
|
98
|
-
const instantiateBuiltinUnit = async (input) => {
|
|
99
|
-
const req = createRequire(import.meta.url);
|
|
100
|
-
const pinned = parsePinnedSpec(input.ref);
|
|
101
|
-
const resolved = req.resolve(pinned.name);
|
|
102
|
-
const mod = (await import(pathToFileURL(resolved).href));
|
|
103
|
-
const ctor = resolveUnitConstructor(mod);
|
|
104
|
-
if (!ctor) {
|
|
105
|
-
throw new Error(`Unit ${pinned.name} does not export a Unit constructor.`);
|
|
106
|
-
}
|
|
107
|
-
const instance = new ctor(...input.args);
|
|
108
|
-
if (!isUnitLike(instance)) {
|
|
109
|
-
throw new Error(`Unit ${pinned.name} exported constructor did not produce a Unit.`);
|
|
110
|
-
}
|
|
111
|
-
return instance;
|
|
112
|
-
};
|
|
113
|
-
const loadBuiltinStrategyUnits = async (input) => {
|
|
114
|
-
const normalized = normalizeStrategyUnits(input.strategyFile);
|
|
115
|
-
const units = await Promise.all(normalized.map(async (unit) => instantiateBuiltinUnit({
|
|
116
|
-
ref: unit.ref,
|
|
117
|
-
args: unit.args
|
|
118
|
-
})));
|
|
119
|
-
assertNoDuplicateUnitIds({
|
|
120
|
-
strategyName: input.strategyName,
|
|
121
|
-
units
|
|
122
|
-
});
|
|
123
|
-
return units;
|
|
124
|
-
};
|
|
125
|
-
const loadStrategyUnits = async (input) => {
|
|
126
|
-
if (input.strategy.source === "installed") {
|
|
127
|
-
return loadInstalledStrategyUnits({
|
|
128
|
-
strategyName: input.strategy.name,
|
|
129
|
-
configService: input.configService
|
|
130
|
-
});
|
|
131
|
-
}
|
|
132
|
-
return loadBuiltinStrategyUnits({
|
|
133
|
-
strategyName: input.strategy.name,
|
|
134
|
-
strategyFile: input.strategy.strategyFile
|
|
135
|
-
});
|
|
136
|
-
};
|
|
8
|
+
import { loadStrategyUnits, resolveStrategy } from "../utils/archive-strategy.js";
|
|
137
9
|
export default class ArchiveCommand extends Command {
|
|
138
10
|
static description = "Archive a web page as an offline snapshot.";
|
|
139
11
|
static args = {
|
|
@@ -165,38 +37,51 @@ export default class ArchiveCommand extends Command {
|
|
|
165
37
|
const timeoutMs = typeof flags.timeout === "number" ? flags.timeout : undefined;
|
|
166
38
|
const maxDurationMs = typeof flags.maxDuration === "number" ? flags.maxDuration : undefined;
|
|
167
39
|
const strategyName = typeof flags.strategy === "string" ? flags.strategy.trim() : undefined;
|
|
40
|
+
const configService = new ConfigService();
|
|
41
|
+
const name = strategyName && strategyName.length > 0 ? strategyName : "default";
|
|
42
|
+
const strategyService = new StrategyService(configService);
|
|
43
|
+
strategyService.ensureConfigFileExists();
|
|
44
|
+
const strategy = resolveStrategy({ strategyName: name, strategyService });
|
|
45
|
+
const units = await loadStrategyUnits({ strategy, configService });
|
|
46
|
+
const strategyFile = strategy.strategyFile;
|
|
47
|
+
const captureOptions = strategyFile.pipeline.captureOptions;
|
|
48
|
+
const effectiveTimeoutMs = typeof timeoutMs === "number" ? timeoutMs : captureOptions?.timeoutMs;
|
|
49
|
+
const effectiveMaxDurationMs = typeof maxDurationMs === "number" ? maxDurationMs : captureOptions?.maxDurationMs;
|
|
168
50
|
const pagepocket = PagePocket.fromURL(targetUrl);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
configService
|
|
182
|
-
});
|
|
183
|
-
const entryTarget = pagepocket;
|
|
184
|
-
const strategyFile = strategy.strategyFile;
|
|
185
|
-
const captureOptions = strategyFile.pipeline.captureOptions;
|
|
186
|
-
const effectiveTimeoutMs = typeof timeoutMs === "number" ? timeoutMs : captureOptions?.timeoutMs;
|
|
187
|
-
const effectiveMaxDurationMs = typeof maxDurationMs === "number" ? maxDurationMs : captureOptions?.maxDurationMs;
|
|
188
|
-
const result = await entryTarget.capture({
|
|
189
|
-
...(typeof effectiveTimeoutMs === "number" ? { timeoutMs: effectiveTimeoutMs } : {}),
|
|
190
|
-
...(typeof effectiveMaxDurationMs === "number"
|
|
191
|
-
? { maxDurationMs: effectiveMaxDurationMs }
|
|
192
|
-
: {}),
|
|
193
|
-
blacklist: [...ga, ...ns],
|
|
194
|
-
units,
|
|
195
|
-
plugins: await loadConfiguredPlugins().catch(() => [])
|
|
196
|
-
});
|
|
197
|
-
return result;
|
|
51
|
+
let spinner = ora();
|
|
52
|
+
let activeUnitId = "";
|
|
53
|
+
let activeUnitLabel = "";
|
|
54
|
+
const formatUnitLabel = (index, total, unitDescription) => `[${index + 1}/${total}] ${unitDescription}`;
|
|
55
|
+
pagepocket.on("unit:start", (e) => {
|
|
56
|
+
activeUnitId = e.unitId;
|
|
57
|
+
activeUnitLabel = formatUnitLabel(e.index, e.total, e.unitDescription);
|
|
58
|
+
spinner = ora(activeUnitLabel).start();
|
|
59
|
+
});
|
|
60
|
+
pagepocket.on("unit:log", (e) => {
|
|
61
|
+
if (e.unitId !== activeUnitId) {
|
|
62
|
+
return;
|
|
198
63
|
}
|
|
199
|
-
|
|
64
|
+
spinner.text = `${activeUnitLabel} ${chalk.gray(e.message)}`;
|
|
65
|
+
});
|
|
66
|
+
pagepocket.on("unit:end", (e) => {
|
|
67
|
+
spinner.succeed(formatUnitLabel(e.index, e.total, e.unitDescription));
|
|
68
|
+
});
|
|
69
|
+
let result;
|
|
70
|
+
try {
|
|
71
|
+
result = await pagepocket.capture({
|
|
72
|
+
...(typeof effectiveTimeoutMs === "number" ? { timeoutMs: effectiveTimeoutMs } : {}),
|
|
73
|
+
...(typeof effectiveMaxDurationMs === "number"
|
|
74
|
+
? { maxDurationMs: effectiveMaxDurationMs }
|
|
75
|
+
: {}),
|
|
76
|
+
blacklist: [...ga, ...ns],
|
|
77
|
+
units,
|
|
78
|
+
plugins: await loadConfiguredPlugins().catch(() => [])
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
spinner.fail();
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
200
85
|
this.log(chalk.green("All done! Snapshot created."));
|
|
201
86
|
if (result.kind === "zip") {
|
|
202
87
|
this.log(`Snapshot saved to ${chalk.cyan(result.zip.outputPath)}`);
|
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
import { Command } from "@oclif/core";
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import { listBuiltinStrategyNames, readBuiltinStrategy } from "../../services/strategy/builtin-strategy-registry.js";
|
|
4
|
-
import { normalizeStrategyUnits } from "../../services/strategy/strategy-normalize.js";
|
|
4
|
+
import { normalizeBuiltinStrategyUnits, normalizeStrategyUnits } from "../../services/strategy/strategy-normalize.js";
|
|
5
5
|
import { StrategyService } from "../../services/strategy/strategy-service.js";
|
|
6
6
|
import { uniq } from "../../utils/array.js";
|
|
7
|
-
const
|
|
7
|
+
const formatInstalledStrategy = (name, strategyFile) => {
|
|
8
8
|
const units = normalizeStrategyUnits(strategyFile);
|
|
9
9
|
const pipeline = units.map((unit) => unit.ref).join(" -> ");
|
|
10
10
|
return pipeline.length > 0 ? `${name}${chalk.dim(`(${pipeline})`)}` : name;
|
|
11
11
|
};
|
|
12
|
+
const formatBuiltinStrategy = (name, strategyFile) => {
|
|
13
|
+
const units = normalizeBuiltinStrategyUnits(strategyFile);
|
|
14
|
+
const pipeline = units.map((unit) => unit.name).join(" -> ");
|
|
15
|
+
return pipeline.length > 0 ? `${name}${chalk.dim(`(${pipeline})`)}` : name;
|
|
16
|
+
};
|
|
12
17
|
export default class StrategyLsCommand extends Command {
|
|
13
18
|
static description = "List available strategies.";
|
|
14
19
|
async run() {
|
|
@@ -25,11 +30,13 @@ export default class StrategyLsCommand extends Command {
|
|
|
25
30
|
return;
|
|
26
31
|
}
|
|
27
32
|
for (const name of names) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
: readBuiltinStrategy(name);
|
|
33
|
+
const isInstalled = installedNames.includes(name);
|
|
34
|
+
const strategyFile = isInstalled ? service.readStrategy(name) : readBuiltinStrategy(name);
|
|
31
35
|
if (strategyFile) {
|
|
32
|
-
|
|
36
|
+
const formatted = isInstalled
|
|
37
|
+
? formatInstalledStrategy(name, strategyFile)
|
|
38
|
+
: formatBuiltinStrategy(name, strategyFile);
|
|
39
|
+
this.log(formatted);
|
|
33
40
|
}
|
|
34
41
|
else {
|
|
35
42
|
this.log(name);
|
package/dist/commands/view.js
CHANGED
|
@@ -51,7 +51,7 @@ export default class ViewCommand extends Command {
|
|
|
51
51
|
host: flags.host,
|
|
52
52
|
port
|
|
53
53
|
});
|
|
54
|
-
this.log(`Serving ${chalk.cyan(rootDir)}`);
|
|
54
|
+
this.log(`Serving ${chalk.cyan(rootDir)} ${chalk.dim(`(${server.snapshotType})`)}`);
|
|
55
55
|
this.log(chalk.green(server.url));
|
|
56
56
|
}
|
|
57
57
|
}
|
|
@@ -43,3 +43,41 @@ export const normalizeStrategyUnits = (strategy) => {
|
|
|
43
43
|
const units = strategy.pipeline.units;
|
|
44
44
|
return units.map((spec, idx) => normalizeUnitSpec(spec, idx));
|
|
45
45
|
};
|
|
46
|
+
const parsePackageName = (spec) => {
|
|
47
|
+
const trimmed = spec.trim();
|
|
48
|
+
if (trimmed.startsWith("@")) {
|
|
49
|
+
const afterScope = trimmed.indexOf("/");
|
|
50
|
+
if (afterScope === -1) {
|
|
51
|
+
throw new Error(`Invalid scoped package spec: ${trimmed}`);
|
|
52
|
+
}
|
|
53
|
+
const versionSep = trimmed.indexOf("@", afterScope + 1);
|
|
54
|
+
return versionSep === -1 ? trimmed : trimmed.slice(0, versionSep);
|
|
55
|
+
}
|
|
56
|
+
const versionSep = trimmed.indexOf("@");
|
|
57
|
+
return versionSep === -1 ? trimmed : trimmed.slice(0, versionSep);
|
|
58
|
+
};
|
|
59
|
+
const normalizeBuiltinUnitSpec = (spec, idx) => {
|
|
60
|
+
if (typeof spec === "string") {
|
|
61
|
+
return { name: parsePackageName(spec), args: [] };
|
|
62
|
+
}
|
|
63
|
+
if (!isRecord(spec)) {
|
|
64
|
+
throw new Error(`Invalid unit spec at index ${idx}`);
|
|
65
|
+
}
|
|
66
|
+
const ref = spec.ref;
|
|
67
|
+
if (typeof ref !== "string") {
|
|
68
|
+
throw new Error(`Invalid unit ref at index ${idx}`);
|
|
69
|
+
}
|
|
70
|
+
const argsRaw = spec.args;
|
|
71
|
+
const args = typeof argsRaw === "undefined" ? [] : argsRaw;
|
|
72
|
+
if (!Array.isArray(args)) {
|
|
73
|
+
throw new Error(`Invalid unit args at index ${idx} (must be an array)`);
|
|
74
|
+
}
|
|
75
|
+
if (!isJsonValue(args)) {
|
|
76
|
+
throw new Error(`Invalid unit args at index ${idx} (must be JSON-only)`);
|
|
77
|
+
}
|
|
78
|
+
return { name: parsePackageName(ref), args };
|
|
79
|
+
};
|
|
80
|
+
export const normalizeBuiltinStrategyUnits = (strategy) => {
|
|
81
|
+
const units = strategy.pipeline.units;
|
|
82
|
+
return units.map((spec, idx) => normalizeBuiltinUnitSpec(spec, idx));
|
|
83
|
+
};
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { parsePinnedSpec } from "../user-packages/parse-pinned-spec.js";
|
|
3
|
-
import { installPinnedPackage } from "../user-packages/user-package-installer.js";
|
|
4
|
-
import { updatePackageToLatest } from "../user-packages/user-package-installer.js";
|
|
3
|
+
import { installPinnedPackage, updatePackageToLatest } from "../user-packages/user-package-installer.js";
|
|
5
4
|
import { UserPackageStore } from "../user-packages/user-package-store.js";
|
|
6
5
|
const STRATEGY_PACKS_KIND = "strategy-packs";
|
|
7
6
|
const STRATEGY_PACKS_PACKAGE_JSON_NAME = "pagepocket-user-strategy-packs";
|
|
@@ -7,9 +7,6 @@ export const isUnitLike = (value) => {
|
|
|
7
7
|
if (typeof value.id !== "string" || value.id.trim().length === 0) {
|
|
8
8
|
return false;
|
|
9
9
|
}
|
|
10
|
-
if (typeof value.kind !== "string" || value.kind.trim().length === 0) {
|
|
11
|
-
return false;
|
|
12
|
-
}
|
|
13
10
|
if (!isCallable(value.run)) {
|
|
14
11
|
return false;
|
|
15
12
|
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { pathToFileURL } from "node:url";
|
|
3
|
+
import { readBuiltinStrategy, listBuiltinStrategyNames } from "../services/strategy/builtin-strategy-registry.js";
|
|
4
|
+
import { normalizeBuiltinStrategyUnits, normalizeStrategyUnits } from "../services/strategy/strategy-normalize.js";
|
|
5
|
+
import { StrategyService } from "../services/strategy/strategy-service.js";
|
|
6
|
+
import { UnitStore } from "../services/units/unit-store.js";
|
|
7
|
+
import { isUnitLike, resolveUnitConstructor } from "../services/units/unit-validate.js";
|
|
8
|
+
import { parsePinnedSpec } from "../services/user-packages/parse-pinned-spec.js";
|
|
9
|
+
import { uniq } from "./array.js";
|
|
10
|
+
const buildMissingStrategyError = (input) => {
|
|
11
|
+
const available = uniq([...input.installedNames, ...listBuiltinStrategyNames()])
|
|
12
|
+
.filter((name) => name.trim().length > 0)
|
|
13
|
+
.sort((left, right) => left.localeCompare(right));
|
|
14
|
+
const suffix = available.length > 0
|
|
15
|
+
? ` Available strategies: ${available.join(", ")}`
|
|
16
|
+
: " No strategies found.";
|
|
17
|
+
return new Error(`Strategy not found: ${input.strategyName}.${suffix}`);
|
|
18
|
+
};
|
|
19
|
+
const assertNoDuplicateUnitIds = (input) => {
|
|
20
|
+
const seen = new Set();
|
|
21
|
+
const duplicateUnit = input.units.find((unit) => {
|
|
22
|
+
if (seen.has(unit.id)) {
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
seen.add(unit.id);
|
|
26
|
+
return false;
|
|
27
|
+
});
|
|
28
|
+
if (duplicateUnit) {
|
|
29
|
+
throw new Error(`Duplicate unit id detected in strategy ${input.strategyName}: ${duplicateUnit.id}`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const loadInstalledStrategyUnits = async (input) => {
|
|
33
|
+
const strategyService = new StrategyService(input.configService);
|
|
34
|
+
const unitStore = new UnitStore(input.configService);
|
|
35
|
+
strategyService.ensureConfigFileExists();
|
|
36
|
+
const strategyFile = strategyService.readStrategy(input.strategyName);
|
|
37
|
+
const normalized = normalizeStrategyUnits(strategyFile);
|
|
38
|
+
const installed = unitStore.readInstalledDependencyVersions();
|
|
39
|
+
const drift = normalized.flatMap((unit) => {
|
|
40
|
+
const pinned = parsePinnedSpec(unit.ref);
|
|
41
|
+
const installedVersion = installed[pinned.name];
|
|
42
|
+
if (!installedVersion || installedVersion !== pinned.version) {
|
|
43
|
+
return [
|
|
44
|
+
{
|
|
45
|
+
name: pinned.name,
|
|
46
|
+
pinned: pinned.version,
|
|
47
|
+
installed: installedVersion
|
|
48
|
+
}
|
|
49
|
+
];
|
|
50
|
+
}
|
|
51
|
+
return [];
|
|
52
|
+
});
|
|
53
|
+
if (drift.length > 0) {
|
|
54
|
+
const details = drift
|
|
55
|
+
.map((driftItem) => {
|
|
56
|
+
const installedText = typeof driftItem.installed === "string" ? driftItem.installed : "(not installed)";
|
|
57
|
+
return `${driftItem.name}: pinned ${driftItem.pinned}, installed ${installedText}`;
|
|
58
|
+
})
|
|
59
|
+
.join("; ");
|
|
60
|
+
throw new Error(`Strategy drift detected (${input.strategyName}). ${details}. ` +
|
|
61
|
+
`Fix: pp strategy update ${input.strategyName} OR pp strategy pin ${input.strategyName}`);
|
|
62
|
+
}
|
|
63
|
+
const units = await Promise.all(normalized.map(async (unit) => unitStore.instantiateFromRef(unit.ref, unit.args)));
|
|
64
|
+
assertNoDuplicateUnitIds({
|
|
65
|
+
strategyName: input.strategyName,
|
|
66
|
+
units
|
|
67
|
+
});
|
|
68
|
+
return units;
|
|
69
|
+
};
|
|
70
|
+
const instantiateBuiltinUnit = async (input) => {
|
|
71
|
+
const req = createRequire(import.meta.url);
|
|
72
|
+
const resolved = req.resolve(input.name);
|
|
73
|
+
const mod = (await import(pathToFileURL(resolved).href));
|
|
74
|
+
const ctor = resolveUnitConstructor(mod);
|
|
75
|
+
if (!ctor) {
|
|
76
|
+
throw new Error(`Unit ${input.name} does not export a Unit constructor.`);
|
|
77
|
+
}
|
|
78
|
+
const instance = new ctor(...input.args);
|
|
79
|
+
if (!isUnitLike(instance)) {
|
|
80
|
+
throw new Error(`Unit ${input.name} exported constructor did not produce a Unit.`);
|
|
81
|
+
}
|
|
82
|
+
return instance;
|
|
83
|
+
};
|
|
84
|
+
const loadBuiltinStrategyUnits = async (input) => {
|
|
85
|
+
const normalized = normalizeBuiltinStrategyUnits(input.strategyFile);
|
|
86
|
+
const units = await Promise.all(normalized.map(async (unit) => instantiateBuiltinUnit(unit)));
|
|
87
|
+
assertNoDuplicateUnitIds({
|
|
88
|
+
strategyName: input.strategyName,
|
|
89
|
+
units
|
|
90
|
+
});
|
|
91
|
+
return units;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Resolve strategy by name from installed strategies first, then built-in strategies.
|
|
95
|
+
*
|
|
96
|
+
* Usage:
|
|
97
|
+
* const strategy = resolveStrategy({ strategyName: "default", strategyService });
|
|
98
|
+
*/
|
|
99
|
+
export const resolveStrategy = (input) => {
|
|
100
|
+
const installedNames = input.strategyService.listInstalledStrategyNames();
|
|
101
|
+
if (installedNames.includes(input.strategyName)) {
|
|
102
|
+
return {
|
|
103
|
+
name: input.strategyName,
|
|
104
|
+
strategyFile: input.strategyService.readStrategy(input.strategyName),
|
|
105
|
+
source: "installed"
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
const builtin = readBuiltinStrategy(input.strategyName);
|
|
109
|
+
if (builtin) {
|
|
110
|
+
return {
|
|
111
|
+
name: input.strategyName,
|
|
112
|
+
strategyFile: builtin,
|
|
113
|
+
source: "builtin"
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
throw buildMissingStrategyError({
|
|
117
|
+
strategyName: input.strategyName,
|
|
118
|
+
installedNames
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Load executable unit instances for a resolved strategy.
|
|
123
|
+
*
|
|
124
|
+
* Usage:
|
|
125
|
+
* const units = await loadStrategyUnits({ strategy, configService });
|
|
126
|
+
*/
|
|
127
|
+
export const loadStrategyUnits = async (input) => {
|
|
128
|
+
if (input.strategy.source === "installed") {
|
|
129
|
+
return loadInstalledStrategyUnits({
|
|
130
|
+
strategyName: input.strategy.name,
|
|
131
|
+
configService: input.configService
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
return loadBuiltinStrategyUnits({
|
|
135
|
+
strategyName: input.strategy.name,
|
|
136
|
+
strategyFile: input.strategy.strategyFile
|
|
137
|
+
});
|
|
138
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
/*! tailwindcss v4.2.0 | MIT License | https://tailwindcss.com */@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-border-style:solid;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-backdrop-blur:initial;--tw-backdrop-brightness:initial;--tw-backdrop-contrast:initial;--tw-backdrop-grayscale:initial;--tw-backdrop-hue-rotate:initial;--tw-backdrop-invert:initial;--tw-backdrop-opacity:initial;--tw-backdrop-saturate:initial;--tw-backdrop-sepia:initial;--tw-duration:initial}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;--font-mono:SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-blue-400:oklch(70.7% .165 254.624);--color-blue-500:oklch(62.3% .214 259.815);--color-blue-600:oklch(54.6% .245 262.881);--color-gray-100:oklch(96.7% .003 264.542);--color-gray-200:oklch(92.8% .006 264.531);--color-gray-400:oklch(70.7% .022 261.325);--color-gray-500:oklch(55.1% .027 264.364);--color-gray-600:oklch(44.6% .03 256.802);--color-gray-700:oklch(37.3% .034 259.733);--color-gray-800:oklch(27.8% .033 256.848);--color-gray-900:oklch(21% .034 264.665);--color-black:#000;--color-white:#fff;--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--tracking-wider:.05em;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-notion-bg-light:#fff;--color-notion-text-light:#37352f;--color-notion-bg-dark:#191919;--color-notion-text-dark:#d4d4d4;--color-notion-border-light:#e9e9e8;--color-notion-border-dark:#373737;--color-notion-code-bg-light:#f7f6f3;--color-notion-code-bg-dark:#1e1e1e;--color-notion-quote-border-light:#37352f;--color-notion-quote-border-dark:#d4d4d4}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab,red,red)){::placeholder{color:color-mix(in oklab,currentcolor 50%,transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){-webkit-appearance:button;-moz-appearance:button;appearance:button}::file-selector-button{-webkit-appearance:button;-moz-appearance:button;appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.pointer-events-none{pointer-events:none}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.sticky{position:sticky}.inset-0{inset:calc(var(--spacing) * 0)}.start{inset-inline-start:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-2{top:calc(var(--spacing) * 2)}.right-0{right:calc(var(--spacing) * 0)}.right-2{right:calc(var(--spacing) * 2)}.bottom-0{bottom:calc(var(--spacing) * 0)}.z-10{z-index:10}.z-40{z-index:40}.z-50{z-index:50}.container{width:100%}@media(min-width:40rem){.container{max-width:40rem}}@media(min-width:48rem){.container{max-width:48rem}}@media(min-width:64rem){.container{max-width:64rem}}@media(min-width:80rem){.container{max-width:80rem}}@media(min-width:96rem){.container{max-width:96rem}}.\!m-0{margin:calc(var(--spacing) * 0)!important}.mx-auto{margin-inline:auto}.my-6{margin-block:calc(var(--spacing) * 6)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.block{display:block}.contents{display:contents}.flex{display:flex}.hidden{display:none}.table{display:table}.h-5{height:calc(var(--spacing) * 5)}.h-screen{height:100vh}.max-h-\[calc\(100vh-4rem\)\]{max-height:calc(100vh - 4rem)}.min-h-screen{min-height:100vh}.w-5{width:calc(var(--spacing) * 5)}.w-\[280px\]{width:280px}.max-w-\[800px\]{max-width:800px}.max-w-\[1200px\]{max-width:1200px}.max-w-full{max-width:100%}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.translate-x-0{--tw-translate-x:calc(var(--spacing) * 0);translate:var(--tw-translate-x) var(--tw-translate-y)}.translate-x-full{--tw-translate-x:100%;translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-zoom-in{cursor:zoom-in}.flex-col{flex-direction:column}.items-center{align-items:center}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.gap-2{gap:calc(var(--spacing) * 2)}:where(.space-y-1>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-r{border-right-style:var(--tw-border-style);border-right-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-l-2{border-left-style:var(--tw-border-style);border-left-width:2px}.border-\[var\(--border-primary\)\]{border-color:var(--border-primary)}.border-blue-500{border-color:var(--color-blue-500)}.border-gray-200{border-color:var(--color-gray-200)}.border-transparent{border-color:#0000}.bg-\[var\(--bg-primary\)\],.bg-\[var\(--bg-primary\)\]\/90{background-color:var(--bg-primary)}@supports (color:color-mix(in lab,red,red)){.bg-\[var\(--bg-primary\)\]\/90{background-color:color-mix(in oklab,var(--bg-primary) 90%,transparent)}}.bg-\[var\(--color-notion-code-bg-light\)\]{background-color:var(--color-notion-code-bg-light)}.bg-black\/50{background-color:#00000080}@supports (color:color-mix(in lab,red,red)){.bg-black\/50{background-color:color-mix(in oklab,var(--color-black) 50%,transparent)}}.bg-white{background-color:var(--color-white)}.\!p-4{padding:calc(var(--spacing) * 4)!important}.p-1\.5{padding:calc(var(--spacing) * 1.5)}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-4{padding-inline:calc(var(--spacing) * 4)}.py-1{padding-block:calc(var(--spacing) * 1)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-8{padding-block:calc(var(--spacing) * 8)}.pr-2{padding-right:calc(var(--spacing) * 2)}.pl-2{padding-left:calc(var(--spacing) * 2)}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-\[var\(--text-primary\)\]{color:var(--text-primary)}.text-blue-600{color:var(--color-blue-600)}.text-gray-500{color:var(--color-gray-500)}.text-gray-600{color:var(--color-gray-600)}.uppercase{text-transform:uppercase}.opacity-0{opacity:0}.opacity-100{opacity:1}.shadow,.shadow-sm{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.shadow-xl{--tw-shadow:0 20px 25px -5px var(--tw-shadow-color,#0000001a), 0 8px 10px -6px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow),var(--tw-inset-ring-shadow),var(--tw-ring-offset-shadow),var(--tw-ring-shadow),var(--tw-shadow)}.backdrop-blur{--tw-backdrop-blur:blur(8px);-webkit-backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,);backdrop-filter:var(--tw-backdrop-blur,) var(--tw-backdrop-brightness,) var(--tw-backdrop-contrast,) var(--tw-backdrop-grayscale,) var(--tw-backdrop-hue-rotate,) var(--tw-backdrop-invert,) var(--tw-backdrop-opacity,) var(--tw-backdrop-saturate,) var(--tw-backdrop-sepia,)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-opacity{transition-property:opacity;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-300{--tw-duration:.3s;transition-duration:.3s}@media(hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:bg-gray-100:hover{background-color:var(--color-gray-100)}.hover\:text-gray-900:hover{color:var(--color-gray-900)}}@media(min-width:64rem){.lg\:mx-auto{margin-inline:auto}.lg\:block{display:block}.lg\:hidden{display:none}.lg\:px-12{padding-inline:calc(var(--spacing) * 12)}.lg\:py-16{padding-block:calc(var(--spacing) * 16)}}.dark\:border-gray-800:is(.dark *){border-color:var(--color-gray-800)}.dark\:bg-\[var\(--color-notion-code-bg-dark\)\]:is(.dark *){background-color:var(--color-notion-code-bg-dark)}.dark\:bg-gray-700:is(.dark *){background-color:var(--color-gray-700)}.dark\:text-blue-400:is(.dark *){color:var(--color-blue-400)}.dark\:text-gray-400:is(.dark *){color:var(--color-gray-400)}@media(hover:hover){.dark\:hover\:bg-gray-600:is(.dark *):hover{background-color:var(--color-gray-600)}.dark\:hover\:bg-gray-800:is(.dark *):hover{background-color:var(--color-gray-800)}.dark\:hover\:text-gray-200:is(.dark *):hover{color:var(--color-gray-200)}}}pre code.hljs{padding:1em;display:block;overflow-x:auto}code.hljs{padding:3px 5px}.hljs{color:#c9d1d9;background:#0d1117}.hljs-doctag,.hljs-keyword,.hljs-meta .hljs-keyword,.hljs-template-tag,.hljs-template-variable,.hljs-type,.hljs-variable.language_{color:#ff7b72}.hljs-title,.hljs-title.class_,.hljs-title.class_.inherited__,.hljs-title.function_{color:#d2a8ff}.hljs-attr,.hljs-attribute,.hljs-literal,.hljs-meta,.hljs-number,.hljs-operator,.hljs-variable,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id{color:#79c0ff}.hljs-regexp,.hljs-string,.hljs-meta .hljs-string{color:#a5d6ff}.hljs-built_in,.hljs-symbol{color:#ffa657}.hljs-comment,.hljs-code,.hljs-formula{color:#8b949e}.hljs-name,.hljs-quote,.hljs-selector-tag,.hljs-selector-pseudo{color:#7ee787}.hljs-subst{color:#c9d1d9}.hljs-section{color:#1f6feb;font-weight:700}.hljs-bullet{color:#f2cc60}.hljs-emphasis{color:#c9d1d9;font-style:italic}.hljs-strong{color:#c9d1d9;font-weight:700}.hljs-addition{color:#aff5b4;background-color:#033a16}.hljs-deletion{color:#ffdcd7;background-color:#67060c}[data-rmiz-ghost]{pointer-events:none;position:absolute}[data-rmiz-btn-zoom],[data-rmiz-btn-unzoom]{color:#fff;outline-offset:2px;touch-action:manipulation;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#000000b3;border:none;border-radius:50%;width:40px;height:40px;margin:0;padding:9px;box-shadow:0 0 1px #ffffff80}[data-rmiz-btn-zoom]:not(:focus):not(:active){clip:rect(0 0 0 0);clip-path:inset(50%);pointer-events:none;white-space:nowrap;width:1px;height:1px;position:absolute;overflow:hidden}[data-rmiz-btn-zoom]{cursor:zoom-in;position:absolute;inset:10px 10px auto auto}[data-rmiz-btn-unzoom]{cursor:zoom-out;z-index:1;position:absolute;inset:20px 20px auto auto}[data-rmiz-content=found] img,[data-rmiz-content=found] svg,[data-rmiz-content=found] [role=img],[data-rmiz-content=found] [data-zoom]{cursor:zoom-in}[data-rmiz-modal]::backdrop{display:none}[data-rmiz-modal][open]{pointer-events:all;background:0 0;border:0;width:100dvw;max-width:none;height:100dvh;max-height:none;margin:0;padding:0;position:fixed;overflow:hidden}[data-rmiz-modal-overlay]{transition:background-color .3s;position:absolute;top:0;right:0;bottom:0;left:0}[data-rmiz-modal-overlay=hidden]{background-color:#fff0}[data-rmiz-modal-overlay=visible]{background-color:#fff}[data-rmiz-modal-content]{width:100%;height:100%;position:relative}[data-rmiz-modal-img]{cursor:zoom-out;image-rendering:high-quality;transform-origin:0 0;transition:transform .3s;position:absolute}@media(prefers-reduced-motion:reduce){[data-rmiz-modal-overlay],[data-rmiz-modal-img]{transition-duration:.01ms!important}}::view-transition-group(root){animation-duration:1s}::view-transition-new(root){mix-blend-mode:normal}::view-transition-old(root){mix-blend-mode:normal}::view-transition-new(root){animation-name:reveal-light}::view-transition-old(root){animation:none}.dark::view-transition-old(root){animation:none}.dark::view-transition-new(root){animation-name:reveal-dark}li{margin-bottom:5px}@keyframes reveal-dark{0%{clip-path:polygon(-30% 0,-30% 0,-15% 100%,-10% 115%)}to{clip-path:polygon(-30% 0,130% 0,115% 100%,-10% 115%)}}@keyframes reveal-light{0%{clip-path:polygon(130% 0,130% 0,115% 100%,110% 115%)}to{clip-path:polygon(130% 0,-30% 0,-15% 100%,110% 115%)}}.prose-notion{max-width:720px;font-family:var(--font-sans);margin-left:auto;margin-right:auto;font-size:16px;line-height:1.7}:root{--bg-primary:var(--color-notion-bg-light);--text-primary:var(--color-notion-text-light);--border-primary:var(--color-notion-border-light);--code-bg:var(--color-notion-code-bg-light);--quote-border:var(--color-notion-quote-border-light)}.dark{--bg-primary:var(--color-notion-bg-dark);--text-primary:var(--color-notion-text-dark);--border-primary:var(--color-notion-border-dark);--code-bg:var(--color-notion-code-bg-dark);--quote-border:var(--color-notion-quote-border-dark)}.prose-notion{color:var(--text-primary);background-color:var(--bg-primary)}.prose-notion h1{margin-top:2em;margin-bottom:.5em;font-size:2em;font-weight:700;line-height:1.3}.prose-notion h2{margin-top:1.4em;margin-bottom:.4em;font-size:1.5em;font-weight:600;line-height:1.3}.prose-notion h3{margin-top:1.2em;margin-bottom:.3em;font-size:1.25em;font-weight:600;line-height:1.3}.prose-notion p{margin-top:.5em;margin-bottom:.5em}.prose-notion blockquote{border-left:3px solid var(--quote-border);opacity:.8;margin-top:1em;margin-bottom:1em;padding-left:1rem;font-style:italic}.prose-notion ul,.prose-notion ol{margin-top:.5em;margin-bottom:.5em;padding-left:1.5em}.prose-notion li{margin-bottom:.25em}.prose-notion hr{border:0;border-top:1px solid var(--border-primary);margin:2em 0}.prose-notion a{text-underline-offset:4px;color:inherit;-webkit-text-decoration:underline #78777466;text-decoration:underline #78777466;transition:text-decoration-color .1s}.prose-notion a:hover{-webkit-text-decoration-color:var(--text-primary);text-decoration-color:var(--text-primary)}.prose-notion img{border-radius:4px;max-width:100%;margin:1.5em auto;display:block}.prose-notion table{border-collapse:collapse;width:100%;margin:1.5em 0;font-size:.9em}.prose-notion th,.prose-notion td{border:1px solid var(--border-primary);text-align:left;padding:8px 12px}.prose-notion th{background-color:#7877740d;font-weight:600}.prose-notion :not(pre)>code{background:var(--code-bg);font-family:var(--font-mono);border-radius:3px;padding:2px 6px;font-size:.9em}.prose-notion pre{background:0 0;margin:0;padding:0}@property --tw-translate-x{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-y{syntax:"*";inherits:false;initial-value:0}@property --tw-translate-z{syntax:"*";inherits:false;initial-value:0}@property --tw-rotate-x{syntax:"*";inherits:false}@property --tw-rotate-y{syntax:"*";inherits:false}@property --tw-rotate-z{syntax:"*";inherits:false}@property --tw-skew-x{syntax:"*";inherits:false}@property --tw-skew-y{syntax:"*";inherits:false}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-backdrop-blur{syntax:"*";inherits:false}@property --tw-backdrop-brightness{syntax:"*";inherits:false}@property --tw-backdrop-contrast{syntax:"*";inherits:false}@property --tw-backdrop-grayscale{syntax:"*";inherits:false}@property --tw-backdrop-hue-rotate{syntax:"*";inherits:false}@property --tw-backdrop-invert{syntax:"*";inherits:false}@property --tw-backdrop-opacity{syntax:"*";inherits:false}@property --tw-backdrop-saturate{syntax:"*";inherits:false}@property --tw-backdrop-sepia{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}
|