fix 前端优化
This commit is contained in:
@@ -37,6 +37,21 @@ describe("AppShell mobile navigation", () => {
|
||||
expect(html).toContain("关闭导航菜单");
|
||||
});
|
||||
|
||||
it("renders the animation layer above the workspace background", () => {
|
||||
const html = renderToStaticMarkup(
|
||||
<AppShell routes={routes} currentPage="home" session={session} operations={[]} onNavigate={() => undefined}>
|
||||
<p>页面内容</p>
|
||||
</AppShell>
|
||||
);
|
||||
|
||||
expect(html.indexOf("workspace-background-layer")).toBeLessThan(html.indexOf("global-particle-layer"));
|
||||
expect(html).toContain("global-orbital-construct");
|
||||
expect(html).toContain("global-particle-lightfall");
|
||||
expect(html).toContain("global-particle-side-ray");
|
||||
expect(html).not.toContain("global-particle-orbit");
|
||||
expect(html).not.toContain("global-particle-frame");
|
||||
});
|
||||
|
||||
it("uses bounded edge-swipe thresholds and accessible dismissal", () => {
|
||||
expect(appShellSource).toContain("start.x <= 28 && deltaX >= 56");
|
||||
expect(appShellSource).toContain("deltaX <= -56");
|
||||
|
||||
@@ -11,23 +11,53 @@ const particleSlots = [
|
||||
{ x: "92%", y: "86%" }
|
||||
];
|
||||
|
||||
const orbitalConstructSlots = [
|
||||
{ x: "76%", y: "82%", size: "min(54vw, 680px)", drift: "-6deg" },
|
||||
{ x: "14%", y: "58%", size: "min(34vw, 420px)", drift: "18deg" },
|
||||
{ x: "94%", y: "22%", size: "min(24vw, 300px)", drift: "-26deg" }
|
||||
];
|
||||
|
||||
type ParticleStyle = CSSProperties & {
|
||||
"--particle-index": string;
|
||||
"--particle-x": string;
|
||||
"--particle-y": string;
|
||||
};
|
||||
|
||||
type OrbitalConstructStyle = CSSProperties & {
|
||||
"--construct-index": string;
|
||||
"--construct-x": string;
|
||||
"--construct-y": string;
|
||||
"--construct-size": string;
|
||||
"--construct-drift": string;
|
||||
};
|
||||
|
||||
export function MagicalParticleLayer() {
|
||||
return (
|
||||
<>
|
||||
<div className="workspace-background-layer" aria-hidden="true" />
|
||||
<div className="global-particle-layer" aria-hidden="true">
|
||||
<span className="global-particle-orbit" />
|
||||
<span className="global-particle-orbit global-particle-orbit-secondary" />
|
||||
<span className="global-particle-lightfall global-particle-lightfall-a" />
|
||||
<span className="global-particle-lightfall global-particle-lightfall-b" />
|
||||
<span className="global-particle-lightfall global-particle-lightfall-c" />
|
||||
<span className="global-particle-side-ray global-particle-side-ray-left" />
|
||||
<span className="global-particle-side-ray global-particle-side-ray-right" />
|
||||
{orbitalConstructSlots.map((construct, index) => (
|
||||
<span
|
||||
key={`${construct.x}-${construct.y}-${construct.size}`}
|
||||
className="global-orbital-construct"
|
||||
style={
|
||||
{
|
||||
"--construct-index": String(index),
|
||||
"--construct-x": construct.x,
|
||||
"--construct-y": construct.y,
|
||||
"--construct-size": construct.size,
|
||||
"--construct-drift": construct.drift
|
||||
} as OrbitalConstructStyle
|
||||
}
|
||||
/>
|
||||
))}
|
||||
<span className="global-particle-sweep" />
|
||||
<span className="global-particle-ribbon" />
|
||||
<span className="global-particle-frame global-particle-frame-tl" />
|
||||
<span className="global-particle-frame global-particle-frame-br" />
|
||||
{particleSlots.map((slot, index) => (
|
||||
<span
|
||||
key={`${slot.x}-${slot.y}`}
|
||||
|
||||
@@ -164,6 +164,8 @@ describe("first-party console pages", () => {
|
||||
expect(serversPageSource).toContain('className="provider-form dialog-form"');
|
||||
expect(serversPageSource).toContain('name="profileKey"');
|
||||
expect(serversPageSource).toContain("runtimeBindingFields");
|
||||
expect(serversPageSource).toContain("createProfileUnavailable");
|
||||
expect(serversPageSource).toContain("当前插件没有声明运行配置");
|
||||
expect(serversPageSource).toContain("updateBinding(field.key");
|
||||
expect(serversPageSource).toContain('type={field.sensitive ? "password" : "text"}');
|
||||
for (const forbidden of ["secret://", "/Users/", "/var/run/", "unix://", "tcp://"]) {
|
||||
|
||||
@@ -139,6 +139,8 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
|
||||
const createPending = operations.isPending("platform", "创建服务器");
|
||||
const canManageServers = session.capabilities.includes("servers.manage");
|
||||
const selectedCreatePlugin = plugins.find((plugin) => plugin.id === form.pluginId);
|
||||
const createProfileOptions = selectedCreatePlugin?.runtimeProfiles?.lifecycleProfiles ?? [];
|
||||
const createProfileUnavailable = Boolean(selectedCreatePlugin && createProfileOptions.length === 0);
|
||||
const createBindingFields = runtimeBindingFields(selectedCreatePlugin, form.profileKey);
|
||||
|
||||
function updateForm(event: ChangeEvent<HTMLInputElement | HTMLSelectElement>) {
|
||||
@@ -442,13 +444,19 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
|
||||
<label>
|
||||
运行配置
|
||||
<select name="profileKey" value={form.profileKey} onChange={updateForm} required>
|
||||
{(selectedCreatePlugin?.runtimeProfiles?.lifecycleProfiles ?? []).map((profile) => (
|
||||
{createProfileOptions.map((profile) => (
|
||||
<option key={profile.key} value={profile.key}>
|
||||
{profile.key} · {profile.mode}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
{createProfileUnavailable && (
|
||||
<div className="operations-inline-warning" role="status">
|
||||
<AlertTriangle size={14} />
|
||||
<span>当前插件没有声明运行配置,请重新注册完整 manifest 后刷新。</span>
|
||||
</div>
|
||||
)}
|
||||
{createBindingFields.map((field) => (
|
||||
<label key={field.key}>
|
||||
{field.key}{field.required ? "(必填)" : ""}
|
||||
@@ -465,7 +473,7 @@ export function ServersPage({ session, operations, onNavigate }: PageComponentPr
|
||||
</div>
|
||||
<div className="confirm-actions">
|
||||
<button type="button" disabled={createPending} onClick={() => setShowCreate(false)}>取消</button>
|
||||
<button type="submit" className="confirm-primary" disabled={createPending || !form.profileKey} title="创建服务器">
|
||||
<button type="submit" className="confirm-primary" disabled={createPending || !form.profileKey || createProfileUnavailable} title="创建服务器">
|
||||
<Sparkles size={16} />
|
||||
<span>{createPending ? "创建中…" : "创建并安装"}</span>
|
||||
</button>
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
||||
<g stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="256" cy="256" r="178" stroke="#fff6fb" stroke-opacity=".64" stroke-width="2.4"/>
|
||||
<circle cx="256" cy="256" r="139" stroke="#ffd66d" stroke-opacity=".58" stroke-width="1.8"/>
|
||||
<circle cx="256" cy="256" r="84" stroke="#ff8fd2" stroke-opacity=".62" stroke-width="1.7"/>
|
||||
<path d="M256 91v330M91 256h330M139 139l234 234M373 139 139 373" stroke="#fff6fb" stroke-opacity=".24" stroke-width="1.2"/>
|
||||
<path d="m256 125 37 82 89 9-66 60 19 87-79-44-79 44 19-87-66-60 89-9z" stroke="#ffd66d" stroke-opacity=".54" stroke-width="1.9"/>
|
||||
<path d="M165 194c28-42 72-65 123-62M346 318c-28 42-72 65-123 62" stroke="#ff8fd2" stroke-opacity=".62" stroke-width="4"/>
|
||||
<path d="M155 318c-18-30-26-62-22-96M358 194c18 30 26 62 22 96" stroke="#fff6fb" stroke-opacity=".42" stroke-width="3"/>
|
||||
<path d="m256 174 19 39 43 6-31 31 7 44-38-21-38 21 7-44-31-31 43-6z" stroke="#fff6fb" stroke-opacity=".78" stroke-width="2"/>
|
||||
<path d="M256 50l8 17 19 3-14 13 4 19-17-9-17 9 4-19-14-13 19-3zM256 410l8 17 19 3-14 13 4 19-17-9-17 9 4-19-14-13 19-3zM50 256l17-8 3-19 13 14 19-4-9 17 9 17-19-4-13 14-3-19zM410 256l17-8 3-19 13 14 19-4-9 17 9 17-19-4-13 14-3-19z" stroke="#ffd66d" stroke-opacity=".72" stroke-width="1.7"/>
|
||||
<path d="M103 178c22-40 52-70 92-91M409 334c-22 40-52 70-92 91" stroke="#8cf0ff" stroke-opacity=".32" stroke-width="1.8" stroke-dasharray="10 13"/>
|
||||
<path d="M103 334c-22-40-28-82-19-126M409 178c22 40 28 82 19 126" stroke="#ff8fd2" stroke-opacity=".32" stroke-width="1.8" stroke-dasharray="8 12"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,20 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" fill="none">
|
||||
<g stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="256" cy="256" r="34" stroke="#ffd66d" stroke-opacity=".66" stroke-width="2"/>
|
||||
<circle cx="256" cy="256" r="48" stroke="#48e6ff" stroke-opacity=".42" stroke-width="1.5" stroke-dasharray="6 11"/>
|
||||
<path d="M79 260a177 177 0 0 1 69-140 177 177 0 0 1 164-31" stroke="#48e6ff" stroke-opacity=".5" stroke-width="2.4"/>
|
||||
<path d="M432 252a177 177 0 0 1-63 135 177 177 0 0 1-153 39" stroke="#48e6ff" stroke-opacity=".46" stroke-width="2.4"/>
|
||||
<path d="M108 344a173 173 0 0 1-27-66M375 111a173 173 0 0 1 48 83" stroke="#ffd66d" stroke-opacity=".34" stroke-width="1.8"/>
|
||||
<path d="M134 158l244 196M152 363l215-225M91 260h90M331 256h92M256 90v79M256 342v82" stroke="#8cf0ff" stroke-opacity=".22" stroke-width="1.2"/>
|
||||
<path d="M174 119l32-18 38 9 11 32-23 29-39-6-25-23z" stroke="#48e6ff" stroke-opacity=".44" stroke-width="1.8"/>
|
||||
<path d="M337 339l39-11 31 19 3 37-31 23-38-11-15-31z" stroke="#48e6ff" stroke-opacity=".4" stroke-width="1.8"/>
|
||||
<path d="M360 117l29 8 14 29-16 28-31 6-23-22 2-33z" stroke="#ffd66d" stroke-opacity=".32" stroke-width="1.6"/>
|
||||
<path d="M118 313l22 8 9 23-14 21-25 2-16-19 3-25z" stroke="#ffd66d" stroke-opacity=".3" stroke-width="1.6"/>
|
||||
<path d="M106 226h54l18 30-18 30h-54l-17-30zM352 226h54l17 30-17 30h-54l-18-30z" stroke="#48e6ff" stroke-opacity=".34" stroke-width="1.4"/>
|
||||
<path d="M230 84h52M230 428h52M83 230v52M429 230v52" stroke="#8cf0ff" stroke-opacity=".5" stroke-width="6"/>
|
||||
<path d="M198 97l-8 31M318 415l8-31M415 198l-31-8M97 318l31 8" stroke="#48e6ff" stroke-opacity=".48" stroke-width="5"/>
|
||||
<path d="M256 208l42 24v48l-42 24-42-24v-48z" stroke="#48e6ff" stroke-opacity=".38" stroke-width="1.7"/>
|
||||
<path d="M256 208v96M214 232l84 48M298 232l-84 48" stroke="#ffd66d" stroke-opacity=".26" stroke-width="1.1"/>
|
||||
<path d="M60 256h24M428 256h24M256 60v24M256 428v24" stroke="#ffd66d" stroke-opacity=".5" stroke-width="2.4"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
+113
-100
@@ -1,156 +1,169 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
function readThemeCss() {
|
||||
return readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
}
|
||||
|
||||
function compact(value) {
|
||||
return value.replace(/\s+/g, "");
|
||||
}
|
||||
|
||||
describe("platform web shared theme CSS", () => {
|
||||
it("keeps production lifecycle and alert controls bounded at narrow width", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const narrow = themeCss.slice(themeCss.indexOf("@media (max-width: 640px)", themeCss.indexOf(".production-governance-panel")));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(narrow).toContain(".plugin-lifecycle-controls");
|
||||
expect(narrow).toContain("grid-template-columns: minmax(0, 1fr)");
|
||||
expect(narrow).toContain(".production-alert-actions button");
|
||||
expect(themeCss).toContain("overflow-wrap: anywhere");
|
||||
expect(css).toContain("@media(max-width:640px){.plugin-lifecycle-controls{grid-template-columns:minmax(0,1fr)}");
|
||||
expect(css).toContain(".production-alert-actions,.production-alert-actionsbutton{width:100%}");
|
||||
expect(css).toContain("overflow-wrap:anywhere");
|
||||
});
|
||||
|
||||
it("suppresses nested state frames inside shared framed surfaces", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const nestedStateReset = themeCss.slice(themeCss.indexOf("A state view inside an already framed surface"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(nestedStateReset).toContain(".state-view::after");
|
||||
expect(nestedStateReset).toContain("content: none");
|
||||
expect(nestedStateReset).toContain("background: transparent");
|
||||
expect(css).toContain(".state-view::after,:root[data-theme-palette]:where(");
|
||||
expect(css).toContain(".state-view::before{content:none;background:00;box-shadow:none;filter:none}");
|
||||
});
|
||||
|
||||
it("keeps server card runtime actions as compact overlay menus", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const popoverRule = themeCss.slice(themeCss.indexOf(".runtime-action-popover"), themeCss.indexOf("/* ---- catalog / detail lists ---- */"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(popoverRule).toContain("position: fixed");
|
||||
expect(popoverRule).toContain("z-index: 45");
|
||||
expect(popoverRule).toContain(".runtime-action-grid");
|
||||
expect(popoverRule).toContain("grid-template-columns: repeat(2, minmax(0, 1fr))");
|
||||
expect(css).toContain(".runtime-action-popover{position:fixed;z-index:45");
|
||||
expect(css).toContain(".runtime-action-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr))");
|
||||
});
|
||||
|
||||
it("keeps server card stat tiles readable over busy backgrounds", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const statRule = themeCss.slice(themeCss.indexOf(".server-card-stat {"), themeCss.indexOf(".server-card-meters"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(statRule).toContain("border: 1px solid");
|
||||
expect(statRule).toContain("--surface-solid");
|
||||
expect(statRule).toContain("font-weight: 850");
|
||||
expect(themeCss).toContain(':root[data-custom-background="true"] .server-card-stat');
|
||||
expect(css).toContain(".server-card-stat{display:grid;gap:2px;padding:8px;border:1pxsolid");
|
||||
expect(css).toContain("--surface-solid");
|
||||
expect(css).toContain("font-weight:850");
|
||||
expect(css).toContain(":root[data-custom-background=true].server-card-stat");
|
||||
});
|
||||
|
||||
it("keeps magical custom-background signal rows readable without the jelly wash", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const signalRule = themeCss.slice(
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] .signal-item'),
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] .signal-item strong')
|
||||
);
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(signalRule).toContain("rgba(58, 44, 56, 0.72)");
|
||||
expect(signalRule).toContain("backdrop-filter: blur(12px) saturate(0.92)");
|
||||
expect(signalRule).not.toContain("var(--jelly-highlight), var(--glass-wash), var(--surface-solid)");
|
||||
expect(css).toContain(":root[data-custom-background=true][data-theme-palette=magical-girl].signal-item");
|
||||
expect(css).toContain("rgba(58,44,56,.72)");
|
||||
expect(css).toContain("backdrop-filter:blur(12px)saturate(0.92)");
|
||||
expect(css).not.toContain("var(--jelly-highlight),var(--glass-wash),var(--surface-solid)");
|
||||
});
|
||||
|
||||
it("keeps shared controls off the heavy jelly and corner-sparkle button wash", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const primaryCommandRule = themeCss.slice(themeCss.indexOf(".primary-command {"), themeCss.indexOf(".action-strip .primary-command"));
|
||||
const css = readThemeCss();
|
||||
|
||||
expect(themeCss).toContain("--control-surface");
|
||||
expect(themeCss).toContain("--primary-command-surface");
|
||||
expect(themeCss).not.toContain("background: var(--jelly-highlight), var(--glass-wash), var(--surface-solid)");
|
||||
expect(themeCss).not.toContain("linear-gradient(145deg, rgba(255, 255, 255, 0.72), transparent 36%)");
|
||||
expect(primaryCommandRule).not.toContain("background-position: right 8px top 4px, center, center");
|
||||
expect(css).toContain("--control-surface");
|
||||
expect(css).toContain("--primary-command-surface");
|
||||
expect(css).not.toContain("background: var(--jelly-highlight), var(--glass-wash), var(--surface-solid)");
|
||||
expect(css).not.toContain("linear-gradient(145deg, rgba(255, 255, 255, 0.72), transparent 36%)");
|
||||
});
|
||||
|
||||
it("uses shared orbital construct backdrop and button ring effects", () => {
|
||||
const themeCss = readThemeCss();
|
||||
const constructRule = themeCss.slice(themeCss.indexOf(".global-orbital-construct"), themeCss.indexOf(".mobile-sidebar-backdrop"));
|
||||
|
||||
expect(constructRule).toContain("clip-path:circle(49% at 50% 50%)");
|
||||
expect(constructRule).toContain("background:var(--orbital-construct-image) center/contain no-repeat transparent");
|
||||
expect(constructRule).toContain("filter:var(--orbital-construct-filter)");
|
||||
expect(constructRule).toContain(".global-orbital-construct::before,.global-orbital-construct::after{content:none}");
|
||||
expect(constructRule).toContain(":root[data-theme-palette=mecha-black] .global-orbital-construct{display:block;opacity:.22}");
|
||||
expect(constructRule).toContain(":root[data-theme-palette=magical-girl] .global-orbital-construct{display:block;opacity:.24}");
|
||||
expect(constructRule).toContain(".global-particle-lightfall");
|
||||
expect(constructRule).toContain(".global-particle-side-ray");
|
||||
expect(themeCss).not.toContain("global-particle-frame");
|
||||
expect(themeCss).not.toContain("global-particle-orbit");
|
||||
expect(themeCss).not.toContain("particle-orbit-drift");
|
||||
expect(constructRule).toContain("orbital-construct-drift");
|
||||
expect(constructRule).toContain("prefers-reduced-motion");
|
||||
expect(constructRule).toContain("var(--command-ring-sheen");
|
||||
expect(constructRule).toContain(".app-nav-group-button::after");
|
||||
expect(constructRule).toContain("magic-side-ray-pulse-right");
|
||||
expect(constructRule).toContain("transform:translateY(1px)");
|
||||
expect(themeCss).toContain(":root[data-custom-background=true] .global-particle-layer{opacity:.42}");
|
||||
expect(themeCss).toContain(":root[data-custom-background=true][data-theme-palette=magical-girl] .global-particle-layer{opacity:.54}");
|
||||
expect(themeCss).not.toContain(":root[data-custom-background=true] .global-particle-layer{opacity:.03}");
|
||||
});
|
||||
|
||||
it("keeps magical-girl custom-background panel overlays restrained", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const customMagicalRule = themeCss.slice(
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] {'),
|
||||
themeCss.indexOf(':root[data-custom-background="true"][data-theme-palette="magical-girl"] .app-sidebar')
|
||||
);
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(customMagicalRule).toContain("--frame-accessory-opacity: 0.22");
|
||||
expect(customMagicalRule).toContain("rgba(22, 22, 29, 0.64)");
|
||||
expect(customMagicalRule).not.toContain("255, 119, 200, 0.78");
|
||||
expect(css).toContain(":root[data-custom-background=true][data-theme-palette=magical-girl]{--frame-accessory-opacity:0.22");
|
||||
expect(css).toContain("--surface:rgba(22,22,29,0.42)");
|
||||
expect(css).toContain("linear-gradient(180deg,rgba(24,24,31,.22),rgba(18,19,26,.18))");
|
||||
expect(css).not.toContain("255,119,200,0.78");
|
||||
});
|
||||
|
||||
it("keeps catalog and console panels translucent without fading content", () => {
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(css).toContain(".catalog-card,.console-panel{border:1pxsolidvar(--line);border-radius:8px;background:var(--sugar-dust),linear-gradient(135deg,color-mix(insrgb,var(--surface)22%,transparent),color-mix(insrgb,var(--surface-raised)14%,transparent))");
|
||||
expect(css).toContain("-webkit-backdrop-filter:blur(10px)saturate(.9);backdrop-filter:blur(10px)saturate(.9)");
|
||||
expect(css).toContain(":root[data-custom-background=true].catalog-card");
|
||||
expect(css).toContain("linear-gradient(180deg,rgba(5,10,16,.42),rgba(5,10,16,.34))");
|
||||
expect(css).not.toContain(".catalog-card,.console-panel{opacity:");
|
||||
expect(css).not.toContain(".catalog-card,.console-panel{border:1pxsolidvar(--line);border-radius:8px;background:var(--panel-material);backdrop-filter:blur(22px)saturate(1.28)");
|
||||
});
|
||||
|
||||
it("styles runtime task progress as a dialog with staged status", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const progressRule = themeCss.slice(themeCss.indexOf(".runtime-task-backdrop"), themeCss.indexOf("/* ---- narrow screens ---- */"));
|
||||
const css = readThemeCss();
|
||||
|
||||
expect(progressRule).toContain(".runtime-task-panel");
|
||||
expect(progressRule).toContain(".runtime-task-meter-track");
|
||||
expect(progressRule).toContain(".runtime-task-stages");
|
||||
expect(progressRule).toContain(".runtime-task-log");
|
||||
expect(progressRule).toContain("prefers-reduced-motion");
|
||||
expect(css).toContain(".runtime-task-panel");
|
||||
expect(css).toContain(".runtime-task-meter-track");
|
||||
expect(css).toContain(".runtime-task-stages");
|
||||
expect(css).toContain(".runtime-task-log");
|
||||
expect(css).toContain("prefers-reduced-motion");
|
||||
});
|
||||
|
||||
it("styles operations surfaces with shared theme materials and narrow-screen collapse", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const operationsRule = themeCss.slice(themeCss.indexOf("/* ---- operations console enrichment ---- */"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(operationsRule).toContain("var(--panel-material)");
|
||||
expect(operationsRule).toContain(".operations-tray-panel");
|
||||
expect(operationsRule).toContain("@media (max-width: 760px)");
|
||||
expect(operationsRule).toContain("grid-template-columns: 1fr");
|
||||
expect(operationsRule).toContain("prefers-reduced-motion");
|
||||
expect(operationsRule).toContain(".operations-job-row-failed");
|
||||
expect(css).toContain(".operations-tray{position:relative;z-index:2");
|
||||
expect(css).toContain("background:var(--panel-material),var(--surface)");
|
||||
expect(css).toContain(".operations-tray-panel{position:absolute");
|
||||
expect(css).toContain("@media(max-width:760px){.operations-command-grid{grid-template-columns:1fr}");
|
||||
expect(css).toContain(".operations-job-row-failed");
|
||||
expect(css).toContain("prefers-reduced-motion");
|
||||
});
|
||||
|
||||
it("keeps mobile navigation off-canvas with readable vertical labels", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const mobileRule = themeCss.slice(themeCss.indexOf("/* ---- narrow screens ---- */"), themeCss.indexOf("/* A state view inside an already framed surface"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(mobileRule).toContain(".mobile-sidebar-handle");
|
||||
expect(mobileRule).toContain("position: fixed");
|
||||
expect(mobileRule).toContain("padding: 14px 14px 14px 52px");
|
||||
expect(mobileRule).toContain("transform: translateX(calc(-100% - 18px))");
|
||||
expect(mobileRule).toContain(".app-sidebar-mobile-open");
|
||||
expect(mobileRule).toContain("grid-template-columns: 40px minmax(0, 1fr)");
|
||||
expect(mobileRule).toContain(".app-nav-copy strong");
|
||||
expect(mobileRule).toContain("display: block");
|
||||
expect(css).toContain(".mobile-sidebar-handle{position:fixed");
|
||||
expect(css).toContain(".app-sidebar{position:fixed;inset:0auto00");
|
||||
expect(css).toContain("transform:translateX(calc(-100%-18px))");
|
||||
expect(css).toContain(".app-sidebar-mobile-open");
|
||||
expect(css).toContain("grid-template-columns:40pxminmax(0,1fr)");
|
||||
expect(css).toContain(".app-nav-copystrong{display:block");
|
||||
});
|
||||
|
||||
it("keeps desktop sidebar controls pinned inside the viewport", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const shellRule = themeCss.slice(themeCss.indexOf(".app-shell {"), themeCss.indexOf(".workspace-background-layer"));
|
||||
const sidebarRule = themeCss.slice(themeCss.indexOf(".app-sidebar {"), themeCss.indexOf(".app-brand {"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(shellRule).toContain("min-height: 100dvh");
|
||||
expect(sidebarRule).toContain("position: sticky");
|
||||
expect(sidebarRule).toContain("height: 100dvh");
|
||||
expect(sidebarRule).toContain("overflow-y: auto");
|
||||
expect(sidebarRule).toContain("overscroll-behavior: contain");
|
||||
expect(css).toContain(".app-shell{min-height:100dvh");
|
||||
expect(css).toContain(".app-sidebar{position:sticky");
|
||||
expect(css).toContain("height:100dvh");
|
||||
expect(css).toContain("overflow-y:auto");
|
||||
expect(css).toContain("overscroll-behavior:contain");
|
||||
});
|
||||
|
||||
it("keeps dialogs and drawers internally scrollable on short viewports", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const dialogRule = themeCss.slice(themeCss.indexOf("/* ---- drawer ---- */"), themeCss.indexOf(".plugin-detail-actions"));
|
||||
const confirmStart = themeCss.indexOf("/* ---- confirm dialog ---- */");
|
||||
const confirmRule = themeCss.slice(confirmStart, themeCss.indexOf(".confirm-actions {", confirmStart));
|
||||
const mobileRule = themeCss.slice(themeCss.indexOf("/* ---- narrow screens ---- */"), themeCss.indexOf("/* A state view inside an already framed surface"));
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(dialogRule).toContain(".dialog-panel-header");
|
||||
expect(dialogRule).toContain(".drawer-panel > .panel-header");
|
||||
expect(dialogRule).toContain("position: sticky");
|
||||
expect(dialogRule).toContain("overflow-y: auto");
|
||||
expect(dialogRule).toContain("overscroll-behavior: contain");
|
||||
expect(confirmRule).toContain("max-height: calc(100dvh - 32px)");
|
||||
expect(confirmRule).toContain("overflow-y: auto");
|
||||
expect(mobileRule).toContain("max-height: calc(100dvh - 20px)");
|
||||
expect(css).toContain(".dialog-panel-header,.drawer-panel>.panel-header{position:sticky");
|
||||
expect(css).toContain("overflow-y:auto");
|
||||
expect(css).toContain("overscroll-behavior:contain");
|
||||
expect(css).toContain("max-height:calc(100dvh-32px)");
|
||||
expect(css).toContain("max-height:calc(100dvh-20px)");
|
||||
});
|
||||
|
||||
it("keeps magical card decoration from becoming square background patches", () => {
|
||||
const themeCss = readFileSync(new URL("./base.css", import.meta.url), "utf8");
|
||||
const magicalSurfaceRule = themeCss.slice(
|
||||
themeCss.indexOf(':root[data-theme-palette="magical-girl"] .metric-card'),
|
||||
themeCss.indexOf(':root[data-theme-palette="magical-girl"] :where(')
|
||||
);
|
||||
const css = compact(readThemeCss());
|
||||
|
||||
expect(magicalSurfaceRule).toContain("background: var(--panel-material)");
|
||||
expect(magicalSurfaceRule).not.toContain("background-size: 56px 56px");
|
||||
expect(magicalSurfaceRule).not.toContain("background-image: var(--corner-sparkle), var(--panel-material)");
|
||||
expect(css).toContain(":root[data-theme-palette=magical-girl].catalog-card");
|
||||
expect(css).toContain("background:var(--sugar-dust),linear-gradient(180deg,color-mix(insrgb,var(--surface)16%,transparent),color-mix(insrgb,var(--surface-raised)10%,transparent));overflow:visible");
|
||||
expect(css).not.toContain("background-size:56px56px");
|
||||
expect(css).not.toContain("background-image:var(--corner-sparkle),var(--panel-material)");
|
||||
expect(css).not.toContain(".global-magic-ring");
|
||||
});
|
||||
});
|
||||
|
||||
+68
-27
@@ -8,42 +8,82 @@ button{font:inherit}
|
||||
:root[data-custom-background=true] .workspace-background-layer::after{content:"";position:absolute;inset:0;background:linear-gradient(90deg,rgba(4,7,11,.28) 0,rgba(4,7,11,.1) 216px,transparent 42%),linear-gradient(180deg,rgba(4,7,11,.02),rgba(4,7,11,.1));pointer-events:none}
|
||||
:root[data-custom-background=true] .workspace-background-layer{filter:saturate(.92) contrast(.96) brightness(.94);opacity:1}
|
||||
.global-particle-layer{position:fixed;inset:0;z-index:0;width:100%;height:100%;pointer-events:none;opacity:var(--ultimate-effect-alpha);mix-blend-mode:screen;overflow:hidden}
|
||||
:root[data-custom-background=true] .global-particle-layer{opacity:.08}
|
||||
:root[data-custom-background=true] .global-particle-layer{opacity:.42}
|
||||
.global-particle-layer span{position:absolute;display:block;pointer-events:none}
|
||||
.global-particle-orbit{right:9vw;bottom:6vh;width:min(36vw,440px);aspect-ratio:1;border:2px solid color-mix(in srgb,var(--accent) 42%,transparent);border-radius:50%;box-shadow:0 0 34px color-mix(in srgb,var(--accent) 18%,transparent),inset 0 0 42px color-mix(in srgb,var(--accent) 12%,transparent);opacity:.62;transform:rotate(12deg)}
|
||||
.global-particle-orbit::after,.global-particle-orbit::before{content:"";position:absolute;inset:18%;border:1px solid color-mix(in srgb,var(--gold) 34%,transparent);border-radius:inherit}
|
||||
.global-particle-orbit::after{inset:34%;border-color:color-mix(in srgb,var(--accent) 28%,transparent)}
|
||||
.global-particle-orbit-secondary{right:23vw;top:9vh;bottom:auto;width:min(18vw,230px);opacity:.36;transform:rotate(-18deg)}
|
||||
.global-particle-sweep{top:-12vh;left:63vw;width:28px;height:118vh;background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--accent) 42%,transparent),transparent);opacity:.42;transform:rotate(24deg);filter:blur(1px)}
|
||||
.global-particle-ribbon{left:-12vw;bottom:24vh;width:128vw;height:120px;background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--pink) 24%,transparent),color-mix(in srgb,var(--gold) 16%,transparent),transparent);opacity:.28;transform:rotate(-11deg);filter:blur(18px)}
|
||||
:root[data-theme-palette=magical-girl] .global-particle-ribbon{display:none}
|
||||
.global-particle-frame{width:120px;height:120px;border:2px solid color-mix(in srgb,var(--gold) 44%,transparent);opacity:.46}
|
||||
.global-particle-frame-tl{top:18px;left:20px;border-right:0;border-bottom:0}
|
||||
.global-particle-frame-br{right:22px;bottom:24px;border-left:0;border-top:0}
|
||||
.global-particle-glint{--particle-index:0;--particle-x:12%;--particle-y:16%;left:var(--particle-x);top:var(--particle-y);width:9px;height:9px;opacity:.46;transform:rotate(45deg)}
|
||||
.global-particle-glint::after,.global-particle-glint::before{content:"";position:absolute;inset:0;background:color-mix(in srgb,var(--sparkle) 76%,transparent);border-radius:999px}
|
||||
.global-particle-glint::after{transform:rotate(90deg)}
|
||||
:root[data-theme-palette=mecha-black] .global-particle-ribbon{background:repeating-linear-gradient(90deg,transparent 0 80px,color-mix(in srgb,var(--gold) 24%,transparent) 81px 86px,transparent 87px 150px);opacity:.32;filter:blur(2px)}
|
||||
:root[data-theme-palette=mecha-black] .global-particle-glint{width:6px;height:18px;border:1px solid color-mix(in srgb,var(--accent) 54%,transparent);background:color-mix(in srgb,var(--accent) 18%,transparent)}
|
||||
:root[data-theme-palette=mecha-black] .global-particle-glint::after,:root[data-theme-palette=mecha-black] .global-particle-glint::before{display:none}
|
||||
:root[data-theme-palette=magical-girl] .global-particle-orbit{border-color:color-mix(in srgb,var(--sparkle) 62%,transparent);box-shadow:0 0 44px color-mix(in srgb,var(--pink) 28%,transparent),inset 0 0 34px color-mix(in srgb,var(--gold) 14%,transparent)}
|
||||
:root[data-theme-palette=magical-girl] .global-particle-frame{border-radius:22px;box-shadow:0 0 22px color-mix(in srgb,var(--gold) 22%,transparent)}
|
||||
@media (prefers-reduced-motion:no-preference){.global-particle-orbit{animation:particle-orbit-drift 22s linear infinite}
|
||||
.global-particle-orbit-secondary{animation-duration:30s;animation-direction:reverse}
|
||||
.global-particle-sweep{animation:particle-sweep 12s ease-in-out infinite}
|
||||
@media (prefers-reduced-motion:no-preference){.global-particle-sweep{animation:particle-sweep 12s ease-in-out infinite}
|
||||
.global-particle-glint{animation:particle-glint 4.8s ease-in-out infinite;animation-delay:calc(var(--particle-index) * -.42s)}
|
||||
}
|
||||
@media (prefers-reduced-motion:reduce){.global-particle-layer *{animation:none!important}
|
||||
}
|
||||
@keyframes particle-orbit-drift{from{transform:rotate(12deg)}
|
||||
to{transform:rotate(372deg)}
|
||||
}
|
||||
@keyframes particle-sweep{0%,100%{transform:translateX(-18vw) rotate(24deg);opacity:.18}
|
||||
48%{transform:translateX(18vw) rotate(24deg);opacity:.46}
|
||||
}
|
||||
@keyframes particle-glint{0%,100%{opacity:.18;transform:scale(.72) rotate(45deg)}
|
||||
50%{opacity:.68;transform:scale(1.16) rotate(45deg)}
|
||||
}
|
||||
.global-orbital-construct{--construct-index:0;--construct-x:78%;--construct-y:78%;--construct-size:min(42vw,520px);--construct-drift:0deg;left:var(--construct-x);top:var(--construct-y);width:var(--construct-size);aspect-ratio:1;border-radius:50%;clip-path:circle(49% at 50% 50%);opacity:0;overflow:hidden;transform:translate(-50%,-50%) rotate(var(--construct-drift));background:var(--orbital-construct-image) center/contain no-repeat transparent;filter:var(--orbital-construct-filter);mix-blend-mode:screen}
|
||||
.global-orbital-construct::before,.global-orbital-construct::after{content:none}
|
||||
.global-particle-lightfall{top:-28vh;width:clamp(54px,8vw,112px);height:152vh;background:var(--magic-lightfall,linear-gradient(180deg,transparent,rgba(154,247,255,.34),transparent));opacity:.32;filter:blur(10px);transform:translate3d(0,-8vh,0) rotate(8deg);mix-blend-mode:screen}
|
||||
.global-particle-lightfall-a{left:18vw}
|
||||
.global-particle-lightfall-b{left:54vw;width:clamp(72px,10vw,136px);opacity:.24;transform:translate3d(0,-18vh,0) rotate(11deg)}
|
||||
.global-particle-lightfall-c{left:82vw;width:clamp(42px,6vw,84px);opacity:.2;transform:translate3d(0,-4vh,0) rotate(6deg)}
|
||||
.global-particle-side-ray{display:none;top:9vh;width:min(42vw,520px);height:74vh;background:var(--magic-side-rays,linear-gradient(90deg,color-mix(in srgb,var(--accent) 24%,transparent),transparent));opacity:.34;filter:blur(11px);transform:translate3d(0,0,0) skewY(-12deg);mix-blend-mode:screen}
|
||||
.global-particle-side-ray-left{left:-11vw}
|
||||
.global-particle-side-ray-right{right:-11vw;transform:scaleX(-1) skewY(-12deg)}
|
||||
:root[data-theme-palette=mecha-black] .global-orbital-construct{display:block;opacity:.22}
|
||||
:root[data-theme-palette=mecha-black] .global-particle-side-ray{display:none}
|
||||
:root[data-theme-palette=magical-girl] .global-orbital-construct{display:block;opacity:.24}
|
||||
:root[data-theme-palette=magical-girl] .global-particle-lightfall{opacity:.14;filter:blur(15px)}
|
||||
:root[data-theme-palette=magical-girl] .global-particle-side-ray{display:block}
|
||||
:root[data-custom-background=true] .global-orbital-construct{opacity:.2}
|
||||
:root[data-custom-background=true][data-theme-palette=mecha-black] .global-orbital-construct{display:block;opacity:.16}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .global-orbital-construct{opacity:.18}
|
||||
:root[data-custom-background=true] .global-particle-lightfall{opacity:.2}
|
||||
:root[data-custom-background=true] .global-particle-side-ray{opacity:.28}
|
||||
@media (prefers-reduced-motion:no-preference){.global-orbital-construct{animation:orbital-construct-drift 26s linear infinite;animation-delay:calc(var(--construct-index) * -5s)}
|
||||
.global-particle-lightfall{animation:magic-lightfall-fall 13s ease-in-out infinite}
|
||||
.global-particle-lightfall-b{animation-duration:17s;animation-delay:-7s}
|
||||
.global-particle-lightfall-c{animation-duration:19s;animation-delay:-3s}
|
||||
.global-particle-side-ray{animation:magic-side-ray-pulse 7s ease-in-out infinite}
|
||||
.global-particle-side-ray-right{animation-name:magic-side-ray-pulse-right;animation-delay:-3.5s}
|
||||
}
|
||||
@keyframes orbital-construct-drift{from{transform:translate(-50%,-50%) rotate(var(--construct-drift))}
|
||||
to{transform:translate(-50%,-50%) rotate(calc(var(--construct-drift) + 360deg))}
|
||||
}
|
||||
@keyframes magic-lightfall-fall{0%,100%{opacity:.14;transform:translate3d(0,-24vh,0) rotate(8deg)}
|
||||
45%{opacity:.42;transform:translate3d(0,8vh,0) rotate(8deg)}
|
||||
}
|
||||
@keyframes magic-side-ray-pulse{0%,100%{opacity:.16;transform:translate3d(-4vw,0,0) skewY(-12deg)}
|
||||
50%{opacity:.44;transform:translate3d(2vw,0,0) skewY(-12deg)}
|
||||
}
|
||||
@keyframes magic-side-ray-pulse-right{0%,100%{opacity:.16;transform:translate3d(4vw,0,0) scaleX(-1) skewY(-12deg)}
|
||||
50%{opacity:.44;transform:translate3d(-2vw,0,0) scaleX(-1) skewY(-12deg)}
|
||||
}
|
||||
.action-strip button,.app-account-button,.app-menu-toggle,.auth-mode-tabs button,.confirm-actions button,.icon-command,.primary-command,.profile-save-button,.row-actions button,.runtime-action-item,.section-tab,.segmented-button,.state-action,.table-link-button,.theme-upload{position:relative;isolation:isolate;overflow:hidden;transition:border-color 140ms ease,box-shadow 160ms ease,color 140ms ease,filter 160ms ease,transform 120ms ease}
|
||||
.action-strip button::before,.app-account-button::before,.app-menu-toggle::before,.auth-mode-tabs button::before,.confirm-actions button::before,.icon-command::before,.primary-command::before,.profile-save-button::before,.row-actions button::before,.runtime-action-item::before,.section-tab::before,.segmented-button::before,.state-action::before,.table-link-button::before,.theme-upload::before{content:"";position:absolute;inset:-64%;z-index:0;border-radius:inherit;background:var(--command-ring-sheen,radial-gradient(circle at 50% 50%,color-mix(in srgb,var(--accent) 26%,transparent),transparent 64%)),conic-gradient(from 0deg,transparent 0 18%,color-mix(in srgb,var(--accent) 34%,transparent) 20% 22%,transparent 24% 54%,color-mix(in srgb,var(--gold) 26%,transparent) 56% 58%,transparent 60% 100%);opacity:0;pointer-events:none;transform:scale(.68) rotate(-14deg);transition:opacity 150ms ease,transform 420ms cubic-bezier(.16,1,.3,1);mix-blend-mode:screen}
|
||||
.action-strip button>* ,.app-account-button>* ,.app-menu-toggle>* ,.auth-mode-tabs button>* ,.confirm-actions button>* ,.icon-command>* ,.primary-command>* ,.profile-save-button>* ,.row-actions button>* ,.runtime-action-item>* ,.section-tab>* ,.segmented-button>* ,.state-action>* ,.table-link-button>* ,.theme-upload>*{position:relative;z-index:1}
|
||||
.action-strip button:focus-visible::before,.action-strip button:hover::before,.app-account-button:focus-visible::before,.app-account-button:hover::before,.app-menu-toggle:focus-visible::before,.app-menu-toggle:hover::before,.auth-mode-tabs button:focus-visible::before,.auth-mode-tabs button:hover::before,.confirm-actions button:focus-visible::before,.confirm-actions button:hover::before,.icon-command:focus-visible::before,.icon-command:hover::before,.primary-command:focus-visible::before,.primary-command:hover::before,.profile-save-button:focus-visible::before,.profile-save-button:hover::before,.row-actions button:focus-visible::before,.row-actions button:hover::before,.runtime-action-item:focus-visible::before,.runtime-action-item:hover::before,.section-tab:focus-visible::before,.section-tab:hover::before,.segmented-button:focus-visible::before,.segmented-button:hover::before,.state-action:focus-visible::before,.state-action:hover::before,.table-link-button:focus-visible::before,.table-link-button:hover::before,.theme-upload:focus-visible::before,.theme-upload:hover::before{opacity:.72;transform:scale(1) rotate(18deg)}
|
||||
.action-strip button:active,.app-account-button:active,.app-menu-toggle:active,.auth-mode-tabs button:active,.confirm-actions button:active,.icon-command:active,.primary-command:active,.profile-save-button:active,.row-actions button:active,.runtime-action-item:active,.section-tab:active,.segmented-button:active,.state-action:active,.table-link-button:active,.theme-upload:active{transform:translateY(1px)}
|
||||
.action-strip button:disabled::before,.icon-command:disabled::before,.primary-command:disabled::before,.row-actions button:disabled::before,.theme-upload:disabled::before{content:none}
|
||||
.app-nav-group-button{overflow:hidden;isolation:isolate;transition:border-color 140ms ease,box-shadow 160ms ease,color 140ms ease,transform 120ms ease}
|
||||
.app-nav-group-button::after{content:"";position:absolute;inset:-70%;z-index:0;border-radius:inherit;background:var(--command-ring-sheen,radial-gradient(circle,color-mix(in srgb,var(--accent) 24%,transparent),transparent 66%));opacity:0;transform:scale(.64) rotate(-16deg);transition:opacity 150ms ease,transform 420ms cubic-bezier(.16,1,.3,1);pointer-events:none;mix-blend-mode:screen}
|
||||
.app-nav-group-button:focus-visible::after,.app-nav-group-button:hover::after,.app-nav-item-active::after{opacity:.62;transform:scale(1) rotate(14deg)}
|
||||
.app-nav-group-button:active{transform:translateY(1px)}
|
||||
.app-nav-copy{position:relative;z-index:1}
|
||||
.app-nav-group-button::before{z-index:1}
|
||||
.mobile-sidebar-handle::after{background:var(--command-ring-sheen),var(--frame-accessory-top) center top/28px 10px no-repeat;background-repeat:no-repeat;opacity:.78}
|
||||
@media (prefers-reduced-motion:reduce){.action-strip button,.app-account-button,.app-menu-toggle,.app-nav-group-button,.auth-mode-tabs button,.confirm-actions button,.icon-command,.primary-command,.profile-save-button,.row-actions button,.runtime-action-item,.section-tab,.segmented-button,.state-action,.table-link-button,.theme-upload{transition:none}
|
||||
.action-strip button::before,.app-account-button::before,.app-menu-toggle::before,.app-nav-group-button::after,.auth-mode-tabs button::before,.confirm-actions button::before,.icon-command::before,.primary-command::before,.profile-save-button::before,.row-actions button::before,.runtime-action-item::before,.section-tab::before,.segmented-button::before,.state-action::before,.table-link-button::before,.theme-upload::before{transition:none;transform:none}
|
||||
}
|
||||
.mobile-sidebar-backdrop,.mobile-sidebar-close,.mobile-sidebar-handle{display:none}
|
||||
@keyframes mobile-sidebar-beacon{0%,100%{opacity:.48;transform:scaleY(.72)}
|
||||
50%{opacity:1;transform:scaleY(1)}
|
||||
@@ -193,7 +233,8 @@ to{transform:rotate(372deg)}
|
||||
.summary-tone-warning{border-left-color:var(--gold)}
|
||||
.summary-tone-neutral{border-left-color:var(--accent)}
|
||||
.metric-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}
|
||||
.metric-card,.overview-card{min-height:104px;display:grid;align-content:space-between;gap:12px;padding:16px;border:1px solid var(--line);border-radius:8px;background:var(--panel-material);backdrop-filter:blur(22px) saturate(1.28);box-shadow:var(--panel-shadow);position:relative;isolation:isolate;overflow:hidden}
|
||||
.catalog-card,.console-panel{border:1px solid var(--line);border-radius:8px;background:var(--sugar-dust),linear-gradient(135deg,color-mix(in srgb,var(--surface) 22%,transparent),color-mix(in srgb,var(--surface-raised) 14%,transparent));-webkit-backdrop-filter:blur(10px) saturate(.9);backdrop-filter:blur(10px) saturate(.9);box-shadow:var(--panel-shadow);padding:16px;position:relative;isolation:isolate;overflow:hidden}
|
||||
.metric-card,.overview-card{min-height:104px;display:grid;align-content:space-between;gap:12px;padding:16px;border:1px solid var(--line);border-radius:8px;background:var(--sugar-dust),linear-gradient(135deg,color-mix(in srgb,var(--surface) 22%,transparent),color-mix(in srgb,var(--surface-raised) 14%,transparent));-webkit-backdrop-filter:blur(10px) saturate(.9);backdrop-filter:blur(10px) saturate(.9);box-shadow:var(--panel-shadow);position:relative;isolation:isolate;overflow:hidden}
|
||||
.catalog-card::before,.console-panel::before,.metric-card::before,.overview-card::before,.server-card::before,.server-detail-header::before{content:"";position:absolute;inset:0;border-radius:inherit;padding:1px;background:linear-gradient(135deg,rgba(255,255,255,.9),color-mix(in srgb,var(--accent) 35%,transparent),color-mix(in srgb,var(--pink) 24%,transparent),rgba(255,255,255,.46));mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);mask-composite:exclude;opacity:.86;z-index:2;pointer-events:none}
|
||||
.catalog-card::after,.confirm-panel::after,.console-panel::after,.drawer-panel::after,.metric-card::after,.overview-card::after,.server-card::after,.server-detail-header::after{content:"";position:absolute;inset:0;background:var(--frame-accessory-top),var(--frame-accessory-bottom),var(--shine-sweep),var(--jelly-highlight);background-size:var(--frame-accessory-size),34px 34px,auto;background-repeat:no-repeat,no-repeat,no-repeat,no-repeat;background-position:right 6px top 5px,left 6px bottom 5px,right 10px top 8px,center;opacity:var(--frame-accessory-opacity);z-index:0;pointer-events:none}
|
||||
.metric-label{color:var(--ink-faint);font-size:13px}
|
||||
@@ -207,12 +248,11 @@ to{transform:rotate(372deg)}
|
||||
.console-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:12px}
|
||||
.overview-card{min-height:132px;gap:10px}
|
||||
.overview-card p{margin:0;color:var(--ink-faint);font-size:13px}
|
||||
.catalog-card,.console-panel{border:1px solid var(--line);border-radius:8px;background:var(--panel-material);backdrop-filter:blur(22px) saturate(1.28);box-shadow:var(--panel-shadow);padding:16px;position:relative;isolation:isolate;overflow:hidden}
|
||||
:where(
|
||||
.metric-card,.overview-card,.console-panel,.catalog-card,.server-card,.server-detail-header,.resource-table-wrap,.provider-table-wrap,.server-table-wrap,.plugin-group,.operation-item,.confirm-panel,.drawer-panel
|
||||
)>*{position:relative;z-index:1}
|
||||
:root[data-theme-palette=mecha-black] .catalog-card,:root[data-theme-palette=mecha-black] .confirm-panel,:root[data-theme-palette=mecha-black] .console-panel,:root[data-theme-palette=mecha-black] .drawer-panel,:root[data-theme-palette=mecha-black] .metric-card,:root[data-theme-palette=mecha-black] .operation-item,:root[data-theme-palette=mecha-black] .overview-card,:root[data-theme-palette=mecha-black] .plugin-group,:root[data-theme-palette=mecha-black] .server-card{clip-path:polygon(0 0,calc(100% - 12px) 0,100% 12px,100% 100%,12px 100%,0 calc(100% - 12px))}
|
||||
:root[data-theme-palette=magical-girl] .catalog-card,:root[data-theme-palette=magical-girl] .confirm-panel,:root[data-theme-palette=magical-girl] .console-panel,:root[data-theme-palette=magical-girl] .drawer-panel,:root[data-theme-palette=magical-girl] .metric-card,:root[data-theme-palette=magical-girl] .operation-item,:root[data-theme-palette=magical-girl] .overview-card,:root[data-theme-palette=magical-girl] .plugin-group,:root[data-theme-palette=magical-girl] .provider-table-wrap,:root[data-theme-palette=magical-girl] .resource-table-wrap,:root[data-theme-palette=magical-girl] .server-card,:root[data-theme-palette=magical-girl] .server-detail-header,:root[data-theme-palette=magical-girl] .server-table-wrap{background:var(--panel-material);overflow:visible}
|
||||
:root[data-theme-palette=magical-girl] .catalog-card,:root[data-theme-palette=magical-girl] .confirm-panel,:root[data-theme-palette=magical-girl] .console-panel,:root[data-theme-palette=magical-girl] .drawer-panel,:root[data-theme-palette=magical-girl] .metric-card,:root[data-theme-palette=magical-girl] .operation-item,:root[data-theme-palette=magical-girl] .overview-card,:root[data-theme-palette=magical-girl] .plugin-group,:root[data-theme-palette=magical-girl] .provider-table-wrap,:root[data-theme-palette=magical-girl] .resource-table-wrap,:root[data-theme-palette=magical-girl] .server-card,:root[data-theme-palette=magical-girl] .server-detail-header,:root[data-theme-palette=magical-girl] .server-table-wrap{background:var(--sugar-dust),linear-gradient(180deg,color-mix(in srgb,var(--surface) 16%,transparent),color-mix(in srgb,var(--surface-raised) 10%,transparent));overflow:visible}
|
||||
:root[data-theme-palette=magical-girl] :where(
|
||||
.metric-card,.overview-card,.console-panel,.catalog-card,.server-card,.server-detail-header,.resource-table-wrap,.provider-table-wrap,.server-table-wrap,.drawer-panel,.confirm-panel,.plugin-group,.operation-item,.server-toolbar,.state-view
|
||||
){--frame-accessory-image:var(--frame-accessory-heart)}
|
||||
@@ -240,8 +280,8 @@ to{transform:rotate(372deg)}
|
||||
:root[data-theme-palette=magical-girl] .catalog-card::after,:root[data-theme-palette=magical-girl] .confirm-panel::after,:root[data-theme-palette=magical-girl] .console-panel::after,:root[data-theme-palette=magical-girl] .drawer-panel::after,:root[data-theme-palette=magical-girl] .metric-card::after,:root[data-theme-palette=magical-girl] .overview-card::after,:root[data-theme-palette=magical-girl] .server-card::after,:root[data-theme-palette=magical-girl] .server-detail-header::after{inset:-14px;background-image:var(--frame-accessory-image);background-size:var(--frame-accessory-size);background-repeat:no-repeat;background-position:right -4px top -6px;opacity:var(--frame-accessory-opacity);filter:drop-shadow(0 0 4px rgba(255, 119, 200, .58)) drop-shadow(0 0 2px rgba(255, 221, 117, .42))}
|
||||
:root[data-theme-palette=mecha-black] .catalog-card::before,:root[data-theme-palette=mecha-black] .console-panel::before,:root[data-theme-palette=mecha-black] .metric-card::before,:root[data-theme-palette=mecha-black] .overview-card::before,:root[data-theme-palette=mecha-black] .server-card::before,:root[data-theme-palette=mecha-black] .server-detail-header::before{background:linear-gradient(135deg,rgba(72,230,255,.72),rgba(255,184,77,.42),rgba(72,230,255,.18));opacity:.76}
|
||||
:root[data-custom-background=true] .app-sidebar{background:radial-gradient(circle at 92% 3%,rgba(255,255,255,.18) 0 2px,transparent 3px),linear-gradient(180deg,rgba(8,10,16,.42),rgba(8,10,16,.3))}
|
||||
:root[data-custom-background=true]{--frame-accessory-opacity:0.04;--surface:rgba(5, 10, 16, 0.76);--surface-solid:rgba(5, 10, 16, 0.92);--surface-raised:rgba(7, 13, 20, 0.82);--glass-tint:transparent;--glass-wash:linear-gradient(145deg, rgba(255, 255, 255, 0.08), rgba(5, 10, 16, 0.76) 44%, rgba(255, 255, 255, 0.03));--panel-material:linear-gradient(145deg, rgba(255, 255, 255, 0.075), rgba(5, 10, 16, 0.86) 48%, rgba(5, 10, 16, 0.76));--panel-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.13),inset 0 0 0 1px rgba(255, 255, 255, 0.045),0 18px 38px rgba(0, 0, 0, 0.32)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl]{--frame-accessory-opacity:0.22;--surface:rgba(22, 22, 29, 0.64);--surface-solid:rgba(18, 19, 26, 0.88);--surface-raised:rgba(25, 24, 32, 0.72);--line:rgba(238, 215, 236, 0.34);--line-strong:rgba(250, 240, 249, 0.5);--glass-wash:linear-gradient(145deg, rgba(255, 246, 253, 0.08), rgba(22, 22, 29, 0.66) 48%, rgba(255, 221, 117, 0.025));--panel-material:linear-gradient(145deg, rgba(255, 246, 253, 0.08), rgba(22, 22, 29, 0.72) 44%, rgba(18, 19, 26, 0.64));--panel-shadow:inset 0 1px 0 rgba(255, 246, 253, 0.18),inset 0 0 0 1px rgba(255, 185, 226, 0.06),0 16px 34px rgba(12, 12, 18, 0.3)}
|
||||
:root[data-custom-background=true]{--frame-accessory-opacity:0.04;--surface:rgba(5, 10, 16, 0.64);--surface-solid:rgba(5, 10, 16, 0.84);--surface-raised:rgba(7, 13, 20, 0.68);--glass-tint:transparent;--glass-wash:linear-gradient(145deg, rgba(255, 255, 255, 0.06), rgba(5, 10, 16, 0.58) 44%, rgba(255, 255, 255, 0.025));--panel-material:linear-gradient(145deg, rgba(255, 255, 255, 0.055), rgba(5, 10, 16, 0.68) 48%, rgba(5, 10, 16, 0.56));--panel-shadow:inset 0 1px 0 rgba(255, 255, 255, 0.13),inset 0 0 0 1px rgba(255, 255, 255, 0.045),0 18px 38px rgba(0, 0, 0, 0.32)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl]{--frame-accessory-opacity:0.22;--surface:rgba(22, 22, 29, 0.42);--surface-solid:rgba(18, 19, 26, 0.66);--surface-raised:rgba(25, 24, 32, 0.5);--line:rgba(238, 215, 236, 0.34);--line-strong:rgba(250, 240, 249, 0.5);--glass-wash:linear-gradient(145deg, rgba(255, 246, 253, 0.045), rgba(22, 22, 29, 0.36) 48%, rgba(255, 221, 117, 0.018));--panel-material:linear-gradient(145deg, rgba(255, 246, 253, 0.045), rgba(22, 22, 29, 0.4) 44%, rgba(18, 19, 26, 0.34));--panel-shadow:inset 0 1px 0 rgba(255, 246, 253, 0.18),inset 0 0 0 1px rgba(255, 185, 226, 0.06),0 16px 34px rgba(12, 12, 18, 0.3)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .app-sidebar{border-right-color:rgba(242,224,240,.26);background:linear-gradient(180deg,rgba(34,31,40,.72),rgba(18,19,26,.68)),rgba(18,18,24,.5);-webkit-backdrop-filter:blur(14px) saturate(0.92);backdrop-filter:blur(14px) saturate(0.92);box-shadow:inset -1px 0 0 rgba(255,246,253,.12),10px 0 28px rgba(12,12,18,.26)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .page-header>div:first-child{border-color:rgba(242,224,240,.18);background:linear-gradient(135deg,rgba(255,246,253,.075),transparent 36%),rgba(22,22,29,.58);-webkit-backdrop-filter:blur(8px) saturate(0.9);backdrop-filter:blur(8px) saturate(0.9)}
|
||||
:root[data-custom-background=true] .app-sidebar{border-right-color:rgba(210,239,246,.24);background:linear-gradient(180deg,rgba(5,10,16,.76),rgba(5,9,14,.68)),rgba(4,8,12,.58);-webkit-backdrop-filter:blur(18px) saturate(0.9);backdrop-filter:blur(18px) saturate(0.9);box-shadow:inset -1px 0 0 rgba(255,255,255,.12),10px 0 28px rgba(0,0,0,.22)}
|
||||
@@ -251,9 +291,9 @@ to{transform:rotate(372deg)}
|
||||
:root[data-custom-background=true] .page-header>div:first-child{display:grid;justify-items:start;padding:7px 12px 9px;border:1px solid rgba(255,255,255,.08);border-radius:8px;background:linear-gradient(135deg,rgba(255,255,255,.055),transparent 36%),rgba(5,10,16,.38);-webkit-backdrop-filter:blur(10px) saturate(0.8);backdrop-filter:blur(10px) saturate(0.8);box-shadow:0 10px 22px rgba(0,0,0,.18)}
|
||||
:root[data-custom-background=true] .page-kicker{margin-bottom:4px;text-shadow:0 1px 2px rgba(0,0,0,.42)}
|
||||
:root[data-custom-background=true] .app-nav-item-active{background:linear-gradient(90deg,color-mix(in srgb,var(--accent) 72%,transparent) 0 3px,transparent 3px),linear-gradient(135deg,rgba(7,16,23,.78),rgba(7,14,20,.56));border-color:color-mix(in srgb,var(--accent) 64%,rgba(255,255,255,.18));box-shadow:inset 0 0 0 1px rgba(255,255,255,.12),0 8px 22px rgba(0,0,0,.22)}
|
||||
:root[data-custom-background=true] .catalog-card,:root[data-custom-background=true] .confirm-panel,:root[data-custom-background=true] .console-panel,:root[data-custom-background=true] .drawer-panel,:root[data-custom-background=true] .metric-card,:root[data-custom-background=true] .operation-item,:root[data-custom-background=true] .overview-card,:root[data-custom-background=true] .plugin-group,:root[data-custom-background=true] .provider-table-wrap,:root[data-custom-background=true] .resource-table-wrap,:root[data-custom-background=true] .server-card,:root[data-custom-background=true] .server-detail-header,:root[data-custom-background=true] .server-table-wrap,:root[data-custom-background=true] .server-toolbar,:root[data-custom-background=true] .state-view{-webkit-backdrop-filter:blur(16px) saturate(0.78);backdrop-filter:blur(16px) saturate(0.78)}
|
||||
:root[data-custom-background=true] .catalog-card,:root[data-custom-background=true] .confirm-panel,:root[data-custom-background=true] .console-panel,:root[data-custom-background=true] .drawer-panel,:root[data-custom-background=true] .metric-card,:root[data-custom-background=true] .operation-item,:root[data-custom-background=true] .overview-card,:root[data-custom-background=true] .plugin-group,:root[data-custom-background=true] .provider-table-wrap,:root[data-custom-background=true] .resource-table-wrap,:root[data-custom-background=true] .server-card,:root[data-custom-background=true] .server-detail-header,:root[data-custom-background=true] .server-table-wrap,:root[data-custom-background=true] .server-toolbar,:root[data-custom-background=true] .state-view{background:linear-gradient(135deg,rgba(255,255,255,.065),transparent 30%),linear-gradient(180deg,rgba(5,10,16,.86),rgba(5,10,16,.78));border-color:color-mix(in srgb,var(--line-strong) 44%,rgba(255,255,255,.16));-webkit-backdrop-filter:blur(16px) saturate(0.78);backdrop-filter:blur(16px) saturate(0.78);box-shadow:inset 0 1px 0 rgba(255,255,255,.12),inset 0 0 0 1px rgba(255,255,255,.04),0 16px 34px rgba(0,0,0,.3)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .catalog-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .confirm-panel,:root[data-custom-background=true][data-theme-palette=magical-girl] .console-panel,:root[data-custom-background=true][data-theme-palette=magical-girl] .drawer-panel,:root[data-custom-background=true][data-theme-palette=magical-girl] .metric-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .operation-item,:root[data-custom-background=true][data-theme-palette=magical-girl] .overview-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .plugin-group,:root[data-custom-background=true][data-theme-palette=magical-girl] .provider-table-wrap,:root[data-custom-background=true][data-theme-palette=magical-girl] .resource-table-wrap,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-detail-header,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-table-wrap,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-toolbar,:root[data-custom-background=true][data-theme-palette=magical-girl] .state-view{background:radial-gradient(circle at 100% 0,rgba(255,221,242,.07),transparent 32%),linear-gradient(135deg,rgba(255,246,253,.065),transparent 34%),linear-gradient(180deg,rgba(24,24,31,.66),rgba(18,19,26,.58));border-color:color-mix(in srgb,var(--line-strong) 54%,rgba(255,255,255,.16));-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:inset 0 1px 0 rgba(255,246,253,.18),inset 0 0 0 1px rgba(255,185,226,.045),0 16px 32px rgba(12,12,18,.28)}
|
||||
:root[data-custom-background=true] .catalog-card,:root[data-custom-background=true] .confirm-panel,:root[data-custom-background=true] .console-panel,:root[data-custom-background=true] .drawer-panel,:root[data-custom-background=true] .metric-card,:root[data-custom-background=true] .operation-item,:root[data-custom-background=true] .overview-card,:root[data-custom-background=true] .plugin-group,:root[data-custom-background=true] .provider-table-wrap,:root[data-custom-background=true] .resource-table-wrap,:root[data-custom-background=true] .server-card,:root[data-custom-background=true] .server-detail-header,:root[data-custom-background=true] .server-table-wrap,:root[data-custom-background=true] .server-toolbar,:root[data-custom-background=true] .state-view{-webkit-backdrop-filter:blur(10px) saturate(0.86);backdrop-filter:blur(10px) saturate(0.86)}
|
||||
:root[data-custom-background=true] .catalog-card,:root[data-custom-background=true] .confirm-panel,:root[data-custom-background=true] .console-panel,:root[data-custom-background=true] .drawer-panel,:root[data-custom-background=true] .metric-card,:root[data-custom-background=true] .operation-item,:root[data-custom-background=true] .overview-card,:root[data-custom-background=true] .plugin-group,:root[data-custom-background=true] .provider-table-wrap,:root[data-custom-background=true] .resource-table-wrap,:root[data-custom-background=true] .server-card,:root[data-custom-background=true] .server-detail-header,:root[data-custom-background=true] .server-table-wrap,:root[data-custom-background=true] .server-toolbar,:root[data-custom-background=true] .state-view{background:linear-gradient(135deg,rgba(255,255,255,.026),transparent 30%),linear-gradient(180deg,rgba(5,10,16,.42),rgba(5,10,16,.34));border-color:color-mix(in srgb,var(--line-strong) 44%,rgba(255,255,255,.16));-webkit-backdrop-filter:blur(10px) saturate(0.86);backdrop-filter:blur(10px) saturate(0.86);box-shadow:inset 0 1px 0 rgba(255,255,255,.12),inset 0 0 0 1px rgba(255,255,255,.04),0 16px 34px rgba(0,0,0,.3)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .catalog-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .confirm-panel,:root[data-custom-background=true][data-theme-palette=magical-girl] .console-panel,:root[data-custom-background=true][data-theme-palette=magical-girl] .drawer-panel,:root[data-custom-background=true][data-theme-palette=magical-girl] .metric-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .operation-item,:root[data-custom-background=true][data-theme-palette=magical-girl] .overview-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .plugin-group,:root[data-custom-background=true][data-theme-palette=magical-girl] .provider-table-wrap,:root[data-custom-background=true][data-theme-palette=magical-girl] .resource-table-wrap,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-card,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-detail-header,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-table-wrap,:root[data-custom-background=true][data-theme-palette=magical-girl] .server-toolbar,:root[data-custom-background=true][data-theme-palette=magical-girl] .state-view{background:radial-gradient(circle at 100% 0,rgba(255,221,242,.026),transparent 32%),linear-gradient(135deg,rgba(255,246,253,.022),transparent 34%),linear-gradient(180deg,rgba(24,24,31,.22),rgba(18,19,26,.18));border-color:color-mix(in srgb,var(--line-strong) 54%,rgba(255,255,255,.16));-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:inset 0 1px 0 rgba(255,246,253,.18),inset 0 0 0 1px rgba(255,185,226,.045),0 16px 32px rgba(12,12,18,.28)}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .server-toolbar,:root[data-custom-background=true][data-theme-palette=magical-girl] .state-view{position:relative;isolation:isolate;overflow:visible}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .server-toolbar::after,:root[data-custom-background=true][data-theme-palette=magical-girl] .state-view::after{content:"";position:absolute;inset:-14px;border-radius:inherit;background-image:var(--frame-accessory-image);background-size:var(--frame-accessory-size);background-repeat:no-repeat;background-position:right -4px top -6px;opacity:var(--frame-accessory-opacity);filter:drop-shadow(0 0 4px rgba(255, 119, 200, .62)) drop-shadow(0 0 2px rgba(255, 221, 117, .46));z-index:0;pointer-events:none}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .server-toolbar>*,:root[data-custom-background=true][data-theme-palette=magical-girl] .state-view>*{position:relative;z-index:1}
|
||||
@@ -265,7 +305,8 @@ to{transform:rotate(372deg)}
|
||||
:root[data-theme-palette=magical-girl] :where(
|
||||
.metric-card,.overview-card,.console-panel,.catalog-card,.server-card,.server-detail-header,.plugin-group,.operation-item,.confirm-panel
|
||||
){overflow:visible}
|
||||
:root[data-custom-background=true] .global-particle-layer{opacity:.03}
|
||||
:root[data-custom-background=true] .global-particle-layer{opacity:.42}
|
||||
:root[data-custom-background=true][data-theme-palette=magical-girl] .global-particle-layer{opacity:.54}
|
||||
:root[data-custom-background=true] .log-filter-bar input,:root[data-custom-background=true] .log-filter-bar select,:root[data-custom-background=true] .profile-form input,:root[data-custom-background=true] .profile-settings-form input,:root[data-custom-background=true] .profile-settings-form textarea,:root[data-custom-background=true] .provider-form input,:root[data-custom-background=true] .provider-form select,:root[data-custom-background=true] .provider-form textarea,:root[data-custom-background=true] .server-toolbar input[type=search],:root[data-custom-background=true] .server-toolbar select{background:rgba(5,10,16,.94);border-color:color-mix(in srgb,var(--accent) 48%,rgba(255,255,255,.18));color:rgba(247,253,255,.96);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 8px 18px rgba(0,0,0,.18)}
|
||||
:root[data-custom-background=true] .action-strip button,:root[data-custom-background=true] .app-account-button,:root[data-custom-background=true] .app-session,:root[data-custom-background=true] .background-preset-option,:root[data-custom-background=true] .icon-command,:root[data-custom-background=true] .palette-option,:root[data-custom-background=true] .row-actions button,:root[data-custom-background=true] .segmented-button,:root[data-custom-background=true] .state-action,:root[data-custom-background=true] .theme-upload{background:linear-gradient(135deg,rgba(255,255,255,.055),transparent 34%),rgba(5,10,16,.84);border-color:color-mix(in srgb,var(--line-strong) 40%,rgba(255,255,255,.14));color:rgba(229,246,251,.9);box-shadow:inset 0 1px 0 rgba(255,255,255,.12),0 7px 16px rgba(0,0,0,.16)}
|
||||
:root[data-custom-background=true] .background-preset-option-active,:root[data-custom-background=true] .palette-option-active,:root[data-custom-background=true] .segmented-button-active{background:linear-gradient(135deg,color-mix(in srgb,var(--accent) 10%,transparent),rgba(5,10,16,.78));border-color:color-mix(in srgb,var(--accent) 58%,rgba(255,255,255,.18));color:rgba(247,253,255,.96);box-shadow:inset 0 1px 0 rgba(255,255,255,.16),0 0 0 1px color-mix(in srgb,var(--accent) 18%,transparent),0 9px 20px rgba(0,0,0,.2)}
|
||||
@@ -443,7 +484,7 @@ to{transform:rotate(372deg)}
|
||||
.log-level-info{color:#7ce3d3}
|
||||
.log-line span:last-child{overflow-wrap:anywhere}
|
||||
.drawer-backdrop{position:fixed;inset:0;z-index:40;background:rgba(61,36,65,.34);backdrop-filter:blur(6px);display:flex;justify-content:flex-end;overflow-y:auto;overscroll-behavior:contain}
|
||||
.drawer-panel{width:min(440px,100%);height:100%;max-height:100dvh;display:grid;align-content:start;gap:14px;padding:20px;background:var(--glass-wash),var(--glass-tint),var(--surface-raised);backdrop-filter:blur(24px) saturate(1.28);border-left:1px solid var(--line);box-shadow:inset 1px 0 0 var(--crystal-rim),-18px 0 42px var(--glass-shadow);position:relative;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
|
||||
.drawer-panel{width:min(440px,100%);height:100%;max-height:100dvh;display:grid;align-content:start;gap:14px;padding:20px;background:var(--glass-wash),var(--glass-tint),var(--surface-raised);backdrop-filter:blur(14px) saturate(0.28);border-left:1px solid var(--line);box-shadow:inset 1px 0 0 var(--crystal-rim),-18px 0 42px var(--glass-shadow);position:relative;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
|
||||
.plugin-detail-panel{width:100%;height:auto;overflow:visible;border:1px solid var(--line);border-left:1px solid var(--line);border-radius:8px;box-shadow:var(--panel-shadow)}
|
||||
.management-dialog-panel{width:min(720px,calc(100vw - 32px));height:auto;max-height:min(760px,calc(100dvh - 32px));overflow-x:hidden;overflow-y:auto;border:1px solid var(--line);border-left:1px solid var(--line);border-radius:8px;box-shadow:var(--jelly-inset),inset 0 0 0 1px var(--diamond-line),0 18px 48px var(--glass-shadow),0 0 34px var(--moonbeam)}
|
||||
.management-dialog-wide{width:min(920px,calc(100vw - 32px))}
|
||||
@@ -539,7 +580,7 @@ to{transform:rotate(372deg)}
|
||||
.signal-tone-error svg{color:var(--danger)}
|
||||
.signal-tone-info svg{color:var(--teal)}
|
||||
.confirm-backdrop{position:fixed;inset:0;z-index:50;display:grid;place-items:center;padding:16px;background:rgba(61,36,65,.38);backdrop-filter:blur(6px);overflow-y:auto;overscroll-behavior:contain}
|
||||
.confirm-panel{width:min(460px,100%);max-height:calc(100dvh - 32px);display:grid;gap:14px;padding:20px;border:1px solid var(--line);border-radius:8px;background:var(--frosted-surface),var(--glass-tint),var(--surface-raised);backdrop-filter:blur(24px) saturate(1.28);box-shadow:var(--jelly-inset),inset 0 0 0 1px var(--diamond-line),0 18px 48px var(--glass-shadow),0 0 34px var(--moonbeam);position:relative;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
|
||||
.confirm-panel{width:min(460px,100%);max-height:calc(100dvh - 32px);display:grid;gap:14px;padding:20px;border:1px solid var(--line);border-radius:8px;background:var(--frosted-surface),var(--glass-tint),var(--surface-raised);backdrop-filter:blur(14px) saturate(0.28);box-shadow:var(--jelly-inset),inset 0 0 0 1px var(--diamond-line),0 18px 48px var(--glass-shadow),0 0 34px var(--moonbeam);position:relative;overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}
|
||||
.confirm-panel h2{margin:0;font-size:18px;color:var(--ink)}
|
||||
.confirm-panel p{margin:0;color:var(--ink-soft);font-size:14px}
|
||||
.confirm-actions{display:flex;gap:8px;justify-content:flex-end;flex-wrap:wrap}
|
||||
|
||||
@@ -28,6 +28,17 @@ describe("platform web theme palettes", () => {
|
||||
expect(palette.variables["--sparkle-gold"]).toContain("rgba");
|
||||
expect(palette.variables["--panel-material"]).toContain("var");
|
||||
expect(palette.variables["--menu-item-bg"]).toContain("gradient");
|
||||
expect(palette.variables["--orbital-construct-image"]).toBe(
|
||||
palette.id === "magical-girl" ? "url(\"/theme/magical-circle.svg\")" : "url(\"/theme/stellar-megastructure.svg\")"
|
||||
);
|
||||
expect(palette.variables["--orbital-construct-filter"]).toContain("drop-shadow");
|
||||
expect(palette.variables["--orbital-construct-filter"]).not.toContain("invert(");
|
||||
expect(palette.variables["--orbital-construct-filter"]).not.toContain("hue-rotate(");
|
||||
expect(palette.variables["--orbital-construct-filter"]).not.toContain("sepia(");
|
||||
expect(palette.variables["--orbital-construct-filter"]).not.toContain("brightness(");
|
||||
expect(palette.variables["--magic-lightfall"]).toContain("linear-gradient");
|
||||
expect(palette.variables["--magic-side-rays"]).toContain("linear-gradient");
|
||||
expect(palette.variables["--command-ring-sheen"]).toContain("radial-gradient");
|
||||
expect(palette.variables["--frame-accessory-top"]).toContain("data:image/svg+xml");
|
||||
expect(palette.variables["--frame-accessory-bottom"]).toContain("data:image/svg+xml");
|
||||
expect(palette.variables["--frame-accessory-opacity"]).toMatch(/^0\.\d{2}$/);
|
||||
@@ -92,7 +103,10 @@ describe("platform web theme palettes", () => {
|
||||
applyThemePalette("mecha-black");
|
||||
|
||||
expect(removeProperty).toHaveBeenCalledWith("--frame-accessory-heart");
|
||||
expect(removeProperty).toHaveBeenCalledWith("--orbital-construct-image");
|
||||
expect(removeProperty).toHaveBeenCalledWith("--magic-side-rays");
|
||||
expect(setProperty).toHaveBeenCalledWith("--accent", "#48e6ff");
|
||||
expect(setProperty).toHaveBeenCalledWith("--orbital-construct-image", "url(\"/theme/stellar-megastructure.svg\")");
|
||||
expect(dispatchEvent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
type: themePaletteChangeEvent,
|
||||
|
||||
@@ -90,6 +90,14 @@ const mechaMaterialVariables = {
|
||||
"--frosted-surface": "linear-gradient(145deg, rgba(14, 28, 38, 0.62), rgba(5, 12, 18, 0.46) 48%, rgba(72, 230, 255, 0.07))",
|
||||
"--frame-corner": "linear-gradient(135deg, rgba(72, 230, 255, 0.9) 0 2px, transparent 2px), linear-gradient(315deg, rgba(255, 184, 77, 0.72) 0 2px, transparent 2px)",
|
||||
"--frame-accent": "linear-gradient(90deg, rgba(72, 230, 255, 0), rgba(72, 230, 255, 0.78), rgba(255, 184, 77, 0.58), rgba(72, 230, 255, 0))",
|
||||
"--orbital-construct-image": "url(\"/theme/stellar-megastructure.svg\")",
|
||||
"--orbital-construct-filter":
|
||||
"drop-shadow(0 0 18px rgba(72, 230, 255, 0.28)) drop-shadow(0 0 34px rgba(255, 184, 77, 0.1))",
|
||||
"--magic-lightfall":
|
||||
"linear-gradient(180deg, transparent 0 4%, rgba(154, 247, 255, 0.34) 18%, rgba(72, 230, 255, 0.1) 42%, transparent 74%)",
|
||||
"--magic-side-rays": "linear-gradient(90deg, rgba(72, 230, 255, 0.3), transparent 72%)",
|
||||
"--command-ring-sheen":
|
||||
"radial-gradient(circle at 50% 50%, rgba(154, 247, 255, 0.32), rgba(72, 230, 255, 0.12) 42%, transparent 68%)",
|
||||
"--frame-accessory-top": mechaFrameAccessoryTop,
|
||||
"--frame-accessory-bottom": mechaFrameAccessoryBottom,
|
||||
"--frame-accessory-opacity": "0.22",
|
||||
@@ -99,15 +107,15 @@ const mechaMaterialVariables = {
|
||||
"--menu-glyph-bg": "linear-gradient(135deg, rgba(72, 230, 255, 0.28), rgba(255, 184, 77, 0.14)), repeating-linear-gradient(90deg, rgba(137, 239, 255, 0.18) 0 1px, transparent 1px 6px), rgba(5, 9, 15, 0.9)",
|
||||
"--menu-title-shadow": "0 0 14px rgba(72, 230, 255, 0.5), 0 0 2px rgba(255, 184, 77, 0.8)",
|
||||
"--menu-active-outline": "linear-gradient(90deg, rgba(72, 230, 255, 0.95), rgba(255, 184, 77, 0.72), rgba(72, 230, 255, 0.95))",
|
||||
"--panel-material": "var(--sugar-dust), var(--glass-wash), linear-gradient(135deg, rgba(5, 12, 18, 0.46), rgba(10, 24, 32, 0.38))",
|
||||
"--panel-material": "var(--sugar-dust), var(--glass-wash), linear-gradient(135deg, rgba(5, 12, 18, 0.3), rgba(10, 24, 32, 0.22))",
|
||||
"--panel-shadow": "var(--jelly-inset), inset 0 0 0 1px rgba(119, 237, 255, 0.24), inset 9px 0 0 rgba(72, 230, 255, 0.06), 0 18px 42px rgba(0, 0, 0, 0.42), 0 0 28px rgba(72, 230, 255, 0.11)"
|
||||
} as const;
|
||||
|
||||
const magicalMaterialVariables = {
|
||||
"--surface": "rgba(88, 18, 52, 0.48)",
|
||||
"--surface-solid": "rgba(78, 14, 46, 0.82)",
|
||||
"--surface-raised": "rgba(118, 26, 68, 0.54)",
|
||||
"--glass-wash": "linear-gradient(180deg, rgba(255, 226, 242, 0.2), rgba(120, 24, 68, 0.32) 56%, rgba(72, 12, 42, 0.28))",
|
||||
"--surface": "rgba(88, 18, 52, 0.38)",
|
||||
"--surface-solid": "rgba(78, 14, 46, 0.7)",
|
||||
"--surface-raised": "rgba(118, 26, 68, 0.42)",
|
||||
"--glass-wash": "linear-gradient(180deg, rgba(255, 226, 242, 0.13), rgba(120, 24, 68, 0.22) 56%, rgba(72, 12, 42, 0.18))",
|
||||
"--shine-sweep": "radial-gradient(circle at 88% 12%, rgba(255, 255, 255, 0.58) 0 2px, transparent 3px)",
|
||||
"--glass-tint": "rgba(255, 119, 200, 0.1)",
|
||||
"--crystal-rim": "rgba(255, 236, 250, 0.92)",
|
||||
@@ -125,9 +133,18 @@ const magicalMaterialVariables = {
|
||||
"--crystal-edge-glow": "0 0 0 1px rgba(255, 217, 239, 0.78), 0 0 18px rgba(255, 119, 200, 0.46), 0 0 34px rgba(255, 221, 117, 0.18)",
|
||||
"--jelly-inset": "inset 0 1px 0 rgba(255, 255, 255, 0.52), inset 0 -1px 0 rgba(255, 119, 200, 0.28), inset 0 0 20px rgba(255, 255, 255, 0.18)",
|
||||
"--frosted-edge": "linear-gradient(180deg, rgba(255, 255, 255, 0.58), rgba(255, 119, 200, 0.2) 48%, rgba(255, 221, 117, 0.18))",
|
||||
"--frosted-surface": "linear-gradient(180deg, rgba(255, 226, 242, 0.18), rgba(118, 24, 66, 0.36) 52%, rgba(72, 12, 42, 0.28))",
|
||||
"--frosted-surface": "linear-gradient(180deg, rgba(255, 226, 242, 0.12), rgba(118, 24, 66, 0.24) 52%, rgba(72, 12, 42, 0.18))",
|
||||
"--frame-corner": "radial-gradient(circle at 0 0, rgba(255, 221, 117, 0.9) 0 4px, transparent 5px), radial-gradient(circle at 100% 100%, rgba(255, 119, 200, 0.9) 0 4px, transparent 5px)",
|
||||
"--frame-accent": "linear-gradient(90deg, rgba(255, 221, 117, 0), rgba(255, 221, 117, 0.86), rgba(255, 119, 200, 0.78), rgba(255, 221, 117, 0))",
|
||||
"--orbital-construct-image": "url(\"/theme/magical-circle.svg\")",
|
||||
"--orbital-construct-filter":
|
||||
"drop-shadow(0 0 18px rgba(255, 119, 200, 0.24)) drop-shadow(0 0 28px rgba(255, 221, 117, 0.16))",
|
||||
"--magic-lightfall":
|
||||
"linear-gradient(180deg, transparent 0 6%, rgba(255, 246, 253, 0.32) 18%, rgba(255, 119, 200, 0.12) 48%, transparent 76%)",
|
||||
"--magic-side-rays":
|
||||
"linear-gradient(90deg, rgba(255, 246, 253, 0.44), rgba(255, 221, 117, 0.18) 34%, rgba(255, 119, 200, 0.08) 62%, transparent)",
|
||||
"--command-ring-sheen":
|
||||
"radial-gradient(circle at 50% 50%, rgba(255, 246, 253, 0.5), rgba(255, 221, 117, 0.18) 42%, transparent 70%)",
|
||||
"--frame-accessory-top": magicalFrameAccessoryWand,
|
||||
"--frame-accessory-bottom": magicalFrameAccessoryCircle,
|
||||
"--frame-accessory-heart": magicalFrameAccessoryHeart,
|
||||
@@ -143,7 +160,7 @@ const magicalMaterialVariables = {
|
||||
"--menu-glyph-bg": "radial-gradient(circle at 30% 22%, rgba(255, 255, 255, 0.78), transparent 34%), radial-gradient(circle at 72% 80%, rgba(255, 221, 117, 0.42), transparent 30%), linear-gradient(135deg, rgba(255, 119, 200, 0.82), rgba(255, 221, 117, 0.44))",
|
||||
"--menu-title-shadow": "0 2px 0 rgba(96, 24, 24, 0.86), 1px 0 0 rgba(96, 24, 24, 0.86), -1px 0 0 rgba(96, 24, 24, 0.86), 0 0 12px rgba(255, 221, 117, 0.48)",
|
||||
"--menu-active-outline": "linear-gradient(90deg, rgba(255, 221, 117, 0.96), rgba(255, 246, 253, 0.92), rgba(255, 119, 200, 0.9), rgba(255, 221, 117, 0.96))",
|
||||
"--panel-material": "var(--sugar-dust), var(--glass-wash), linear-gradient(180deg, rgba(106, 22, 60, 0.3), rgba(72, 12, 42, 0.3))",
|
||||
"--panel-material": "var(--sugar-dust), var(--glass-wash), linear-gradient(180deg, rgba(106, 22, 60, 0.11), rgba(72, 12, 42, 0.09))",
|
||||
"--panel-shadow": "var(--jelly-inset), inset 0 0 0 1px rgba(255, 217, 239, 0.5), 0 18px 42px rgba(72, 0, 34, 0.28), 0 0 30px rgba(255, 119, 200, 0.2), 0 0 16px rgba(255, 221, 117, 0.1)"
|
||||
} as const;
|
||||
|
||||
|
||||
@@ -141,7 +141,9 @@ json_post() {
|
||||
return 0
|
||||
;;
|
||||
*)
|
||||
printf 'POST %s failed with HTTP %s\n' "$url" "$status" >&2
|
||||
if [[ ! -s "$output_file" ]] || ! response_code_is_duplicate "$output_file"; then
|
||||
printf 'POST %s failed with HTTP %s\n' "$url" "$status" >&2
|
||||
fi
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
@@ -154,6 +156,28 @@ json_get() {
|
||||
curl -fsS "$@" "$url" >"$output_file"
|
||||
}
|
||||
|
||||
response_code_is_duplicate() {
|
||||
local file="$1"
|
||||
node -e 'const fs=require("fs"); const data=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); process.exit(data.code === "duplicate" || data.code === "duplicate_resource" ? 0 : 1);' "$file" 2>/dev/null
|
||||
}
|
||||
|
||||
register_plugin_manifest() {
|
||||
local label="$1"
|
||||
local plugin_id="$2"
|
||||
local request_file="$3"
|
||||
local response_file="$4"
|
||||
if json_post "$API_URL/game-plugins/register-manifest" "$request_file" "$response_file" "${AUTH_HEADER[@]}"; then
|
||||
return 0
|
||||
fi
|
||||
if [[ -s "$response_file" ]] && response_code_is_duplicate "$response_file"; then
|
||||
json_get "$API_URL/game-plugins/$plugin_id" "$response_file" "${AUTH_HEADER[@]}"
|
||||
return 0
|
||||
fi
|
||||
printf '%s manifest registration failed; platform response:\n' "$label" >&2
|
||||
sed -n '1,160p' "$response_file" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
require_file_contains() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
@@ -183,6 +207,43 @@ json_id() {
|
||||
node -e 'const fs=require("fs"); const data=JSON.parse(fs.readFileSync(process.argv[1],"utf8")); const id=data.instance?.id || data.serverInstance?.id || data.id || ((data.items||[])[0]||{}).id; if (!id) process.exit(2); process.stdout.write(id);' "$1"
|
||||
}
|
||||
|
||||
require_scum_manifest_contract() {
|
||||
local file="$1"
|
||||
node - "$file" <<'NODE'
|
||||
const fs = require("fs");
|
||||
const file = process.argv[2];
|
||||
const plugin = JSON.parse(fs.readFileSync(file, "utf8"));
|
||||
const missing = [];
|
||||
const declaredPermissions = Array.isArray(plugin.declaredPermissions) ? plugin.declaredPermissions : [];
|
||||
const bridgeActions = Array.isArray(plugin.bridgeActions) ? plugin.bridgeActions : [];
|
||||
const lifecycleProfiles = plugin.runtimeProfiles?.lifecycleProfiles ?? [];
|
||||
const clientManagers = plugin.runtimeProfiles?.clientManagers ?? [];
|
||||
if (!declaredPermissions.includes("server.run.distribution")) {
|
||||
missing.push("server.run.distribution permission");
|
||||
}
|
||||
if (!declaredPermissions.includes("server.client-manager.manage")) {
|
||||
missing.push("server.client-manager.manage permission");
|
||||
}
|
||||
if (!bridgeActions.includes("run.distribution.request")) {
|
||||
missing.push("run.distribution.request bridge action");
|
||||
}
|
||||
if (!lifecycleProfiles.some((profile) => profile.key === "run-local")) {
|
||||
missing.push("run-local runtime profile");
|
||||
}
|
||||
if (!clientManagers.some((profile) => profile.key === "scum-client-manager")) {
|
||||
missing.push("scum-client-manager runtime profile");
|
||||
}
|
||||
if (!plugin.gameClientBridge?.commands?.length) {
|
||||
missing.push("game client bridge declarations");
|
||||
}
|
||||
if (missing.length > 0) {
|
||||
console.error(`SCUM plugin registration is stale or incomplete: missing ${missing.join(", ")}`);
|
||||
console.error("Run scripts/local-debug-reset.sh, restart the local debug stack, then rerun scripts/local-debug-smoke.sh.");
|
||||
process.exit(1);
|
||||
}
|
||||
NODE
|
||||
}
|
||||
|
||||
wait_for_distribution_build() {
|
||||
local distribution_file="$1"
|
||||
local job_file="$2"
|
||||
@@ -311,9 +372,7 @@ fs.writeFileSync(outputPath, JSON.stringify({
|
||||
NODE
|
||||
|
||||
printf 'registering dev plugin through platform API\n'
|
||||
if ! json_post "$API_URL/game-plugins/register-manifest" "$WORK_DIR/register-plugin.request.json" "$WORK_DIR/register-plugin.response.json" "${AUTH_HEADER[@]}"; then
|
||||
json_get "$API_URL/game-plugins/game.example" "$WORK_DIR/register-plugin.response.json" "${AUTH_HEADER[@]}"
|
||||
fi
|
||||
register_plugin_manifest "dev plugin" "game.example" "$WORK_DIR/register-plugin.request.json" "$WORK_DIR/register-plugin.response.json"
|
||||
reject_forbidden_fragments "$WORK_DIR/register-plugin.response.json"
|
||||
|
||||
node - "$ROOT_DIR/plugins/examples/scum-server-plugin/manifest.json" "$WORK_DIR/register-scum-plugin.request.json" <<'NODE'
|
||||
@@ -330,6 +389,7 @@ const localRunCapabilities = [
|
||||
"dependencies.check",
|
||||
"dependencies.install",
|
||||
"logs.backfill",
|
||||
...source.runtimeProfiles.clientManagers.flatMap((manager) => manager.deployment?.requiredRunCapabilities ?? []),
|
||||
...source.capabilities.filter((capability) => capability.startsWith("remote."))
|
||||
];
|
||||
const localRuntimeProfiles = {
|
||||
@@ -342,7 +402,9 @@ const localRuntimeProfiles = {
|
||||
start: "actions/start.json",
|
||||
stop: "actions/stop.json"
|
||||
}
|
||||
}]
|
||||
}],
|
||||
transportProfiles: source.runtimeProfiles?.transportProfiles ?? [],
|
||||
clientManagers: source.runtimeProfiles?.clientManagers ?? []
|
||||
};
|
||||
const manifest = {
|
||||
id: source.id,
|
||||
@@ -365,7 +427,8 @@ const manifest = {
|
||||
ai: source.ai,
|
||||
productionLifecycle: source.productionLifecycle,
|
||||
remoteAccess: source.remoteAccess,
|
||||
runtimeProfiles: localRuntimeProfiles
|
||||
runtimeProfiles: localRuntimeProfiles,
|
||||
gameClientBridge: source.gameClientBridge
|
||||
};
|
||||
fs.writeFileSync(outputPath, JSON.stringify({
|
||||
manifestRef: "artifact://manifests/game.scum/0.1.0",
|
||||
@@ -374,10 +437,9 @@ fs.writeFileSync(outputPath, JSON.stringify({
|
||||
NODE
|
||||
|
||||
printf 'registering SCUM plugin through platform API\n'
|
||||
if ! json_post "$API_URL/game-plugins/register-manifest" "$WORK_DIR/register-scum-plugin.request.json" "$WORK_DIR/register-scum-plugin.response.json" "${AUTH_HEADER[@]}"; then
|
||||
json_get "$API_URL/game-plugins/game.scum" "$WORK_DIR/register-scum-plugin.response.json" "${AUTH_HEADER[@]}"
|
||||
fi
|
||||
register_plugin_manifest "SCUM plugin" "game.scum" "$WORK_DIR/register-scum-plugin.request.json" "$WORK_DIR/register-scum-plugin.response.json"
|
||||
reject_forbidden_fragments "$WORK_DIR/register-scum-plugin.response.json"
|
||||
require_scum_manifest_contract "$WORK_DIR/register-scum-plugin.response.json"
|
||||
|
||||
printf 'waiting for run endpoint heartbeat\n'
|
||||
for _ in $(seq 1 20); do
|
||||
|
||||
Reference in New Issue
Block a user