Files
isokit/studio/studio.css
T
grabowski 95ea4a8512
Deploy web Studio / deploy (push) Successful in 3m27s
Build your own house: walls, windows, doors, floors, roof, stairs, furniture
House parts (stdlib isokit/house): house_wall drawn along a line, with ends
that adapt at corners, T-junctions and straight runs so no two walls
overlap; window and door that sit on a wall and cut its opening (door
styles panel, glass, double, sliding, opening; swing and hinge by
rotation); floor (boards, tiles, carpet, stone); roof (hip, gable, flat,
optional solar); stairs.

Furniture (stdlib isokit/furniture): chair, sofa / armchair, table with
chairs, desk with monitor and chair, TV, bed, cupboard, bookshelf, kitchen
run, bathtub, shower, toilet, washbasin, plant, rug, floor lamp.

Cut-open levels: on gf / 1f the walls in front of a room are cut low
(rooms are found by their floors), the roof becomes an outline on the
floor, and storeys above are left out. A part's storey comes from z
(storey_h, default 3 m).

Scripting: objects_of, objects_at, objects_on, mount_of, mounted, frame,
to_world, to_local, local_of. Prefab meta: shape (line / rect), mount,
storey.

Studio: drag to draw lines and rectangles (walls, roads, fences, hedges,
floors, areas, buildings, roofs). The Room tool draws a floor plus walls,
without doubling walls that already stand on an edge. Windows and doors
snap onto walls with a hover preview, and they move, turn, copy and
delete with their wall in one undo step. New parts land on the storey
the view shows. Palette categories collapse, and the palette scrolls
apart from the object list.

The MCP instructions explain house building, and list_prefabs shows
sits_on. examples/house is a furnished bungalow; the web Studio gets a
House demo button. Tests: wall corners and junctions, zero depth-sort
cycles in every view, cut levels, and the house demo in every view;
check_studio covers the room / window / door / storey flow, check_web
the House demo.
2026-10-24 17:39:04 +07:00

171 lines
14 KiB
CSS

/* isokit Studio: layout and controls. Scene colours come from the theme. */
:root { color-scheme: dark; --bg: #0a0f1a; --panel: #0f1626; --panel2: #131c30; --stroke: #2a3a56; --fg: #d6deeb; --mute: #8a97b0; --accent: #3ddc84; --danger: #ff6b6b; }
html[data-theme=light] { color-scheme: light; --bg: #eef2f7; --panel: #ffffff; --panel2: #f5f7fb; --stroke: #cfd8e6; --fg: #1b2533; --mute: #5b6880; --accent: #0e8f4f; --danger: #c62828; }
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body { background: var(--bg); color: var(--fg); font: 13px/1.4 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; display: flex; flex-direction: column; overflow: hidden; }
button, select, input, textarea { font: inherit; color: inherit; }
button, .st-btn { background: var(--panel); border: 1px solid var(--stroke); border-radius: 5px; padding: 4px 10px; cursor: pointer; text-decoration: none; color: var(--fg); white-space: nowrap; }
button:hover:not(:disabled), .st-btn:hover { border-color: var(--accent); }
button:disabled { opacity: .45; cursor: default; }
button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
input, select, textarea { background: var(--panel2); border: 1px solid var(--stroke); border-radius: 4px; padding: 3px 6px; min-width: 0; }
input[type=checkbox] { accent-color: var(--accent); }
.st-head { display: flex; align-items: center; gap: 8px; padding: 6px 10px; background: var(--panel); border-bottom: 1px solid var(--stroke); flex: 0 0 auto; }
.st-logo { font-size: 15px; letter-spacing: .02em; }
.st-logo span { color: var(--accent); font-weight: 600; }
.st-title { color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-grow { flex: 1 1 auto; }
.st-sep { width: 1px; height: 22px; background: var(--stroke); }
.st-save { color: var(--mute); font-size: 12px; min-width: 64px; text-align: center; }
.st-save.st-err { color: var(--danger); }
.st-main { flex: 1 1 auto; display: grid; grid-template-columns: 250px minmax(0, 1fr) 300px; min-height: 0; }
.st-left, .st-right { background: var(--panel); border-right: 1px solid var(--stroke); overflow: auto; min-height: 0; }
.st-left { display: flex; flex-direction: column; }
.st-right { border-right: 0; border-left: 1px solid var(--stroke); padding: 10px; }
.st-panel { padding: 8px 10px; border-bottom: 1px solid var(--stroke); }
.st-grow-panel { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 140px; }
/* the palette scrolls on its own, so the object list below stays in view */
.st-left { overflow: hidden; }
.st-pal-panel { flex: 0 1 auto; max-height: 62%; min-height: 120px; display: flex; flex-direction: column; }
.st-pal-panel .st-palette { overflow: auto; min-height: 0; padding-right: 2px; }
.st-panel h2, .st-right h2 { margin: 2px 0 6px; font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--mute); font-weight: 600; }
.st-panel h2 small { text-transform: none; letter-spacing: 0; font-weight: 400; margin-left: 4px; }
.st-panel input[type=search] { width: 100%; margin-bottom: 6px; }
.st-palette { display: flex; flex-direction: column; gap: 6px; }
.st-cat { font-size: 11px; color: var(--mute); margin: 4px 0 2px; cursor: pointer; user-select: none; }
.st-cat::before { content: "▾"; display: inline-block; width: 12px; }
.st-cat.st-closed::before { content: "▸"; }
.st-cat:hover { color: var(--fg); }
.st-cat small { opacity: .7; }
.st-tiles[hidden] { display: none; }
.st-tiles { display: grid; grid-template-columns: 1fr 1fr; gap: 4px; }
.st-tile { display: flex; align-items: center; gap: 6px; text-align: left; padding: 4px 6px; overflow: hidden; }
.st-tile i { font-style: normal; width: 18px; text-align: center; flex: 0 0 auto; }
.st-tile span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-tile[aria-pressed=true] { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 18%, var(--panel)); }
.st-objects { list-style: none; margin: 0; padding: 0; overflow: auto; flex: 1 1 auto; }
.st-objects li { display: flex; align-items: center; gap: 6px; padding: 3px 6px; border-radius: 4px; cursor: pointer; }
.st-objects li:hover { background: var(--panel2); }
.st-objects li[aria-selected=true] { background: color-mix(in srgb, var(--accent) 20%, var(--panel)); }
.st-objects li i { font-style: normal; width: 18px; text-align: center; }
.st-objects li span { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-objects li small { color: var(--mute); }
.st-objects li.st-hidden span { opacity: .5; text-decoration: line-through; }
.st-centre { position: relative; display: flex; flex-direction: column; min-width: 0; min-height: 0; }
.st-centre > #view { flex: 1 1 auto; min-height: 0; }
.st-centre.st-placing .iso-svg { cursor: crosshair; }
.st-hint { position: absolute; top: 52px; left: 50%; transform: translateX(-50%); background: var(--panel); border: 1px solid var(--accent); border-radius: 16px; padding: 4px 14px; z-index: 5; pointer-events: none; }
.st-status { display: flex; align-items: center; gap: 14px; padding: 4px 10px; background: var(--panel); border-top: 1px solid var(--stroke); color: var(--mute); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.st-status select { padding: 1px 4px; }
.st-ver { color: var(--mute); font-size: 11px; text-decoration: none; white-space: nowrap; }
.st-ver:hover { color: var(--fg); text-decoration: underline; }
.st-link { background: none; border: 0; color: var(--danger); padding: 0; text-decoration: underline; }
.st-right h3 { margin: 0; font-size: 15px; display: flex; align-items: center; gap: 6px; }
.st-right h3 i { font-style: normal; }
.st-sub { color: var(--mute); font-size: 12px; margin: 2px 0 8px; }
.st-actions { display: flex; flex-wrap: wrap; gap: 4px; margin-bottom: 10px; }
.st-actions button { padding: 3px 8px; }
.st-actions .st-danger { color: var(--danger); }
.st-form { display: grid; grid-template-columns: 92px minmax(0, 1fr); gap: 5px 8px; align-items: center; margin-bottom: 12px; }
.st-form > label { color: var(--mute); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.st-form textarea { resize: vertical; min-height: 44px; }
.st-form .st-full { grid-column: 1 / -1; }
.st-xy { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 4px; }
.st-xy input { width: 100%; }
.st-num { display: grid; grid-template-columns: minmax(0, 1fr) 64px; gap: 6px; align-items: center; }
.st-num input[type=number] { width: 100%; }
.st-rot { display: flex; gap: 4px; align-items: center; }
.st-levels { display: flex; flex-wrap: wrap; gap: 4px 10px; }
.st-levels label { color: var(--fg); font-size: 12px; }
.st-help { color: var(--mute); font-size: 12px; margin: -4px 0 10px; }
.st-box { border: 1px dashed var(--stroke); border-radius: 6px; padding: 8px; margin: 10px 0; }
.st-box code { display: block; background: var(--panel2); border-radius: 4px; padding: 4px 6px; margin: 4px 0; font-size: 11.5px; word-break: break-all; user-select: all; }
.st-warn { color: var(--danger); font-size: 12px; margin: 4px 0; }
.st-toast { position: fixed; bottom: 40px; left: 50%; transform: translateX(-50%); background: var(--panel); border: 1px solid var(--accent); border-radius: 6px; padding: 6px 14px; z-index: 50; box-shadow: 0 4px 14px rgba(0, 0, 0, .3); }
.st-toast.st-bad { border-color: var(--danger); }
.st-dialog { background: var(--panel); color: var(--fg); border: 1px solid var(--stroke); border-radius: 8px; max-width: 560px; }
.st-dialog th { text-align: left; padding: 3px 12px 3px 0; color: var(--mute); font-weight: 500; vertical-align: top; }
.st-dialog form { text-align: right; margin-top: 10px; }
/* agent window (agent.js): floats over the map, moves by its title, resizes at the top-left corner */
[hidden] { display: none !important; }
.ag-open[aria-pressed=true] { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 18%, var(--panel)); }
.ag-win { position: absolute; right: 12px; bottom: 40px; width: 400px; height: 540px; z-index: 6; display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--stroke); border-radius: 8px; box-shadow: 0 8px 28px rgba(0, 0, 0, .35); overflow: hidden; }
.ag-head { display: flex; align-items: center; gap: 6px; padding: 5px 6px 5px 12px; border-bottom: 1px solid var(--stroke); background: var(--panel2); cursor: move; user-select: none; touch-action: none; }
.ag-head button { padding: 2px 8px; }
.ag-logo { white-space: nowrap; }
.ag-logo, .ag-open { color: var(--fg); }
.ag-who { background: none; border: 0; padding: 0 2px !important; color: var(--mute); font-size: 12px; overflow: hidden; text-overflow: ellipsis; min-width: 0; display: flex; align-items: center; gap: 5px; }
.ag-who:hover { color: var(--fg); }
.ag-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--mute); flex: 0 0 auto; }
.ag-dot.ag-on { background: var(--accent); }
.ag-dot.ag-off { background: var(--danger); }
.ag-set { display: grid; grid-template-columns: 70px minmax(0, 1fr); gap: 5px 8px; align-items: center; padding: 8px 10px; border-bottom: 1px solid var(--stroke); font-size: 12px; }
.ag-set > label { color: var(--mute); }
.ag-set input:not([type=checkbox]), .ag-set select { width: 100%; }
.ag-row { display: flex; gap: 4px; align-items: center; min-width: 0; }
.ag-row > input, .ag-row > select { flex: 1 1 auto; }
.ag-row button { padding: 2px 8px; }
.ag-check { color: var(--fg); display: flex; align-items: center; gap: 5px; }
.ag-check small { color: var(--mute); }
.ag-fnote { grid-column: 1 / -1; margin: 0; color: var(--mute); }
.ag-fnote code { font-size: 11px; word-break: break-all; }
.ag-end { grid-column: 1 / -1; justify-content: flex-end; }
.ag-smsg { flex: 1 1 auto; color: var(--mute); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ag-badtext { color: var(--danger); white-space: normal; }
.ag-log { flex: 1 1 auto; overflow: auto; padding: 10px 10px 4px; display: flex; flex-direction: column; gap: 8px; min-height: 0; }
.ag-empty { color: var(--mute); margin: auto 0 0; }
.ag-empty p { margin: 0 0 8px; }
.ag-sugs { display: flex; flex-wrap: wrap; gap: 5px; }
.ag-sug { white-space: normal; text-align: left; font-size: 12px; padding: 4px 9px; border-radius: 14px; }
.ag-user { align-self: flex-end; max-width: 88%; background: color-mix(in srgb, var(--accent) 16%, var(--panel2)); border: 1px solid color-mix(in srgb, var(--accent) 35%, var(--stroke)); border-radius: 10px 10px 2px 10px; padding: 5px 10px; overflow-wrap: anywhere; }
.ag-bot { overflow-wrap: anywhere; }
.ag-bot p { margin: 0 0 6px; }
.ag-bot p:last-child { margin-bottom: 0; }
.ag-bot ul, .ag-bot ol { margin: 0 0 6px; padding-left: 20px; }
.ag-bot pre, .ag-tool pre { background: var(--panel2); border: 1px solid var(--stroke); border-radius: 4px; padding: 5px 7px; margin: 4px 0; font-size: 11.5px; white-space: pre-wrap; word-break: break-word; max-height: 220px; overflow: auto; }
.ag-bot code, .ag-tool code { background: var(--panel2); border-radius: 3px; padding: 0 3px; font-size: 12px; }
.ag-live .ag-text:empty::after, .ag-live .ag-text > :last-child::after { content: "\258D"; color: var(--accent); animation: ag-blink 1s steps(2) infinite; }
.ag-live .ag-think:not([hidden]) + .ag-text:empty::after { content: none; }
@keyframes ag-blink { 50% { opacity: 0; } }
.ag-think { color: var(--mute); font-size: 12px; margin-bottom: 4px; }
.ag-think summary { cursor: pointer; }
.ag-live .ag-think summary { animation: ag-pulse 1.4s ease-in-out infinite; }
@keyframes ag-pulse { 50% { opacity: .45; } }
.ag-think div { white-space: pre-wrap; border-left: 2px solid var(--stroke); padding: 2px 0 2px 8px; margin: 4px 0; max-height: 180px; overflow: auto; }
.ag-tool { font-size: 12px; }
.ag-tool summary { cursor: pointer; display: flex; align-items: center; gap: 6px; color: var(--mute); list-style: none; padding: 2px 6px; border: 1px solid var(--stroke); border-radius: 5px; background: var(--panel2); }
.ag-tool summary::-webkit-details-marker { display: none; }
.ag-tool summary::before { content: "\2699"; }
.ag-tool summary b { color: var(--fg); font-weight: 600; font-family: ui-monospace, Consolas, monospace; font-size: 11.5px; }
.ag-tool summary span { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ag-tool summary em { font-style: normal; }
.ag-tool.ag-ok summary em { color: var(--accent); }
.ag-tool.ag-bad summary { border-color: color-mix(in srgb, var(--danger) 50%, var(--stroke)); }
.ag-tool.ag-bad summary em { color: var(--danger); }
.ag-obj { cursor: pointer; color: var(--accent); text-decoration: underline dotted; }
.ag-note { color: var(--mute); font-size: 12px; font-style: italic; }
.ag-err { color: var(--danger); font-size: 12px; border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--stroke)); border-radius: 5px; padding: 5px 8px; overflow-wrap: anywhere; }
.ag-link { background: none; border: 0; padding: 0 2px; color: var(--fg); text-decoration: underline; font-size: 12px; }
.ag-in { display: flex; gap: 6px; padding: 8px; border-top: 1px solid var(--stroke); align-items: flex-end; }
.ag-in textarea { flex: 1 1 auto; resize: none; min-height: 38px; max-height: 140px; line-height: 1.35; }
.ag-in button { align-self: stretch; min-width: 62px; }
.ag-in .ag-stop { border-color: var(--danger); color: var(--danger); }
.ag-grip { position: absolute; left: 0; top: 0; width: 14px; height: 14px; cursor: nwse-resize; touch-action: none; background: linear-gradient(135deg, var(--mute) 0 2px, transparent 2px 4px, var(--mute) 4px 5px, transparent 5px); opacity: .5; border-top-left-radius: 8px; }
.ag-grip:hover { opacity: 1; }
/* previews while placing: footprints, wall planes, where a window lands */
.iso-svg .st-ghosts { pointer-events: none; }
.iso-svg .st-ghost { fill: color-mix(in srgb, var(--iso-accent) 16%, transparent); stroke: var(--iso-accent); stroke-width: 1.5; stroke-dasharray: 5 3; vector-effect: non-scaling-stroke; stroke-linejoin: round; }
.iso-svg .st-ghost.st-plane { fill: color-mix(in srgb, var(--iso-accent) 28%, transparent); stroke-dasharray: none; }
@media (max-width: 900px) {
.st-main { grid-template-columns: 200px minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) 40vh; }
.st-right { grid-column: 1 / -1; border-left: 0; border-top: 1px solid var(--stroke); }
}