Workers P2: Sichtprüfung im Browser – Schilder über den Figuren, Raumtitel über den Möbeln, Zoomknöpfe unten, Abstand der Meldung

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Sonnet 5.5 committed 2026-10-18 04:55:51 +00:00
1 parent d67f8cc471
commit 820ccd510d
9 files changed
+122 -21

No files matched your search

+29 -10
View File
@@ -13,6 +13,7 @@ const GREY_TINT = '#9aa3b2'
const DIVIDER = '#e1e5ec'
const SIGN_TEXT = 'noch nicht angeschlossen'
const SIGN_MAX_W = 60
const TITLE_HALO = 'rgba(255, 255, 255, 0.85)' // Rand der Raumtitel
const STEP_COLORS = [TEAM.radar, TEAM.bewertung, TEAM.werkstatt, TEAM.pruefung, TEAM.freigabe] // Stationen der Pipeline-Tafel
const FEED_ROWS = [8, 5, 9] // Breiten der durchlaufenden Zeilen im Feed-Schirm
const MAX_PAGE_DOTS = 19
@@ -274,9 +275,16 @@ export const ROOM_DRAWERS = Object.freeze({
// ---------- Zeichnen ----------
function drawRoom(ctx, room, floor, index, t, look) {
const isGrey = (room, look) => room.workerId != null && look.greyRooms.has(room.id)
function drawRoomSign(ctx, room, floor) {
const w = Math.min(room.w - 2, SIGN_MAX_W)
drawSign(ctx, room.x + (room.w - w) / 2, floor.top + 1, w, SIGN_TEXT)
}
function drawRoom(ctx, room, floor, index, t, look, signs) {
// Räume ohne Worker (pult, tresor) werden nie grau
const grey = room.workerId != null && look.greyRooms.has(room.id)
const grey = isGrey(room, look)
const r = {
room,
x: room.x,
@@ -292,19 +300,30 @@ function drawRoom(ctx, room, floor, index, t, look) {
// Teamfarbton hat jeder Raum, auch ein grauer (die Grautönung kommt darüber)
tint(ctx, room.x, floor.top, room.w, floor.h, r.color, TINT_ALPHA)
if (index > 0) px(ctx, room.x, floor.top, 1, floor.h, DIVIDER)
// Der Titel steht in der obersten Zeile des Raums. Im grauen Raum nimmt das Schild (44 breit, 8 hoch) diese Zeile ein:
// über dem Hut der schlafenden Figur sind nur 6 Zeilen frei, Titel (3,3) und Schild (8) passen in den 46 breiten
// Such-Räumen, den einzigen, die grau sein können, nicht übereinander. Name und Branche zeigen dort Möbel und Icon.
if (!grey) label(ctx, room.title, room.x + 2, floor.top + 1, { size: 10, color: PAL.mute, weight: 500 })
if (Object.hasOwn(ROOM_DRAWERS, room.kind)) ROOM_DRAWERS[room.kind](ctx, r)
// Der Titel steht in der obersten Zeile des Raums, über den Möbeln (ein Monitor deckt sonst den Anfang zu) und mit hellem
// Rand, damit er auch über einem dunklen Bildschirm lesbar bleibt. Im grauen Raum nimmt das Schild (44 breit, 8 hoch) diese
// Zeile ein: über dem Hut der schlafenden Figur sind nur 6 Zeilen frei, Titel (3,3) und Schild (8) passen in den 46 breiten
// Such-Räumen, den einzigen, die grau sein können, nicht übereinander. Name und Branche zeigen dort Möbel und Icon.
if (!grey) label(ctx, room.title, room.x + 2, floor.top + 1, { size: 10, color: PAL.mute, weight: 500, halo: TITLE_HALO })
if (grey) {
tint(ctx, room.x, floor.top, room.w, floor.h, GREY_TINT, GREY_ALPHA)
const w = Math.min(room.w - 2, SIGN_MAX_W)
drawSign(ctx, room.x + (room.w - w) / 2, floor.top + 1, w, SIGN_TEXT)
if (signs) drawRoomSign(ctx, room, floor)
}
}
// Alle Räume aller Etagen. Vorher: drawOutside, drawTower. Nachher: Aufzug hinten, Figuren, Aufzug vorn.
export function drawRooms(ctx, layout, t, look) {
for (const floor of layout.floors) floor.rooms.forEach((room, index) => drawRoom(ctx, room, floor, index, t, look))
// signs = false lässt die Schilder der grauen Räume aus: die Szene zeichnet sie mit drawSigns erst nach den Figuren,
// sonst deckt der Hut der schlafenden Figur die untere Zeile des Schildtexts zu.
export function drawRooms(ctx, layout, t, look, { signs = true } = {}) {
for (const floor of layout.floors) floor.rooms.forEach((room, index) => drawRoom(ctx, room, floor, index, t, look, signs))
}
// Die Schilder „noch nicht angeschlossen“ der grauen Räume (Gegenstück zu drawRooms mit signs: false)
export function drawSigns(ctx, layout, look) {
for (const floor of layout.floors) {
for (const room of floor.rooms) {
if (isGrey(room, look)) drawRoomSign(ctx, room, floor)
}
}
}
+4 -3
View File
@@ -10,7 +10,7 @@ import { WORLD, buildLayout } from './layout.js'
import { createCamera, fitZoom, viewToWorld, worldToView, zoomAt, panBy, centerOn, pickAgent } from './camera.js'
import { createLift } from './lift.js'
import { createFx } from './fx.js'
import { createLook, drawRooms } from './rooms.js'
import { createLook, drawRooms, drawSigns } from './rooms.js'
import { drawOutside, drawTower, drawPauseVeil } from './building.js'
import { createDirector, drawAgents } from './director.js'
@@ -46,7 +46,7 @@ function inertHandle() {
→ { destroy(), setState(state), pushEvents(events), select(workerId|null), setPaused(bool), flush(), resize(), zoomBy(faktor),
resetView(), locate(workerId): { x, y } | null, stats(): { frames, avgDrawMs, zoom }, debug() }
Rasterweite je Bild: k = Canvas-Breite / (320 / zoom). Reihenfolge je Bild: Weiß, Außenwelt, Turm, Räume, Aufzug hinten, Figuren,
Aufzug vorn, Effekte, Pausenschleier. onSelect(id | null) kommt nur bei Klicks des Nutzers, nie bei select().
Schilder der grauen Räume, Aufzug vorn, Effekte, Pausenschleier. onSelect(id | null) kommt nur bei Klicks des Nutzers, nie bei select().
reducedMotion (und fehlendes raf): keine Schleife; setState, pushEvents, select, resize und die Kamera zeichnen je ein Standbild,
pushEvents spielt die Regie sofort zu Ende. fontsReady (Promise): nach dem Laden der Schrift wird einmal neu gezeichnet.
flush() beruhigt die Regie auf einen Schlag (alles zu Ende gespielt, alle Figuren heim, Aufzug und Effekte leer): die Bühne ruft es
@@ -98,9 +98,10 @@ export function mountTower(canvas, options = {}) {
ctx.fillRect(0, 0, canvas.width, canvas.height)
drawOutside(ctx)
drawTower(ctx, layout, time, lift)
drawRooms(ctx, layout, time, look)
drawRooms(ctx, layout, time, look, { signs: false })
lift.drawBack(ctx)
drawAgents(ctx, director, selectedId, time)
drawSigns(ctx, layout, look)
lift.drawFront(ctx)
fx.draw(ctx)
drawPauseVeil(ctx, look)
+9 -2
View File
@@ -227,8 +227,9 @@ export function drawAlertMark(ctx, x, y) {
const fontOf = (weight, devPx) => `${weight} ${Math.round(devPx * 100) / 100}px ${FONT_FAMILY}`
/* Text an (x, y) in logischen Einheiten (y = Oberkante der Zeile). Schrift = size * k / 3 Geräte-Pixel,
bei k = 3 also size px. bg: Farbe eines Kastens hinter dem Text; align: 'left' | 'center' | 'right'. */
export function label(ctx, text, x, y, { size = 12, color = PAL.ink, weight = 500, align = 'left', bg } = {}) {
bei k = 3 also size px. bg: Farbe eines Kastens hinter dem Text; halo: Farbe eines dünnen Rands um die Buchstaben
(lesbar auch über dunklen Flächen wie einem Monitor); align: 'left' | 'center' | 'right'. */
export function label(ctx, text, x, y, { size = 12, color = PAL.ink, weight = 500, align = 'left', bg, halo } = {}) {
const { k, ox, oy } = gridOf(ctx)
const f = k / 3
const str = String(text ?? '')
@@ -243,6 +244,12 @@ export function label(ctx, text, x, y, { size = 12, color = PAL.ink, weight = 50
ctx.fillStyle = bg
ctx.fillRect(Math.round(left - 4 * f), Math.round(dy - 2 * f), Math.round(tw + 8 * f), Math.round(size * f + 5 * f))
}
if (halo) {
ctx.lineJoin = 'round'
ctx.lineWidth = Math.max(2, Math.round(3 * f))
ctx.strokeStyle = halo
ctx.strokeText(str, dx, dy)
}
ctx.fillStyle = color
ctx.fillText(str, dx, dy)
ctx.textAlign = 'left'
+4 -1
View File
@@ -24,6 +24,8 @@
margin-bottom: 20px;
}
.wk-status { color: var(--text-2); }
/* Meldungen zwischen Kopfzeile und Kennzahlen (Notbremse, Fehler) halten Abstand zu den Kennzahlen */
.wk-toolbar ~ .msg { margin-bottom: 16px; }
/* ---------- Kennzahlen ---------- */
@@ -59,9 +61,10 @@
cursor: pointer;
touch-action: pan-y;
}
/* unten rechts: oben rechts läge er über dem Schild der Kommandobrücke */
.wk-zoom {
position: absolute;
top: 8px;
bottom: 8px;
right: 8px;
display: flex;
gap: 4px;