Skip to content

Commit 953f414

Browse files
committed
refactor(web): extract shared UI and host packages
1 parent d0d5f8b commit 953f414

61 files changed

Lines changed: 815 additions & 352 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

‎web/BUILD.bazel‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ test_suite(
2020
"//web/service/submitqueue:typecheck_typecheck_test",
2121
"//web/submitqueue:test",
2222
"//web/submitqueue:typecheck_typecheck_test",
23+
"//web/platform:lint",
24+
"//web/platform:test",
25+
"//web/platform:typecheck_typecheck_test",
26+
"//web/service/shared:lint",
27+
"//web/service/shared:test",
28+
"//web/service/shared:typecheck_typecheck_test",
29+
"//web/submitqueue:lint",
2330
":e2e_typecheck_typecheck_test",
2431
],
2532
)

‎web/platform/BUILD.bazel‎

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
load("@npm//:defs.bzl", "npm_link_all_packages")
2+
load("@npm//web/platform:vitest/package_json.bzl", vitest = "bin")
3+
load("@npm//web/service/submitqueue:eslint/package_json.bzl", eslint = "bin")
4+
load("//web/tool:library.bzl", "web_library")
5+
6+
package(default_visibility = ["//web:__subpackages__"])
7+
8+
npm_link_all_packages(name = "node_modules")
9+
10+
web_library(
11+
assets = ["styles.css"],
12+
eslint = eslint,
13+
package = "@submitqueue/web-platform",
14+
test_deps = [
15+
":node_modules/@testing-library/jest-dom",
16+
":node_modules/@testing-library/react",
17+
":node_modules/@types/react-dom",
18+
":node_modules/jsdom",
19+
":node_modules/react-dom",
20+
":node_modules/vitest",
21+
],
22+
vitest = vitest,
23+
deps = [
24+
":node_modules/@types/node",
25+
":node_modules/@types/react",
26+
":node_modules/react",
27+
],
28+
)
29+
30+
exports_files(["styles.css"])
File renamed without changes.

‎web/platform/eslint.config.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { default } from "../tool/eslint.config.mjs";

‎web/platform/index.ts‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
"use client";
2+
3+
export * from "./navigation.js";
4+
export * from "./auto-refresh.js";

‎web/platform/package.json‎

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "@submitqueue/web-platform",
3+
"version": "0.0.0",
4+
"private": true,
5+
"type": "module",
6+
"sideEffects": [
7+
"./styles.css"
8+
],
9+
"files": [
10+
"dist",
11+
"styles.css"
12+
],
13+
"exports": {
14+
".": {
15+
"types": "./dist/index.d.ts",
16+
"import": "./dist/index.js"
17+
},
18+
"./path-segment": {
19+
"types": "./dist/path-segment.d.ts",
20+
"import": "./dist/path-segment.js"
21+
},
22+
"./styles.css": "./styles.css"
23+
},
24+
"scripts": {
25+
"build": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\" && tsc -p tsconfig.build.json",
26+
"lint": "eslint .",
27+
"test": "vitest run",
28+
"typecheck": "tsc --noEmit"
29+
},
30+
"devDependencies": {
31+
"@testing-library/jest-dom": "^6.6.3",
32+
"@testing-library/react": "^16.3.0",
33+
"@types/node": "22.18.6",
34+
"@types/react": "^19.1.0",
35+
"@types/react-dom": "^19.1.0",
36+
"jsdom": "^26.1.0",
37+
"react": "^19.1.0",
38+
"react-dom": "^19.1.0",
39+
"typescript": "^5.8.3",
40+
"vitest": "4.0.7"
41+
},
42+
"peerDependencies": {
43+
"react": ">=18 <20"
44+
}
45+
}

‎web/platform/styles.css‎

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/* Common native UI styles, scoped to .sq-app; hosts may override --sq-* properties. */
2+
.sq-app {
3+
color-scheme: light;
4+
--sq-surface: #fff;
5+
--sq-surface-muted: #f5f6f8;
6+
--sq-text: #202327;
7+
--sq-muted: #626a74;
8+
--sq-border: #e3e6e9;
9+
--sq-accent: #175fc5;
10+
--sq-accent-soft: #edf3ff;
11+
--sq-success: #187247;
12+
--sq-success-soft: #edf8f1;
13+
--sq-warning: #8b590f;
14+
--sq-warning-soft: #fff5e7;
15+
--sq-danger: #b33535;
16+
--sq-danger-soft: #fff0ef;
17+
--sq-font: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
18+
--sq-font-mono: ui-monospace, SFMono-Regular, Menlo, monospace;
19+
font-family: var(--sq-font);
20+
}
21+
.sq-app[data-theme="dark"] {
22+
color-scheme: dark;
23+
--sq-surface: #16181b;
24+
--sq-surface-muted: #202328;
25+
--sq-text: #e8eaed;
26+
--sq-muted: #a0a7b0;
27+
--sq-border: #33373d;
28+
--sq-accent: #6ea8ff;
29+
--sq-accent-soft: #1c2a40;
30+
--sq-success: #5cc28a;
31+
--sq-success-soft: #18301f;
32+
--sq-warning: #e0a84a;
33+
--sq-warning-soft: #36290f;
34+
--sq-danger: #ff7b72;
35+
--sq-danger-soft: #3a1f1e;
36+
}
37+
@media (prefers-color-scheme: dark) {
38+
.sq-app[data-theme="system"] {
39+
color-scheme: dark;
40+
--sq-surface: #16181b;
41+
--sq-surface-muted: #202328;
42+
--sq-text: #e8eaed;
43+
--sq-muted: #a0a7b0;
44+
--sq-border: #33373d;
45+
--sq-accent: #6ea8ff;
46+
--sq-accent-soft: #1c2a40;
47+
--sq-success: #5cc28a;
48+
--sq-success-soft: #18301f;
49+
--sq-warning: #e0a84a;
50+
--sq-warning-soft: #36290f;
51+
--sq-danger: #ff7b72;
52+
--sq-danger-soft: #3a1f1e;
53+
}
54+
}
55+
.sq-app, .sq-app * { box-sizing: border-box; }
56+
.sq-app { background: var(--sq-surface); color: var(--sq-text); font-size: 14px; line-height: 1.5; }
57+
.sq-app a { color: var(--sq-accent); text-decoration: none; overflow-wrap: anywhere; }
58+
.sq-app a:hover { text-decoration: underline; }
59+
.sq-app a:focus-visible, .sq-app button:focus-visible, .sq-app input:focus-visible, .sq-app select:focus-visible, .sq-app summary:focus-visible {
60+
outline: 3px solid var(--sq-accent); outline-offset: 3px;
61+
}
62+
.sq-app h1 { font-size: 26px; line-height: 1.25; margin: 0 0 10px; letter-spacing: -.5px; }
63+
.sq-app h2 { font-size: 16px; margin: 0 0 12px; }
64+
.sq-app p { margin: 8px 0; }
65+
.sq-app button, .sq-app input, .sq-app select {
66+
font: inherit; border: 1px solid var(--sq-border); border-radius: 6px;
67+
color: var(--sq-text); background: var(--sq-surface); padding: 8px 12px;
68+
}
69+
.sq-app button, .sq-app summary { cursor: pointer; }
70+
.sq-app button:disabled { cursor: default; opacity: .65; }
71+
.sq-app button:hover:not(:disabled) { background: var(--sq-surface-muted); }
72+
.sq-app input { min-width: 0; }
73+
.sq-app.site-frame { min-height: 100vh; display: grid; grid-template-rows: auto 1fr auto; }
74+
.sq-app .site-header, .sq-app .shell, .sq-app .site-footer { width: min(1200px, calc(100% - 48px)); margin-inline: auto; }
75+
.sq-app .site-header { min-height: 80px; display: flex; align-items: center; justify-content: space-between; gap: 20px; border-bottom: 1px solid var(--sq-border); }
76+
.sq-app .brand { display: flex; align-items: center; gap: 9px; color: var(--sq-text); font-weight: 600; font-size: 18px; }
77+
.sq-app .brand-mark { background: var(--sq-text); color: var(--sq-surface); border-radius: 6px; padding: 5px 7px; font-size: 12px; }
78+
.sq-app .environment-pill, .sq-app .site-footer, .sq-app .sq-secondary { color: var(--sq-muted); font-size: 12px; }
79+
.sq-app .site-footer { padding: 20px 0; border-top: 1px solid var(--sq-border); }
80+
.sq-app .shell { padding-block: 28px 48px; }
81+
.sq-app .centered-state { min-height: 100vh; display: grid; place-items: center; }
82+
.sq-app .page-heading { display: flex; justify-content: space-between; gap: 24px; align-items: flex-start; flex-wrap: wrap; margin-bottom: 24px; }
83+
.sq-app .page-heading p { color: var(--sq-muted); font-size: 12px; }
84+
.sq-app .eyebrow { font-size: 12px; margin: 0 0 10px; color: var(--sq-muted); }
85+
.sq-app .page-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
86+
.sq-app .sq-refresh { display: flex; align-items: center; gap: 10px; color: var(--sq-muted); font-size: 12px; }
87+
.sq-app .sq-error { padding: 16px; border-radius: 6px; background: var(--sq-danger-soft); }
88+
.sq-app .sq-error p { overflow-wrap: anywhere; }
89+
.sq-app .button-link { display: inline-block; margin-top: 16px; }
90+
.sq-app .state-card { max-width: 700px; }
91+
@media (max-width: 680px) {
92+
.sq-app .site-header, .sq-app .shell, .sq-app .site-footer { width: calc(100% - 32px); }
93+
.sq-app .site-header { gap: 12px; flex-wrap: wrap; padding-block: 14px; }
94+
.sq-app button, .sq-app select, .sq-app input { min-height: 44px; }
95+
}
96+
97+
98+
.sq-app .sq-table-scroll { overflow-x: auto; }
99+
.sq-app table { width: 100%; border-collapse: collapse; text-align: left; }
100+
.sq-app th { font-weight: 500; color: var(--sq-muted); font-size: 12px; padding: 10px 12px; border-bottom: 1px solid var(--sq-border); }
101+
.sq-app td { padding: 18px 12px; border-bottom: 1px solid var(--sq-border); vertical-align: top; }
102+
.sq-app th:first-child, .sq-app td:first-child { padding-left: 0; }
103+
.sq-app th:last-child, .sq-app td:last-child { padding-right: 0; }
104+
.sq-app td time { font-size: 12px; color: var(--sq-muted); }
105+
.sq-app .sq-table-footer { display: flex; justify-content: space-between; align-items: center; gap: 16px; margin-top: 18px; font-size: 12px; color: var(--sq-muted); }
106+
.sq-app .sq-status, .sq-app .sq-event { display: inline-block; padding: 2px 7px; border-radius: 4px; font-size: 12px; color: var(--sq-accent); background: var(--sq-accent-soft); white-space: nowrap; }
107+
.sq-app .sq-status[data-tone="neutral"] { color: var(--sq-muted); background: var(--sq-surface-muted); }
108+
.sq-app .sq-status[data-tone="success"] { color: var(--sq-success); background: var(--sq-success-soft); }
109+
.sq-app .sq-status[data-tone="danger"] { color: var(--sq-danger); background: var(--sq-danger-soft); }
110+
.sq-app .sq-status[data-tone="warning"] { color: var(--sq-warning); background: var(--sq-warning-soft); }
111+
.sq-app .sq-empty { padding: 35px 0; color: var(--sq-muted); }
112+
.sq-app .sq-breadcrumb { display: flex; gap: 10px; flex-wrap: wrap; color: var(--sq-muted); font-size: 12px; margin-bottom: 24px; }
113+
.sq-app .sq-detail-columns { display: grid; grid-template-columns: 1fr 1.15fr; gap: 38px; }
114+
.sq-app .sq-facts { margin: 0; }
115+
.sq-app .sq-facts > div { padding: 11px 0; border-bottom: 1px solid var(--sq-border); }
116+
.sq-app dt { color: var(--sq-muted); font-size: 12px; margin-bottom: 5px; }
117+
.sq-app dd { margin: 0; overflow-wrap: anywhere; }
118+
@media (max-width: 680px) {
119+
.sq-app .sq-detail-columns { grid-template-columns: 1fr; gap: 20px; }
120+
}

0 commit comments

Comments
 (0)