@rstest/browser 0.8.3 → 0.8.4
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/browser-container/container-static/css/index.5a71c757.css +1 -0
- package/dist/browser-container/container-static/js/{837.e631233e.js → 392.28f9a733.js} +14259 -9646
- package/dist/browser-container/container-static/js/392.28f9a733.js.LICENSE.txt +1 -0
- package/dist/browser-container/container-static/js/{container.93bdade7.js → index.129eaf9c.js} +854 -321
- package/dist/browser-container/container-static/js/{lib-react.48335539.js → lib-react.97ee79b0.js} +22 -22
- package/dist/browser-container/container-static/js/lib-react.97ee79b0.js.LICENSE.txt +1 -0
- package/dist/browser-container/container-static/js/scheduler.6976de44.js +411 -0
- package/dist/browser-container/{container.html → index.html} +1 -1
- package/dist/browser-container/scheduler.html +19 -0
- package/dist/configValidation.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +224 -37
- package/dist/protocol.d.ts +6 -0
- package/dist/viewportPresets.d.ts +16 -0
- package/package.json +4 -4
- package/src/configValidation.ts +66 -0
- package/src/hostController.ts +149 -44
- package/src/index.ts +8 -0
- package/src/protocol.ts +9 -0
- package/src/viewportPresets.ts +64 -0
- package/dist/browser-container/container-static/css/container.425b2695.css +0 -1
- package/dist/browser-container/container-static/js/837.e631233e.js.LICENSE.txt +0 -1
- package/dist/browser-container/container-static/js/lib-react.48335539.js.LICENSE.txt +0 -1
package/src/hostController.ts
CHANGED
|
@@ -44,6 +44,19 @@ type RsbuildDevServer = rsbuild.RsbuildDevServer;
|
|
|
44
44
|
type RsbuildInstance = rsbuild.RsbuildInstance;
|
|
45
45
|
|
|
46
46
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
47
|
+
const OPTIONS_PLACEHOLDER = '__RSTEST_OPTIONS_PLACEHOLDER__';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Serialize JSON for inline <script> injection.
|
|
51
|
+
* Escapes '<' to prevent accidental </script> break-out.
|
|
52
|
+
* Escapes U+2028/U+2029 to keep script parsing safe.
|
|
53
|
+
*/
|
|
54
|
+
const serializeForInlineScript = (value: unknown): string => {
|
|
55
|
+
return JSON.stringify(value)
|
|
56
|
+
.replace(/</g, '\\u003c')
|
|
57
|
+
.replace(/\u2028/g, '\\u2028')
|
|
58
|
+
.replace(/\u2029/g, '\\u2029');
|
|
59
|
+
};
|
|
47
60
|
|
|
48
61
|
// ============================================================================
|
|
49
62
|
// Type Definitions
|
|
@@ -716,6 +729,21 @@ const htmlTemplate = `<!DOCTYPE html>
|
|
|
716
729
|
</html>
|
|
717
730
|
`;
|
|
718
731
|
|
|
732
|
+
const fallbackSchedulerHtmlTemplate = `<!DOCTYPE html>
|
|
733
|
+
<html lang="en">
|
|
734
|
+
<head>
|
|
735
|
+
<meta charset="UTF-8" />
|
|
736
|
+
<title>Rstest Browser Scheduler</title>
|
|
737
|
+
<script>
|
|
738
|
+
window.__RSTEST_BROWSER_OPTIONS__ = ${OPTIONS_PLACEHOLDER};
|
|
739
|
+
</script>
|
|
740
|
+
</head>
|
|
741
|
+
<body>
|
|
742
|
+
<script type="module" src="/container-static/js/scheduler.js"></script>
|
|
743
|
+
</body>
|
|
744
|
+
</html>
|
|
745
|
+
`;
|
|
746
|
+
|
|
719
747
|
// Workaround for noisy "removed ..." logs caused by VirtualModulesPlugin.
|
|
720
748
|
// Rsbuild suppresses the removed-file log if all removed paths include "virtual":
|
|
721
749
|
// https://github.com/web-infra-dev/rsbuild/blob/1258fa9dba5c321a4629b591a6dadbd2e26c6963/packages/core/src/createCompiler.ts#L73-L76
|
|
@@ -800,22 +828,30 @@ const createBrowserRuntime = async ({
|
|
|
800
828
|
[manifestPath]: manifestSource,
|
|
801
829
|
});
|
|
802
830
|
|
|
803
|
-
const optionsPlaceholder = '__RSTEST_OPTIONS_PLACEHOLDER__';
|
|
804
831
|
const containerHtmlTemplate = containerDistPath
|
|
805
|
-
? await fs.readFile(join(containerDistPath, '
|
|
832
|
+
? await fs.readFile(join(containerDistPath, 'index.html'), 'utf-8')
|
|
833
|
+
: null;
|
|
834
|
+
const schedulerHtmlTemplate = containerDistPath
|
|
835
|
+
? await fs
|
|
836
|
+
.readFile(join(containerDistPath, 'scheduler.html'), 'utf-8')
|
|
837
|
+
.catch(() => null)
|
|
806
838
|
: null;
|
|
807
839
|
|
|
808
840
|
let injectedContainerHtml: string | null = null;
|
|
841
|
+
let injectedSchedulerHtml: string | null = null;
|
|
809
842
|
let serializedOptions = 'null';
|
|
810
843
|
|
|
811
844
|
const setContainerOptions = (options: BrowserHostConfig): void => {
|
|
812
|
-
serializedOptions =
|
|
845
|
+
serializedOptions = serializeForInlineScript(options);
|
|
813
846
|
if (containerHtmlTemplate) {
|
|
814
847
|
injectedContainerHtml = containerHtmlTemplate.replace(
|
|
815
|
-
|
|
848
|
+
OPTIONS_PLACEHOLDER,
|
|
816
849
|
serializedOptions,
|
|
817
850
|
);
|
|
818
851
|
}
|
|
852
|
+
injectedSchedulerHtml = (
|
|
853
|
+
schedulerHtmlTemplate || fallbackSchedulerHtmlTemplate
|
|
854
|
+
).replace(OPTIONS_PLACEHOLDER, serializedOptions);
|
|
819
855
|
};
|
|
820
856
|
|
|
821
857
|
// Get user Rsbuild config from the first browser project
|
|
@@ -996,7 +1032,7 @@ const createBrowserRuntime = async ({
|
|
|
996
1032
|
const serveContainer = containerDistPath
|
|
997
1033
|
? sirv(containerDistPath, {
|
|
998
1034
|
dev: false,
|
|
999
|
-
single: '
|
|
1035
|
+
single: 'index.html',
|
|
1000
1036
|
})
|
|
1001
1037
|
: null;
|
|
1002
1038
|
|
|
@@ -1020,7 +1056,7 @@ const createBrowserRuntime = async ({
|
|
|
1020
1056
|
}
|
|
1021
1057
|
|
|
1022
1058
|
let html = await response.text();
|
|
1023
|
-
html = html.replace(
|
|
1059
|
+
html = html.replace(OPTIONS_PLACEHOLDER, serializedOptions);
|
|
1024
1060
|
|
|
1025
1061
|
res.statusCode = response.status;
|
|
1026
1062
|
response.headers.forEach((value, key) => {
|
|
@@ -1097,14 +1133,26 @@ const createBrowserRuntime = async ({
|
|
|
1097
1133
|
}
|
|
1098
1134
|
return;
|
|
1099
1135
|
}
|
|
1100
|
-
if (url.pathname === '/') {
|
|
1136
|
+
if (url.pathname === '/' || url.pathname === '/scheduler.html') {
|
|
1101
1137
|
if (await respondWithDevServerHtml(url, res)) {
|
|
1102
1138
|
return;
|
|
1103
1139
|
}
|
|
1104
1140
|
|
|
1141
|
+
if (url.pathname === '/scheduler.html') {
|
|
1142
|
+
res.setHeader('Content-Type', 'text/html');
|
|
1143
|
+
res.end(
|
|
1144
|
+
injectedSchedulerHtml ||
|
|
1145
|
+
(schedulerHtmlTemplate || fallbackSchedulerHtmlTemplate).replace(
|
|
1146
|
+
OPTIONS_PLACEHOLDER,
|
|
1147
|
+
'null',
|
|
1148
|
+
),
|
|
1149
|
+
);
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1105
1153
|
const html =
|
|
1106
1154
|
injectedContainerHtml ||
|
|
1107
|
-
containerHtmlTemplate?.replace(
|
|
1155
|
+
containerHtmlTemplate?.replace(OPTIONS_PLACEHOLDER, 'null');
|
|
1108
1156
|
|
|
1109
1157
|
if (html) {
|
|
1110
1158
|
res.setHeader('Content-Type', 'text/html');
|
|
@@ -1232,6 +1280,56 @@ export const runBrowserController = async (
|
|
|
1232
1280
|
): Promise<BrowserTestRunResult | void> => {
|
|
1233
1281
|
const { skipOnTestRunEnd = false } = options ?? {};
|
|
1234
1282
|
const buildStart = Date.now();
|
|
1283
|
+
const browserProjects = getBrowserProjects(context);
|
|
1284
|
+
const useSchedulerPage = browserProjects.every(
|
|
1285
|
+
(project) => project.normalizedConfig.browser.headless,
|
|
1286
|
+
);
|
|
1287
|
+
|
|
1288
|
+
/**
|
|
1289
|
+
* Build an error BrowserTestRunResult and call onTestRunEnd if needed.
|
|
1290
|
+
* Used for early-exit error paths to ensure errors reach the summary report.
|
|
1291
|
+
*/
|
|
1292
|
+
const buildErrorResult = async (
|
|
1293
|
+
error: Error,
|
|
1294
|
+
): Promise<BrowserTestRunResult> => {
|
|
1295
|
+
const elapsed = Math.max(0, Date.now() - buildStart);
|
|
1296
|
+
const errorResult: BrowserTestRunResult = {
|
|
1297
|
+
results: [],
|
|
1298
|
+
testResults: [],
|
|
1299
|
+
duration: { totalTime: elapsed, buildTime: elapsed, testTime: 0 },
|
|
1300
|
+
hasFailure: true,
|
|
1301
|
+
unhandledErrors: [error],
|
|
1302
|
+
};
|
|
1303
|
+
|
|
1304
|
+
if (!skipOnTestRunEnd) {
|
|
1305
|
+
for (const reporter of context.reporters) {
|
|
1306
|
+
await (reporter as Reporter).onTestRunEnd?.({
|
|
1307
|
+
results: [],
|
|
1308
|
+
testResults: [],
|
|
1309
|
+
duration: errorResult.duration,
|
|
1310
|
+
snapshotSummary: context.snapshotManager.summary,
|
|
1311
|
+
getSourcemap: async () => null,
|
|
1312
|
+
unhandledErrors: errorResult.unhandledErrors,
|
|
1313
|
+
});
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
return errorResult;
|
|
1318
|
+
};
|
|
1319
|
+
|
|
1320
|
+
const toError = (error: unknown): Error => {
|
|
1321
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
1322
|
+
};
|
|
1323
|
+
|
|
1324
|
+
const failWithError = async (
|
|
1325
|
+
error: unknown,
|
|
1326
|
+
cleanup?: () => Promise<void>,
|
|
1327
|
+
): Promise<BrowserTestRunResult> => {
|
|
1328
|
+
ensureProcessExitCode(1);
|
|
1329
|
+
await cleanup?.();
|
|
1330
|
+
return buildErrorResult(toError(error));
|
|
1331
|
+
};
|
|
1332
|
+
|
|
1235
1333
|
const containerDevServerEnv = process.env.RSTEST_CONTAINER_DEV_SERVER;
|
|
1236
1334
|
let containerDevServer: string | undefined;
|
|
1237
1335
|
let containerDistPath: string | undefined;
|
|
@@ -1243,13 +1341,9 @@ export const runBrowserController = async (
|
|
|
1243
1341
|
`[Browser UI] Using dev server for container: ${containerDevServer}`,
|
|
1244
1342
|
);
|
|
1245
1343
|
} catch (error) {
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
),
|
|
1250
|
-
);
|
|
1251
|
-
ensureProcessExitCode(1);
|
|
1252
|
-
return;
|
|
1344
|
+
const originalError = toError(error);
|
|
1345
|
+
originalError.message = `Invalid RSTEST_CONTAINER_DEV_SERVER value: ${originalError.message}`;
|
|
1346
|
+
return failWithError(originalError);
|
|
1253
1347
|
}
|
|
1254
1348
|
}
|
|
1255
1349
|
|
|
@@ -1257,9 +1351,7 @@ export const runBrowserController = async (
|
|
|
1257
1351
|
try {
|
|
1258
1352
|
containerDistPath = resolveContainerDist();
|
|
1259
1353
|
} catch (error) {
|
|
1260
|
-
|
|
1261
|
-
ensureProcessExitCode(1);
|
|
1262
|
-
return;
|
|
1354
|
+
return failWithError(error);
|
|
1263
1355
|
}
|
|
1264
1356
|
}
|
|
1265
1357
|
|
|
@@ -1340,10 +1432,9 @@ export const runBrowserController = async (
|
|
|
1340
1432
|
containerDevServer,
|
|
1341
1433
|
});
|
|
1342
1434
|
} catch (error) {
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
return;
|
|
1435
|
+
return failWithError(error, async () => {
|
|
1436
|
+
await fs.rm(tempDir, { recursive: true, force: true }).catch(() => {});
|
|
1437
|
+
});
|
|
1347
1438
|
}
|
|
1348
1439
|
|
|
1349
1440
|
if (isWatchMode) {
|
|
@@ -1366,20 +1457,19 @@ export const runBrowserController = async (
|
|
|
1366
1457
|
|
|
1367
1458
|
// Only include browser mode projects in runtime configs
|
|
1368
1459
|
// Normalize projectRoot to posix format for cross-platform compatibility
|
|
1369
|
-
const
|
|
1370
|
-
|
|
1371
|
-
browserProjectsForRuntime.map((project: ProjectContext) => ({
|
|
1460
|
+
const projectRuntimeConfigs: BrowserProjectRuntime[] = browserProjects.map(
|
|
1461
|
+
(project: ProjectContext) => ({
|
|
1372
1462
|
name: project.name,
|
|
1373
1463
|
environmentName: project.environmentName,
|
|
1374
1464
|
projectRoot: normalize(project.rootPath),
|
|
1375
1465
|
runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project)),
|
|
1376
|
-
|
|
1466
|
+
viewport: project.normalizedConfig.browser.viewport,
|
|
1467
|
+
}),
|
|
1468
|
+
);
|
|
1377
1469
|
|
|
1378
1470
|
// Get max testTimeout from all browser projects for RPC timeout
|
|
1379
1471
|
const maxTestTimeoutForRpc = Math.max(
|
|
1380
|
-
...
|
|
1381
|
-
(p) => p.normalizedConfig.testTimeout ?? 5000,
|
|
1382
|
-
),
|
|
1472
|
+
...browserProjects.map((p) => p.normalizedConfig.testTimeout ?? 5000),
|
|
1383
1473
|
);
|
|
1384
1474
|
|
|
1385
1475
|
const hostOptions: BrowserHostConfig = {
|
|
@@ -1443,7 +1533,11 @@ export const runBrowserController = async (
|
|
|
1443
1533
|
// Forward browser console to terminal
|
|
1444
1534
|
containerPage.on('console', (msg: ConsoleMessage) => {
|
|
1445
1535
|
const text = msg.text();
|
|
1446
|
-
if (
|
|
1536
|
+
if (
|
|
1537
|
+
text.startsWith('[Container]') ||
|
|
1538
|
+
text.startsWith('[Runner]') ||
|
|
1539
|
+
text.startsWith('[Scheduler]')
|
|
1540
|
+
) {
|
|
1447
1541
|
logger.log(color.gray(`[Browser Console] ${text}`));
|
|
1448
1542
|
}
|
|
1449
1543
|
});
|
|
@@ -1584,21 +1678,28 @@ export const runBrowserController = async (
|
|
|
1584
1678
|
|
|
1585
1679
|
// Only navigate on first creation
|
|
1586
1680
|
if (isNewPage) {
|
|
1587
|
-
|
|
1681
|
+
const pagePath = useSchedulerPage ? '/scheduler.html' : '/';
|
|
1682
|
+
if (useSchedulerPage) {
|
|
1683
|
+
const serializedOptions = serializeForInlineScript(hostOptions);
|
|
1684
|
+
await containerPage.addInitScript(
|
|
1685
|
+
`window.__RSTEST_BROWSER_OPTIONS__ = ${serializedOptions};`,
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
await containerPage.goto(`http://localhost:${port}${pagePath}`, {
|
|
1588
1689
|
waitUntil: 'load',
|
|
1589
1690
|
});
|
|
1590
1691
|
|
|
1591
1692
|
logger.log(
|
|
1592
|
-
color.cyan(
|
|
1693
|
+
color.cyan(
|
|
1694
|
+
`\nBrowser mode opened at http://localhost:${port}${pagePath}\n`,
|
|
1695
|
+
),
|
|
1593
1696
|
);
|
|
1594
1697
|
}
|
|
1595
1698
|
|
|
1596
1699
|
// Wait for all tests to complete
|
|
1597
1700
|
// Calculate total timeout based on config: max testTimeout * file count + buffer
|
|
1598
1701
|
const maxTestTimeout = Math.max(
|
|
1599
|
-
...
|
|
1600
|
-
(p) => p.normalizedConfig.testTimeout ?? 5000,
|
|
1601
|
-
),
|
|
1702
|
+
...browserProjects.map((p) => p.normalizedConfig.testTimeout ?? 5000),
|
|
1602
1703
|
);
|
|
1603
1704
|
const totalTimeoutMs = maxTestTimeout * allTestFiles.length + 30_000;
|
|
1604
1705
|
|
|
@@ -1670,15 +1771,21 @@ export const runBrowserController = async (
|
|
|
1670
1771
|
}
|
|
1671
1772
|
|
|
1672
1773
|
if (!isWatchMode) {
|
|
1774
|
+
try {
|
|
1775
|
+
await containerPage.close();
|
|
1776
|
+
} catch {
|
|
1777
|
+
// ignore
|
|
1778
|
+
}
|
|
1779
|
+
try {
|
|
1780
|
+
await containerContext.close();
|
|
1781
|
+
} catch {
|
|
1782
|
+
// ignore
|
|
1783
|
+
}
|
|
1673
1784
|
await destroyBrowserRuntime(runtime);
|
|
1674
1785
|
}
|
|
1675
1786
|
|
|
1676
1787
|
if (fatalError) {
|
|
1677
|
-
|
|
1678
|
-
color.red(`Browser test run failed: ${(fatalError as Error).message}`),
|
|
1679
|
-
);
|
|
1680
|
-
ensureProcessExitCode(1);
|
|
1681
|
-
return;
|
|
1788
|
+
return failWithError(fatalError);
|
|
1682
1789
|
}
|
|
1683
1790
|
|
|
1684
1791
|
const duration = {
|
|
@@ -1815,6 +1922,7 @@ export const listBrowserTests = async (
|
|
|
1815
1922
|
environmentName: project.environmentName,
|
|
1816
1923
|
projectRoot: normalize(project.rootPath),
|
|
1817
1924
|
runtimeConfig: serializableConfig(getRuntimeConfigFromProject(project)),
|
|
1925
|
+
viewport: project.normalizedConfig.browser.viewport,
|
|
1818
1926
|
}),
|
|
1819
1927
|
);
|
|
1820
1928
|
|
|
@@ -1895,10 +2003,7 @@ export const listBrowserTests = async (
|
|
|
1895
2003
|
);
|
|
1896
2004
|
|
|
1897
2005
|
// Inject host options before navigation so the runner can access them
|
|
1898
|
-
const serializedOptions =
|
|
1899
|
-
/</g,
|
|
1900
|
-
'\\u003c',
|
|
1901
|
-
);
|
|
2006
|
+
const serializedOptions = serializeForInlineScript(hostOptions);
|
|
1902
2007
|
await page.addInitScript(
|
|
1903
2008
|
`window.__RSTEST_BROWSER_OPTIONS__ = ${serializedOptions};`,
|
|
1904
2009
|
);
|
package/src/index.ts
CHANGED
|
@@ -9,6 +9,14 @@ import {
|
|
|
9
9
|
runBrowserController,
|
|
10
10
|
} from './hostController';
|
|
11
11
|
|
|
12
|
+
export { validateBrowserConfig } from './configValidation';
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
BROWSER_VIEWPORT_PRESET_DIMENSIONS,
|
|
16
|
+
BROWSER_VIEWPORT_PRESET_IDS,
|
|
17
|
+
resolveBrowserViewportPreset,
|
|
18
|
+
} from './viewportPresets';
|
|
19
|
+
|
|
12
20
|
export async function runBrowserTests(
|
|
13
21
|
context: Rstest,
|
|
14
22
|
options?: BrowserTestRunOptions,
|
package/src/protocol.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DevicePreset } from '@rstest/core/browser';
|
|
1
2
|
import type {
|
|
2
3
|
RuntimeConfig,
|
|
3
4
|
Test,
|
|
@@ -8,11 +9,19 @@ import type { SnapshotUpdateState } from '@vitest/snapshot';
|
|
|
8
9
|
|
|
9
10
|
export type SerializedRuntimeConfig = RuntimeConfig;
|
|
10
11
|
|
|
12
|
+
export type BrowserViewport =
|
|
13
|
+
| {
|
|
14
|
+
width: number;
|
|
15
|
+
height: number;
|
|
16
|
+
}
|
|
17
|
+
| DevicePreset;
|
|
18
|
+
|
|
11
19
|
export type BrowserProjectRuntime = {
|
|
12
20
|
name: string;
|
|
13
21
|
environmentName: string;
|
|
14
22
|
projectRoot: string;
|
|
15
23
|
runtimeConfig: SerializedRuntimeConfig;
|
|
24
|
+
viewport?: BrowserViewport;
|
|
16
25
|
};
|
|
17
26
|
|
|
18
27
|
/**
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime source of truth for browser viewport presets.
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: Keep this list/map in sync with `DevicePreset` typing in
|
|
5
|
+
* `@rstest/core` (`packages/core/src/types/config.ts`) so `defineConfig`
|
|
6
|
+
* autocomplete and runtime validation stay consistent.
|
|
7
|
+
*/
|
|
8
|
+
export const BROWSER_VIEWPORT_PRESET_IDS = [
|
|
9
|
+
'iPhoneSE',
|
|
10
|
+
'iPhoneXR',
|
|
11
|
+
'iPhone12Pro',
|
|
12
|
+
'iPhone14ProMax',
|
|
13
|
+
'Pixel7',
|
|
14
|
+
'SamsungGalaxyS8Plus',
|
|
15
|
+
'SamsungGalaxyS20Ultra',
|
|
16
|
+
'iPadMini',
|
|
17
|
+
'iPadAir',
|
|
18
|
+
'iPadPro',
|
|
19
|
+
'SurfacePro7',
|
|
20
|
+
'SurfaceDuo',
|
|
21
|
+
'GalaxyZFold5',
|
|
22
|
+
'AsusZenbookFold',
|
|
23
|
+
'SamsungGalaxyA51A71',
|
|
24
|
+
'NestHub',
|
|
25
|
+
'NestHubMax',
|
|
26
|
+
] as const;
|
|
27
|
+
|
|
28
|
+
type BrowserViewportPresetId = (typeof BROWSER_VIEWPORT_PRESET_IDS)[number];
|
|
29
|
+
|
|
30
|
+
type BrowserViewportSize = {
|
|
31
|
+
width: number;
|
|
32
|
+
height: number;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
export const BROWSER_VIEWPORT_PRESET_DIMENSIONS: Record<
|
|
36
|
+
BrowserViewportPresetId,
|
|
37
|
+
BrowserViewportSize
|
|
38
|
+
> = {
|
|
39
|
+
iPhoneSE: { width: 375, height: 667 },
|
|
40
|
+
iPhoneXR: { width: 414, height: 896 },
|
|
41
|
+
iPhone12Pro: { width: 390, height: 844 },
|
|
42
|
+
iPhone14ProMax: { width: 430, height: 932 },
|
|
43
|
+
Pixel7: { width: 412, height: 915 },
|
|
44
|
+
SamsungGalaxyS8Plus: { width: 360, height: 740 },
|
|
45
|
+
SamsungGalaxyS20Ultra: { width: 412, height: 915 },
|
|
46
|
+
iPadMini: { width: 768, height: 1024 },
|
|
47
|
+
iPadAir: { width: 820, height: 1180 },
|
|
48
|
+
iPadPro: { width: 1024, height: 1366 },
|
|
49
|
+
SurfacePro7: { width: 912, height: 1368 },
|
|
50
|
+
SurfaceDuo: { width: 540, height: 720 },
|
|
51
|
+
GalaxyZFold5: { width: 344, height: 882 },
|
|
52
|
+
AsusZenbookFold: { width: 853, height: 1280 },
|
|
53
|
+
SamsungGalaxyA51A71: { width: 412, height: 914 },
|
|
54
|
+
NestHub: { width: 1024, height: 600 },
|
|
55
|
+
NestHubMax: { width: 1280, height: 800 },
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export const resolveBrowserViewportPreset = (
|
|
59
|
+
presetId: string,
|
|
60
|
+
): BrowserViewportSize | null => {
|
|
61
|
+
const size =
|
|
62
|
+
BROWSER_VIEWPORT_PRESET_DIMENSIONS[presetId as BrowserViewportPresetId];
|
|
63
|
+
return size ?? null;
|
|
64
|
+
};
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
@import "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600&display=swap";@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{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-duration:initial;--tw-ease:initial}::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-ordinal:initial;--tw-slashed-zero:initial;--tw-numeric-figure:initial;--tw-numeric-spacing:initial;--tw-numeric-fraction:initial;--tw-duration:initial;--tw-ease:initial}}}@layer theme{:root,:host{--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-zinc-950:#09090b;--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-light:300;--font-weight-medium:500;--font-weight-semibold:600;--font-weight-bold:700;--tracking-tight:-.025em;--tracking-wider:.05em;--tracking-widest:.1em;--radius-md:.375rem;--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4,0,.6,1)infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4,0,.2,1)}@supports (color:color(display-p3 0 0 0)){:root,:host{--color-zinc-950:color(display-p3 .0353716 .0353595 .0435539)}}@supports (color:lab(0% 0 0)){:root,:host{--color-zinc-950:lab(2.51107% .242703 -.886115)}}}@layer base,components;@layer utilities{.absolute{position:absolute}.relative{position:relative}.inset-0{inset:calc(var(--spacing)*0)}.inset-x-0{inset-inline:calc(var(--spacing)*0)}.inset-y-0{inset-block:calc(var(--spacing)*0)}.bottom-0{bottom:calc(var(--spacing)*0)}.left-0{left:calc(var(--spacing)*0)}.z-10{z-index:10}.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)}.ml-1{margin-left:calc(var(--spacing)*1)}.block{display:block}.flex{display:flex}.grid{display:grid}.inline{display:inline}.inline-flex{display:inline-flex}.h-2{height:calc(var(--spacing)*2)}.h-5{height:calc(var(--spacing)*5)}.h-6{height:calc(var(--spacing)*6)}.h-8{height:calc(var(--spacing)*8)}.h-\[1px\]{height:1px}.h-\[48px\]{height:48px}.h-full{height:100%}.h-screen{height:100vh}.min-h-0{min-height:calc(var(--spacing)*0)}.w-2{width:calc(var(--spacing)*2)}.w-5{width:calc(var(--spacing)*5)}.w-8{width:calc(var(--spacing)*8)}.w-\[16px\]{width:16px}.w-full{width:100%}.max-w-\[400px\]{max-width:400px}.flex-1{flex:1}.shrink-0{flex-shrink:0}.animate-pulse{animation:var(--animate-pulse)}.animate-spin{animation:var(--animate-spin)}.cursor-default{cursor:default}.grid-cols-\[auto_minmax\(0\,1fr\)_auto\]{grid-template-columns:auto minmax(0,1fr) auto}.flex-col{flex-direction:column}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.gap-1{gap:calc(var(--spacing)*1)}.gap-1\.5{gap:calc(var(--spacing)*1.5)}.gap-2{gap:calc(var(--spacing)*2)}.gap-3{gap:calc(var(--spacing)*3)}:where(.space-y-2>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing)*2)*var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing)*2)*calc(1 - var(--tw-space-y-reverse)))}.truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.overflow-hidden{overflow:hidden}.overflow-x-hidden{overflow-x:hidden}.overflow-y-auto{overflow-y:auto}.rounded-full{border-radius:3.40282e38px}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.bg-black\/\[0\.02\]{background-color:#00000005}@supports (color:color-mix(in lab, red, red)){.bg-black\/\[0\.02\]{background-color:color-mix(in oklab,var(--color-black)2%,transparent)}}.bg-transparent{background-color:#0000}.bg-zinc-950{background-color:var(--color-zinc-950)}.p-0{padding:calc(var(--spacing)*0)}.p-3{padding:calc(var(--spacing)*3)}.px-2{padding-inline:calc(var(--spacing)*2)}.px-4{padding-inline:calc(var(--spacing)*4)}.py-0{padding-block:calc(var(--spacing)*0)}.py-0\.5{padding-block:calc(var(--spacing)*.5)}.py-1\.5{padding-block:calc(var(--spacing)*1.5)}.py-3{padding-block:calc(var(--spacing)*3)}.font-mono{font-family:var(--font-mono)}.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))}.text-\[9px\]{font-size:9px}.text-\[10px\]{font-size:10px}.text-\[12px\]{font-size:12px}.text-\[13px\]{font-size:13px}.leading-none{--tw-leading:1;line-height:1}.font-bold{--tw-font-weight:var(--font-weight-bold);font-weight:var(--font-weight-bold)}.font-light{--tw-font-weight:var(--font-weight-light);font-weight:var(--font-weight-light)}.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-tight{--tw-tracking:var(--tracking-tight);letter-spacing:var(--tracking-tight)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.tracking-widest{--tw-tracking:var(--tracking-widest);letter-spacing:var(--tracking-widest)}.text-\(--accents-3\){color:var(--accents-3)}.text-\(--accents-4\){color:var(--accents-4)}.text-\(--accents-5\){color:var(--accents-5)}.text-\(--accents-7\){color:var(--accents-7)}.text-\(--muted-foreground\){color:var(--muted-foreground)}.text-\(--warning\){color:var(--warning)}.text-white{color:var(--color-white)}.uppercase{text-transform:uppercase}.tabular-nums{--tw-numeric-spacing:tabular-nums;font-variant-numeric:var(--tw-ordinal, )var(--tw-slashed-zero, )var(--tw-numeric-figure, )var(--tw-numeric-spacing, )var(--tw-numeric-fraction, )}.opacity-0{opacity:0}.opacity-40{opacity:.4}.opacity-70{opacity:.7}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.transition-all{transition-property:all;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.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))}.duration-200{--tw-duration:.2s;transition-duration:.2s}.duration-500{--tw-duration:.5s;transition-duration:.5s}.ease-\[cubic-bezier\(0\.4\,0\,0\.2\,1\)\]{--tw-ease:cubic-bezier(.4,0,.2,1);transition-timing-function:cubic-bezier(.4,0,.2,1)}.select-none{-webkit-user-select:none;user-select:none}@media (hover:hover){.group-hover\:opacity-100:is(:where(.group):hover *){opacity:1}.hover\:bg-\(--accents-1\):hover{background-color:var(--accents-1)}.hover\:text-\(--accents-6\):hover{color:var(--accents-6)}}}.ant-typography.font-mono{font-family:var(--font-mono)}:root{--font-mono:"JetBrains Mono",ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--background:#fff;--foreground:#000;--ds-gray-100:#fafafa;--ds-gray-200:#eaeaea;--ds-gray-300:#999;--ds-gray-400:#888;--ds-gray-500:#666;--ds-gray-600:#444;--ds-gray-700:#333;--ds-gray-800:#111;--ds-gray-900:#000;--ds-gray-1000:#000;--ds-red-100:#fff0f0;--ds-red-200:#ffebeb;--ds-red-300:#ffe6e6;--ds-red-700:#e5484d;--ds-red-800:#da2f35;--ds-red-900:#cb2a2f;--ds-green-100:#effbef;--ds-green-200:#ebfaeb;--ds-green-300:#daf6da;--ds-green-700:#45a557;--ds-green-800:#398e4a;--ds-green-900:#297a3a;--ds-amber-100:#fff6e6;--ds-amber-200:#fff4d6;--ds-amber-300:#fef0cd;--ds-amber-700:#ffb224;--ds-amber-800:#ff990a;--ds-amber-900:#a35200;--ds-blue-700:#0070f3;--accents-1:var(--ds-gray-100);--accents-2:var(--ds-gray-200);--accents-3:var(--ds-gray-300);--accents-4:var(--ds-gray-400);--accents-5:var(--ds-gray-500);--accents-6:var(--ds-gray-600);--accents-7:var(--ds-gray-700);--accents-8:var(--ds-gray-800);--border:var(--accents-2);--muted:var(--accents-1);--muted-foreground:var(--accents-5);--primary:var(--foreground);--success:var(--ds-green-700);--error:var(--ds-red-800);--warning:var(--ds-amber-700);--lightningcss-light:initial;--lightningcss-dark: ;--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}[data-theme=dark]{--background:#000;--foreground:#fff;--ds-gray-100:#111;--ds-gray-200:#333;--ds-gray-300:#444;--ds-gray-400:#666;--ds-gray-500:#888;--ds-gray-600:#999;--ds-gray-700:#eaeaea;--ds-gray-800:#fafafa;--ds-gray-900:#fff;--ds-gray-1000:#fff;--ds-red-100:#2a1314;--ds-red-200:#3c1618;--ds-red-300:#561a1e;--ds-red-700:#e5484d;--ds-red-800:#d93036;--ds-red-900:#ff6166;--ds-green-100:#0b2212;--ds-green-200:#0f2e18;--ds-green-300:#12361b;--ds-green-700:#45a557;--ds-green-800:#398e4a;--ds-green-900:#62c073;--ds-amber-100:#291800;--ds-amber-200:#331b00;--ds-amber-300:#4d2a00;--ds-amber-700:#ffb224;--ds-amber-800:#ff990a;--ds-amber-900:#f2a20d;--ds-blue-700:#0070f3;--accents-1:var(--ds-gray-100);--accents-2:var(--ds-gray-200);--accents-3:var(--ds-gray-300);--accents-4:var(--ds-gray-400);--accents-5:var(--ds-gray-500);--accents-6:var(--ds-gray-600);--accents-7:var(--ds-gray-700);--accents-8:var(--ds-gray-800);--border:var(--accents-2);--muted:var(--accents-1);--muted-foreground:var(--accents-5);--primary:var(--foreground);--success:var(--ds-green-700);--error:var(--ds-red-800);--warning:var(--ds-amber-700);--lightningcss-light: ;--lightningcss-dark:initial;--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}body{background:var(--background);min-height:100vh;color:var(--foreground);margin:0;font-family:Inter,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Noto Sans,Ubuntu,Cantarell,Helvetica Neue,sans-serif;overflow:hidden}.ant-input-affix-wrapper:focus,.ant-input-affix-wrapper-focused{border-color:var(--foreground);box-shadow:0 0 0 1px var(--foreground)}.ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected{transition:none}@keyframes status-flash{0%{filter:brightness()}5%{filter:brightness(2.5)}15%{filter:brightness(1.8)}40%{filter:brightness(1.3)}to{filter:brightness()}}.status-icon-flash{animation:1.5s ease-out status-flash}@keyframes progress-segment-flash{0%{filter:brightness();box-shadow:none}10%{filter:brightness(2.5);box-shadow:0 0 8px 1px}to{filter:brightness();box-shadow:none}}.progress-flash-active{animation:1s cubic-bezier(.4,0,.2,1) progress-segment-flash}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-tracking{syntax:"*";inherits:false}@property --tw-ordinal{syntax:"*";inherits:false}@property --tw-slashed-zero{syntax:"*";inherits:false}@property --tw-numeric-figure{syntax:"*";inherits:false}@property --tw-numeric-spacing{syntax:"*";inherits:false}@property --tw-numeric-fraction{syntax:"*";inherits:false}@property --tw-duration{syntax:"*";inherits:false}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! For license information please see 837.e631233e.js.LICENSE.txt */
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
/*! For license information please see lib-react.48335539.js.LICENSE.txt */
|