@openparachute/vault 0.6.4-rc.10 → 0.6.4-rc.12
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/core/src/conformance.test.ts +112 -0
- package/core/src/conformance.ts +214 -0
- package/core/src/store.ts +19 -0
- package/core/src/types.ts +15 -0
- package/package.json +1 -1
- package/src/routes.ts +86 -7
- package/src/routing.test.ts +125 -0
- package/src/routing.ts +10 -2
- package/src/self-register.test.ts +4 -4
- package/src/self-register.ts +1 -2
- package/web/ui/dist/assets/{index-UlvD8KHr.css → index-C5XqQM-c.css} +1 -1
- package/web/ui/dist/assets/index-TJ-XN4OZ.js +61 -0
- package/web/ui/dist/index.html +2 -2
- package/web/ui/dist/assets/index-DwYo23aY.js +0 -61
package/src/routing.ts
CHANGED
|
@@ -831,9 +831,17 @@ export async function route(
|
|
|
831
831
|
// and the broad-vs-narrowed shape (`vault:<verb>` from pvt_*, or
|
|
832
832
|
// `vault:<vaultName>:<verb>` from hub JWTs) are handled by
|
|
833
833
|
// `hasScopeForVault`.
|
|
834
|
-
|
|
834
|
+
//
|
|
835
|
+
// Read-only POST carve-out (vault#283): the conformance check is a POST
|
|
836
|
+
// (it carries a proposed-fields body) but mutates nothing — it counts
|
|
837
|
+
// notes. Gate it on `read`, not `write`, so a read-scoped caller can
|
|
838
|
+
// preview a schema tightening without holding a write credential.
|
|
839
|
+
const apiSubpath = apiMatch[1] ?? "";
|
|
840
|
+
const isReadOnlyPost =
|
|
841
|
+
req.method === "POST" && /^\/tags\/[^/]+\/conformance$/.test(apiSubpath);
|
|
842
|
+
const requiredVerb = isReadOnlyPost ? "read" : verbForMethod(req.method);
|
|
835
843
|
if (!hasScopeForVault(auth.scopes, vaultName, requiredVerb)) {
|
|
836
|
-
const requiredApiScope = scopeForMethod(req.method);
|
|
844
|
+
const requiredApiScope = isReadOnlyPost ? SCOPE_READ : scopeForMethod(req.method);
|
|
837
845
|
return Response.json(
|
|
838
846
|
{
|
|
839
847
|
error: "Forbidden",
|
|
@@ -62,17 +62,17 @@ function captureLogs(): {
|
|
|
62
62
|
|
|
63
63
|
describe("self-register", () => {
|
|
64
64
|
test("buildVaultServicePaths — no vaults yet → empty paths (no phantom /vault/default, #478)", () => {
|
|
65
|
-
expect(buildVaultServicePaths(undefined, []
|
|
65
|
+
expect(buildVaultServicePaths(undefined, [])).toEqual([]);
|
|
66
66
|
});
|
|
67
67
|
|
|
68
68
|
test("buildVaultServicePaths — default vault sorts first", () => {
|
|
69
69
|
expect(
|
|
70
|
-
buildVaultServicePaths("default", ["alpha", "default", "beta"]
|
|
70
|
+
buildVaultServicePaths("default", ["alpha", "default", "beta"]),
|
|
71
71
|
).toEqual(["/vault/default", "/vault/alpha", "/vault/beta"]);
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
test("buildVaultServicePaths — no default → map by listed order", () => {
|
|
75
|
-
expect(buildVaultServicePaths(undefined, ["alpha", "beta"]
|
|
75
|
+
expect(buildVaultServicePaths(undefined, ["alpha", "beta"])).toEqual([
|
|
76
76
|
"/vault/alpha",
|
|
77
77
|
"/vault/beta",
|
|
78
78
|
]);
|
|
@@ -80,7 +80,7 @@ describe("self-register", () => {
|
|
|
80
80
|
|
|
81
81
|
test("buildVaultServicePaths — default points to missing vault → ignore default", () => {
|
|
82
82
|
expect(
|
|
83
|
-
buildVaultServicePaths("missing", ["alpha", "beta"]
|
|
83
|
+
buildVaultServicePaths("missing", ["alpha", "beta"]),
|
|
84
84
|
).toEqual(["/vault/alpha", "/vault/beta"]);
|
|
85
85
|
});
|
|
86
86
|
|
package/src/self-register.ts
CHANGED
|
@@ -69,7 +69,6 @@ import { listVaults, readGlobalConfig, DEFAULT_PORT } from "./config.ts";
|
|
|
69
69
|
export function buildVaultServicePaths(
|
|
70
70
|
defaultVault: string | undefined,
|
|
71
71
|
vaults: readonly string[],
|
|
72
|
-
fallbackFromManifest: readonly string[],
|
|
73
72
|
): string[] {
|
|
74
73
|
if (vaults.length === 0) return [];
|
|
75
74
|
if (defaultVault && vaults.includes(defaultVault)) {
|
|
@@ -165,7 +164,7 @@ export function selfRegister(deps: SelfRegisterDeps): SelfRegisterResult {
|
|
|
165
164
|
return { status: "failed", reason: msg };
|
|
166
165
|
}
|
|
167
166
|
|
|
168
|
-
const paths = buildVaultServicePaths(globalConfig.default_vault, vaults
|
|
167
|
+
const paths = buildVaultServicePaths(globalConfig.default_vault, vaults);
|
|
169
168
|
const port = globalConfig.port ?? DEFAULT_PORT;
|
|
170
169
|
|
|
171
170
|
// Derive the health path from the primary path (paths[0]) rather than
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{--bg: #faf8f4;--bg-soft: #f3f0ea;--fg: #2c2a26;--fg-muted: #6b6860;--fg-dim: #9a9690;--accent: #4a7c59;--accent-soft: rgba(74, 124, 89, .08);--accent-hover: #3d6849;--border: #e4e0d8;--border-light: #ece9e2;--card-bg: #ffffff;--error: #a3392b;--error-soft: rgba(163, 57, 43, .08);--warn: #b08023;--warn-soft: rgba(176, 128, 35, .08);--success: #3d6849;--success-soft: rgba(61, 104, 73, .08);--font-serif: Georgia, "Times New Roman", serif;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;--font-mono: ui-monospace, "SF Mono", Menlo, Monaco, "Cascadia Mono", monospace;font-family:var(--font-sans)}*{box-sizing:border-box}html,body{margin:0;padding:0;background:var(--bg);color:var(--fg)}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}code{font-family:var(--font-mono);font-size:.85em;background:var(--bg-soft);padding:.1em .3em;border-radius:3px}.page{max-width:880px;margin:0 auto;padding:1.5rem 1.5rem 6rem}.nav{display:flex;gap:1rem;align-items:center;padding-bottom:1rem;border-bottom:1px solid var(--border);margin-bottom:2rem}.nav .brand{font-weight:600;font-family:var(--font-serif);font-size:1.15rem;margin-right:auto}.nav .brand .sub{color:var(--fg-dim);font-size:.78rem;font-weight:400;margin-left:.4rem;font-family:var(--font-sans)}.nav a{color:var(--fg-muted);font-size:.95rem}.nav a:hover{text-decoration:none;color:var(--fg)}h2{margin:0 0 1rem;font-size:1.4rem;font-weight:500}.muted{color:var(--fg-muted);font-size:.92rem}.dim{color:var(--fg-dim);font-size:.85rem}.error-banner{background:var(--error-soft);border:1px solid var(--error);color:var(--error);padding:.75rem 1rem;border-radius:8px;margin-bottom:1rem;font-size:.9rem}.warn-banner{background:var(--warn-soft);border:1px solid var(--warn);color:var(--warn);padding:.75rem 1rem;border-radius:8px;margin-bottom:1rem;font-size:.9rem}.empty{padding:3rem 1.5rem;text-align:center;color:var(--fg-muted);background:var(--bg-soft);border-radius:10px}.list-header{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;margin-bottom:1rem}.list-header h2{margin:0}.vault-row{display:flex;align-items:center;gap:1rem;padding:.85rem 1rem;background:#fff;border:1px solid var(--border);border-radius:8px;margin-bottom:.5rem;text-decoration:none;color:inherit;transition:border-color .15s ease}.vault-row:hover{border-color:var(--accent);text-decoration:none}.vault-row .body{flex:1;min-width:0}.vault-row .name{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.vault-row .name code{font-size:.95em}.vault-row .chev{color:var(--fg-dim);font-size:1.2rem}.vault-row-group{margin-bottom:.5rem}.vault-row-group .vault-row{margin-bottom:0}.vault-row-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.vault-usage{font-size:.85rem}.field-error{color:#b3402a;font-size:.85rem;margin-top:.35rem}.section{background:#fff;border:1px solid var(--border);border-radius:10px;padding:1.25rem 1.5rem;margin-bottom:1.5rem}.kv{display:grid;grid-template-columns:8.5rem 1fr;gap:.5rem 1rem;font-size:.92rem}.kv>div:nth-child(odd){color:var(--fg-muted)}.kv code{word-break:break-all}.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));gap:1rem;font-size:.92rem}.stat{background:var(--bg-soft);border-radius:8px;padding:.85rem 1rem}.stat .label{color:var(--fg-muted);font-size:.82rem;margin-bottom:.2rem}.stat .value{font-size:1.4rem;font-weight:500;font-family:var(--font-mono)}button{font:inherit;background:var(--accent);color:#fff;border:0;border-radius:6px;padding:.55rem 1.1rem;cursor:pointer;transition:background .15s ease}button:hover{background:var(--accent-hover)}button:disabled{opacity:.5;cursor:not-allowed}button.secondary{background:#fff;color:var(--fg);border:1px solid var(--border)}button.secondary:hover{background:var(--bg-soft)}input,select{font:inherit;background:#fff;color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:.5rem .7rem;width:100%;max-width:28rem}input:focus,select:focus{outline:2px solid var(--accent-soft);border-color:var(--accent)}.form-row{margin-bottom:1rem}.form-row label{display:block;font-size:.88rem;color:var(--fg-muted);margin-bottom:.3rem}.actions{display:flex;gap:.5rem;margin-top:1rem}.manage-list{margin:0;padding-left:1.1rem;font-size:.95rem}.manage-list li{margin-bottom:.4rem}.mint-banner{background:var(--success-soft);border:1px solid var(--success);border-radius:10px;padding:1.25rem 1.5rem;margin-bottom:1.5rem}.mint-banner h3{margin:0 0 .5rem;font-size:1rem;color:var(--success)}.mint-banner .token-box{display:flex;align-items:center;gap:.5rem;margin:.85rem 0 .5rem}.mint-banner code{flex:1;font-size:.9rem;padding:.6rem .75rem;background:#fff;border:1px solid var(--border);word-break:break-all;-webkit-user-select:all;user-select:all}.mint-banner .warn{margin:.75rem 0 0;font-size:.85rem;color:var(--warn)}.mint-banner .actions{margin-top:1rem}.mcp-connect-card h3{margin:0 0 .4rem;font-size:1rem;font-weight:500}.mcp-connect-card>p{margin-top:0}.mcp-connect-card .token-box{display:flex;align-items:center;gap:.5rem;margin:.35rem 0 .25rem}.mcp-connect-card .token-box code{flex:1;font-size:.85rem;padding:.55rem .7rem;background:var(--bg-soft);border:1px solid var(--border);border-radius:6px;word-break:break-all;-webkit-user-select:all;user-select:all}.mcp-field{margin-top:.9rem}.mcp-field-label{display:block;font-size:.82rem;font-weight:600;color:var(--fg-muted)}.mcp-field .dim{margin:.3rem 0 0}.mcp-docs-link{margin:.9rem 0 0}.token-list{display:flex;flex-direction:column;gap:.5rem}.token-row{display:flex;align-items:flex-start;gap:1rem;padding:.85rem 1rem;background:#fff;border:1px solid var(--border);border-radius:8px}.token-row .body{flex:1;min-width:0}.token-row .name{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.3rem}.token-row .meta{font-size:.85rem;margin-bottom:.2rem}.token-row .actions{margin-top:0;flex-shrink:0}.scope-tag{font-size:.78rem;padding:.1em .4em;margin-right:.3em}.tag-pill{background:var(--accent-soft, rgba(120, 120, 200, .12))}.tag-picker{display:flex;flex-wrap:wrap;gap:.4rem .8rem;margin-top:.35rem;max-height:9rem;overflow-y:auto;padding:.4rem .5rem;border:1px solid var(--border, #d8d8de);border-radius:4px}.tag-checkbox{display:inline-flex;align-items:center;gap:.3rem;font-size:.85rem;cursor:pointer;-webkit-user-select:none;user-select:none}.tag-checkbox input[type=checkbox]{margin:0}.preset-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.75rem;margin-top:.35rem}.preset-card{background:var(--bg-soft);color:var(--fg);border:1px solid var(--border);border-radius:8px;padding:.85rem 1rem;text-align:left;display:flex;flex-direction:column;gap:.25rem;cursor:pointer;transition:border-color .15s ease,background .15s ease}.preset-card:hover{border-color:var(--accent);background:#fff}.preset-card:disabled{opacity:.5;cursor:not-allowed}.preset-card strong{font-size:.95rem;font-weight:500}.preset-card .dim{font-size:.8rem;line-height:1.35}.radio-row{display:flex;align-items:center;gap:.5rem;margin-bottom:.35rem;font-size:.92rem;cursor:pointer}.radio-row input[type=radio]{margin:0}.info-banner{background:var(--accent-soft);border:1px solid var(--accent);color:var(--accent-hover);padding:.75rem 1rem;border-radius:8px;margin-bottom:1rem;font-size:.9rem}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000059;display:flex;align-items:center;justify-content:center;z-index:100;padding:1rem}.modal{background:var(--bg);border:1px solid var(--border);border-radius:12px;padding:1.5rem;width:min(520px,100%);max-height:90vh;overflow-y:auto;box-shadow:0 10px 40px #0003}.device-code-row{display:flex;align-items:center;gap:.75rem;margin:.75rem 0}.device-code{font-size:1.4rem;letter-spacing:.1em;padding:.6rem 1rem;background:var(--bg-soft);border-radius:8px;border:1px solid var(--border);font-family:ui-monospace,SF Mono,Menlo,monospace;-webkit-user-select:all;user-select:all;flex:1;text-align:center}.repo-list{display:flex;flex-direction:column;gap:.35rem;max-height:260px;overflow-y:auto;border:1px solid var(--border);border-radius:8px;padding:.4rem;background:var(--bg-soft)}.repo-row{background:var(--bg);border:1px solid transparent;border-radius:6px;padding:.55rem .75rem;font-size:.92rem;display:flex;align-items:baseline;justify-content:space-between;gap:1rem;cursor:pointer;text-align:left;color:inherit;transition:border-color .12s ease,background .12s ease}.repo-row:hover{border-color:var(--accent);background:#fff}.repo-row:disabled{opacity:.5;cursor:not-allowed}
|
|
1
|
+
:root{--bg: #faf8f4;--bg-soft: #f3f0ea;--fg: #2c2a26;--fg-muted: #6b6860;--fg-dim: #9a9690;--accent: #4a7c59;--accent-soft: rgba(74, 124, 89, .08);--accent-hover: #3d6849;--border: #e4e0d8;--border-light: #ece9e2;--card-bg: #ffffff;--error: #a3392b;--error-soft: rgba(163, 57, 43, .08);--warn: #b08023;--warn-soft: rgba(176, 128, 35, .08);--success: #3d6849;--success-soft: rgba(61, 104, 73, .08);--font-serif: Georgia, "Times New Roman", serif;--font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;--font-mono: ui-monospace, "SF Mono", Menlo, Monaco, "Cascadia Mono", monospace;font-family:var(--font-sans)}*{box-sizing:border-box}html,body{margin:0;padding:0;background:var(--bg);color:var(--fg)}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}code{font-family:var(--font-mono);font-size:.85em;background:var(--bg-soft);padding:.1em .3em;border-radius:3px}.page{max-width:880px;margin:0 auto;padding:1.5rem 1.5rem 6rem}.nav{display:flex;gap:1rem;align-items:center;padding-bottom:1rem;border-bottom:1px solid var(--border);margin-bottom:2rem}.nav .brand{font-weight:600;font-family:var(--font-serif);font-size:1.15rem;margin-right:auto}.nav .brand .sub{color:var(--fg-dim);font-size:.78rem;font-weight:400;margin-left:.4rem;font-family:var(--font-sans)}.nav a{color:var(--fg-muted);font-size:.95rem}.nav a:hover{text-decoration:none;color:var(--fg)}h2{margin:0 0 1rem;font-size:1.4rem;font-weight:500}.muted{color:var(--fg-muted);font-size:.92rem}.dim{color:var(--fg-dim);font-size:.85rem}.error-banner{background:var(--error-soft);border:1px solid var(--error);color:var(--error);padding:.75rem 1rem;border-radius:8px;margin-bottom:1rem;font-size:.9rem}.warn-banner{background:var(--warn-soft);border:1px solid var(--warn);color:var(--warn);padding:.75rem 1rem;border-radius:8px;margin-bottom:1rem;font-size:.9rem}.empty{padding:3rem 1.5rem;text-align:center;color:var(--fg-muted);background:var(--bg-soft);border-radius:10px}.list-header{display:flex;align-items:baseline;justify-content:space-between;gap:1rem;margin-bottom:1rem}.list-header h2{margin:0}.vault-row{display:flex;align-items:center;gap:1rem;padding:.85rem 1rem;background:#fff;border:1px solid var(--border);border-radius:8px;margin-bottom:.5rem;text-decoration:none;color:inherit;transition:border-color .15s ease}.vault-row:hover{border-color:var(--accent);text-decoration:none}.vault-row .body{flex:1;min-width:0}.vault-row .name{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap}.vault-row .name code{font-size:.95em}.vault-row .chev{color:var(--fg-dim);font-size:1.2rem}.vault-row-group{margin-bottom:.5rem}.vault-row-group .vault-row{margin-bottom:0}.vault-row-actions{display:flex;align-items:center;gap:.5rem;flex-shrink:0}.vault-usage{font-size:.85rem}.field-error{color:#b3402a;font-size:.85rem;margin-top:.35rem}.section{background:#fff;border:1px solid var(--border);border-radius:10px;padding:1.25rem 1.5rem;margin-bottom:1.5rem}.kv{display:grid;grid-template-columns:8.5rem 1fr;gap:.5rem 1rem;font-size:.92rem}.kv>div:nth-child(odd){color:var(--fg-muted)}.kv code{word-break:break-all}.stats{display:grid;grid-template-columns:repeat(auto-fit,minmax(8rem,1fr));gap:1rem;font-size:.92rem}.stat{background:var(--bg-soft);border-radius:8px;padding:.85rem 1rem}.stat .label{color:var(--fg-muted);font-size:.82rem;margin-bottom:.2rem}.stat .value{font-size:1.4rem;font-weight:500;font-family:var(--font-mono)}button{font:inherit;background:var(--accent);color:#fff;border:0;border-radius:6px;padding:.55rem 1.1rem;cursor:pointer;transition:background .15s ease}button:hover{background:var(--accent-hover)}button:disabled{opacity:.5;cursor:not-allowed}button.secondary{background:#fff;color:var(--fg);border:1px solid var(--border)}button.secondary:hover{background:var(--bg-soft)}input,select{font:inherit;background:#fff;color:var(--fg);border:1px solid var(--border);border-radius:6px;padding:.5rem .7rem;width:100%;max-width:28rem}input:focus,select:focus{outline:2px solid var(--accent-soft);border-color:var(--accent)}.form-row{margin-bottom:1rem}.form-row label{display:block;font-size:.88rem;color:var(--fg-muted);margin-bottom:.3rem}.actions{display:flex;gap:.5rem;margin-top:1rem}.manage-list{margin:0;padding-left:1.1rem;font-size:.95rem}.manage-list li{margin-bottom:.4rem}.mint-banner{background:var(--success-soft);border:1px solid var(--success);border-radius:10px;padding:1.25rem 1.5rem;margin-bottom:1.5rem}.mint-banner h3{margin:0 0 .5rem;font-size:1rem;color:var(--success)}.mint-banner .token-box{display:flex;align-items:center;gap:.5rem;margin:.85rem 0 .5rem}.mint-banner code{flex:1;font-size:.9rem;padding:.6rem .75rem;background:#fff;border:1px solid var(--border);word-break:break-all;-webkit-user-select:all;user-select:all}.mint-banner .warn{margin:.75rem 0 0;font-size:.85rem;color:var(--warn)}.mint-banner .actions{margin-top:1rem}.mcp-connect-card h3{margin:0 0 .4rem;font-size:1rem;font-weight:500}.mcp-connect-card>p{margin-top:0}.mcp-connect-card .token-box{display:flex;align-items:center;gap:.5rem;margin:.35rem 0 .25rem}.mcp-connect-card .token-box code{flex:1;font-size:.85rem;padding:.55rem .7rem;background:var(--bg-soft);border:1px solid var(--border);border-radius:6px;word-break:break-all;-webkit-user-select:all;user-select:all}.mcp-field{margin-top:.9rem}.mcp-field-label{display:block;font-size:.82rem;font-weight:600;color:var(--fg-muted)}.mcp-field .dim{margin:.3rem 0 0}.mcp-docs-link{margin:.9rem 0 0}.token-list{display:flex;flex-direction:column;gap:.5rem}.token-row{display:flex;align-items:flex-start;gap:1rem;padding:.85rem 1rem;background:#fff;border:1px solid var(--border);border-radius:8px}.token-row .body{flex:1;min-width:0}.token-row .name{display:flex;align-items:center;gap:.5rem;flex-wrap:wrap;margin-bottom:.3rem}.token-row .meta{font-size:.85rem;margin-bottom:.2rem}.token-row .actions{margin-top:0;flex-shrink:0}.scope-tag{font-size:.78rem;padding:.1em .4em;margin-right:.3em}.tag-pill{background:var(--accent-soft, rgba(120, 120, 200, .12))}.tag-picker{display:flex;flex-wrap:wrap;gap:.4rem .8rem;margin-top:.35rem;max-height:9rem;overflow-y:auto;padding:.4rem .5rem;border:1px solid var(--border, #d8d8de);border-radius:4px}.tag-checkbox{display:inline-flex;align-items:center;gap:.3rem;font-size:.85rem;cursor:pointer;-webkit-user-select:none;user-select:none}.tag-checkbox input[type=checkbox]{margin:0}.preset-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(11rem,1fr));gap:.75rem;margin-top:.35rem}.preset-card{background:var(--bg-soft);color:var(--fg);border:1px solid var(--border);border-radius:8px;padding:.85rem 1rem;text-align:left;display:flex;flex-direction:column;gap:.25rem;cursor:pointer;transition:border-color .15s ease,background .15s ease}.preset-card:hover{border-color:var(--accent);background:#fff}.preset-card:disabled{opacity:.5;cursor:not-allowed}.preset-card strong{font-size:.95rem;font-weight:500}.preset-card .dim{font-size:.8rem;line-height:1.35}.radio-row{display:flex;align-items:center;gap:.5rem;margin-bottom:.35rem;font-size:.92rem;cursor:pointer}.radio-row input[type=radio]{margin:0}.info-banner{background:var(--accent-soft);border:1px solid var(--accent);color:var(--accent-hover);padding:.75rem 1rem;border-radius:8px;margin-bottom:1rem;font-size:.9rem}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;background:#00000059;display:flex;align-items:center;justify-content:center;z-index:100;padding:1rem}.modal{background:var(--bg);border:1px solid var(--border);border-radius:12px;padding:1.5rem;width:min(520px,100%);max-height:90vh;overflow-y:auto;box-shadow:0 10px 40px #0003}.device-code-row{display:flex;align-items:center;gap:.75rem;margin:.75rem 0}.device-code{font-size:1.4rem;letter-spacing:.1em;padding:.6rem 1rem;background:var(--bg-soft);border-radius:8px;border:1px solid var(--border);font-family:ui-monospace,SF Mono,Menlo,monospace;-webkit-user-select:all;user-select:all;flex:1;text-align:center}.repo-list{display:flex;flex-direction:column;gap:.35rem;max-height:260px;overflow-y:auto;border:1px solid var(--border);border-radius:8px;padding:.4rem;background:var(--bg-soft)}.repo-row{background:var(--bg);border:1px solid transparent;border-radius:6px;padding:.55rem .75rem;font-size:.92rem;display:flex;align-items:baseline;justify-content:space-between;gap:1rem;cursor:pointer;text-align:left;color:inherit;transition:border-color .12s ease,background .12s ease}.repo-row:hover{border-color:var(--accent);background:#fff}.repo-row:disabled{opacity:.5;cursor:not-allowed}.schema-layout{display:grid;grid-template-columns:minmax(180px,240px) 1fr;gap:1.25rem;align-items:start}@media(max-width:720px){.schema-layout{grid-template-columns:1fr}}.schema-taglist{border:1px solid var(--border);border-radius:10px;padding:.5rem;background:var(--bg-soft);max-height:70vh;overflow-y:auto}.tag-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:.25rem}.tag-list-item{width:100%;display:flex;flex-direction:column;gap:.2rem;align-items:flex-start;background:transparent;border:1px solid transparent;border-radius:7px;padding:.4rem .5rem;cursor:pointer;text-align:left;color:inherit;font:inherit}.tag-list-item:hover{background:#fff;border-color:var(--border)}.tag-list-item.selected{background:var(--accent-soft);border-color:var(--accent)}.tag-list-item .tag-name{font-size:.9rem}.tag-meta{display:flex;flex-wrap:wrap;gap:.3rem;align-items:center}.badge{display:inline-block;font-size:.7rem;line-height:1.4;padding:0 .4rem;border-radius:999px;background:var(--accent-soft);color:var(--accent);border:1px solid var(--accent)}.badge-strict{background:var(--error-soft);color:var(--error);border-color:var(--error)}.schema-detail{border:1px solid var(--border);border-radius:10px;padding:1rem 1.25rem;min-width:0}.schema-fields,.schema-parents{border:1px solid var(--border);border-radius:8px;padding:.75rem;margin:1rem 0}.schema-fields legend,.schema-parents legend{font-weight:600;font-size:.9rem;padding:0 .4rem}.field-row{border-bottom:1px solid var(--border-light);padding:.5rem 0}.field-row:last-of-type{border-bottom:none}.field-row-main{display:flex;flex-wrap:wrap;gap:.5rem;align-items:center}.field-name-input{flex:1 1 140px;min-width:120px}.field-row-main select{flex:0 0 auto;width:auto}.field-flag{display:inline-flex;align-items:center;gap:.25rem;font-size:.85rem;white-space:nowrap}.field-flag input{width:auto;margin:0}.field-remove{padding:.2rem .5rem;line-height:1}.field-row-extra{display:flex;flex-wrap:wrap;gap:.5rem;margin-top:.4rem}.field-enum-input,.field-desc-input{flex:1 1 200px;min-width:160px}.field-codeclarers{margin:.35rem 0 0}.field-codeclarers code{margin-left:.25rem}.parent-grid{display:flex;flex-wrap:wrap;gap:.4rem}.parent-chip{display:inline-flex;align-items:center;gap:.3rem;border:1px solid var(--border);border-radius:999px;padding:.15rem .55rem;font-size:.85rem}.parent-chip input{width:auto;margin:0}.schema-table{width:100%;border-collapse:collapse;font-size:.88rem}.schema-table th,.schema-table td{text-align:left;padding:.3rem .5rem;border-bottom:1px solid var(--border-light)}.schema-table th{font-weight:600;color:var(--fg-muted)}.schema-table td code{margin-right:.3rem}.schema-table .tag-self{background:var(--accent-soft);border-radius:4px;padding:0 .2rem}.schema-table .row-this-tag td{background:var(--accent-soft)}
|