Fold the trajectory replay controls into a collapsed dropdown
The live map's replay row is now a single 回放 pill that expands to the position readout, play/pause, 回到最新 and speed select, so the map keeps its full width and no slider-shaped control sits above the legend.
This commit is contained in:
@@ -787,8 +787,9 @@ to{transform:translate(-50%,-50%) rotate(calc(var(--construct-drift) + 360deg))}
|
||||
.map-ride-marker{position:absolute;z-index:4;display:grid;place-items:center;width:22px;height:22px;border:1px solid color-mix(in srgb,var(--gold) 70%,var(--line));border-radius:999px;background:color-mix(in srgb,var(--surface-solid) 76%,transparent);color:var(--gold);font:800 9px/1 var(--font-mono);transform:translate(-50%,-50%) scale(var(--map-inverse,1));pointer-events:none}
|
||||
.map-ride-marker img{width:100%;height:100%;object-fit:contain}
|
||||
.map-track-swatch{display:inline-block;width:10px;height:10px;margin-right:6px;border-radius:999px;vertical-align:middle}
|
||||
.map-playback-bar{display:flex;align-items:center;gap:8px;flex-wrap:wrap;width:max-content;max-width:100%;margin-left:auto;padding:5px 10px;border:1px solid color-mix(in srgb,var(--line) 70%,transparent);border-radius:999px;background:var(--control-surface)}
|
||||
.map-playback-bar{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:wrap}
|
||||
.map-playback-bar .page-status{margin:0}
|
||||
.map-playback-bar .scum-map-menu-body{left:auto;right:0}
|
||||
.scum-map-menu{position:relative;flex:0 0 auto}
|
||||
.scum-map-menu>summary{display:inline-flex;align-items:center;min-height:26px;padding:0 9px;font-size:11px;list-style:none;cursor:pointer}
|
||||
.scum-map-menu>summary::-webkit-details-marker{display:none}
|
||||
|
||||
@@ -721,10 +721,17 @@ function persistMapLayers(view: ViewState, actions: SCUMWorkspaceActions | undef
|
||||
function mapPlaybackBar(e: ReactLike["createElement"], view: ViewState, span: { from: number; to: number }, enabled: boolean) {
|
||||
const cursor = view.mapPlayback.value >= 1 ? Infinity : span.from + (span.to - span.from) * view.mapPlayback.value;
|
||||
return e("div", { className: "map-playback-bar" },
|
||||
e("span", { className: "page-status" }, Number.isFinite(cursor) ? `回放 ${timeLabel(cursor)}` : "回放 全部"),
|
||||
e("details", { className: "scum-map-menu", "aria-label": "轨迹回放" },
|
||||
e("summary", { className: "icon-command" }, view.mapPlayback.playing ? "回放 播放中" : "回放"),
|
||||
e("div", { className: "scum-map-menu-body" },
|
||||
e("p", { className: "page-status" }, Number.isFinite(cursor) ? `回放位置 ${timeLabel(cursor)}` : "回放位置 全部"),
|
||||
e("div", { className: "console-row-actions" },
|
||||
e("button", { type: "button", className: view.mapPlayback.playing ? "primary-command" : "icon-command", disabled: !enabled, "aria-label": view.mapPlayback.playing ? "暂停轨迹回放" : "播放轨迹回放", onClick: () => view.setMapPlayback((previous) => ({ ...previous, playing: !previous.playing, value: !previous.playing && previous.value >= 1 ? 0 : previous.value })) }, view.mapPlayback.playing ? "暂停" : "回放"),
|
||||
labeledField(e, "速度", e("select", { value: String(view.mapPlayback.speed), "aria-label": "轨迹回放速度", onChange: (event: InputEvent) => view.setMapPlayback((previous) => ({ ...previous, speed: Number(inputValue(event)) || 1 })) }, [1, 2, 4].map((speed) => e("option", { key: speed, value: String(speed) }, `${speed}x`)))),
|
||||
e("button", { type: "button", className: "icon-command", disabled: !enabled, onClick: () => view.setMapPlayback(scumMapPlaybackIdle) }, "回到最新")
|
||||
),
|
||||
labeledField(e, "速度", e("select", { value: String(view.mapPlayback.speed), "aria-label": "轨迹回放速度", onChange: (event: InputEvent) => view.setMapPlayback((previous) => ({ ...previous, speed: Number(inputValue(event)) || 1 })) }, [1, 2, 4].map((speed) => e("option", { key: speed, value: String(speed) }, `${speed}x`))))
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
"id": "game.scum",
|
||||
"name": "SCUM Server",
|
||||
"description": "First-party SCUM game server operations plugin with platform-mediated lifecycle and plugin-owned RCON data flows.",
|
||||
"version": "0.1.24",
|
||||
"version": "0.1.25",
|
||||
"kind": "game-plugin",
|
||||
"tags": [
|
||||
"scum",
|
||||
|
||||
@@ -357,7 +357,7 @@ describe("plugin manifest validation", () => {
|
||||
const serialized = JSON.stringify(manifest).toLowerCase();
|
||||
|
||||
expect(serialized).not.toContain("local-proof");
|
||||
expect(manifest.version).toBe("0.1.24");
|
||||
expect(manifest.version).toBe("0.1.25");
|
||||
expect(installAction.environment?.SERVER_TEMPLATE).toBe("scum-server");
|
||||
expect(manifest.permissions).toEqual(expect.arrayContaining(["server.game-client.read", "server.game-client.command", "server.game-client.maintenance"]));
|
||||
expect(manifest.gameClientBridge.commands.map((command) => command.type)).toEqual(expect.arrayContaining([
|
||||
|
||||
@@ -312,7 +312,8 @@ describe("SCUM plugin feature module", () => {
|
||||
expect(view.inputs.map((input) => input.label)).not.toContain("启用自定义地图");
|
||||
expect(view.texts).not.toContain("地图范围与图层");
|
||||
expect(view.texts).not.toContain("自定义范围");
|
||||
expect(view.elements.map((element) => element.label)).toEqual(expect.arrayContaining(["轨迹用户筛选", "地图图层筛选"]));
|
||||
expect(view.elements.map((element) => element.label)).toEqual(expect.arrayContaining(["轨迹用户筛选", "地图图层筛选", "轨迹回放"]));
|
||||
expect(view.nodes).toContain("details:轨迹回放");
|
||||
expect(view.texts).not.toContain("轨迹列表");
|
||||
expect(view.elements.map((element) => element.label)).toContain("轨迹颜色图例");
|
||||
expect(view.buttons.map((button) => button.label)).toEqual(expect.arrayContaining(["全选", "清空", "不限", "回放", "回到最新"]));
|
||||
|
||||
Reference in New Issue
Block a user