283 lines
15 KiB
HTML
283 lines
15 KiB
HTML
<!doctype html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="utf-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||
<title>Pixel Island Summoner - Local Prototype</title>
|
||
<link rel="stylesheet" href="./styles.css" />
|
||
</head>
|
||
<body>
|
||
<div class="app">
|
||
<canvas id="worldCanvas" aria-label="Island map"></canvas>
|
||
<div id="islandClickHint" class="islandClickHint">Left-click: select · Right-click: clear</div>
|
||
|
||
<button id="openEditor" class="drawDockButton" type="button" title="Open Pixel Studio">✎ DRAW</button>
|
||
<div id="placementPreviewBar" class="placementPreviewBar" hidden>
|
||
<button id="confirmPreviewPlace" class="primary" type="button">Place here</button>
|
||
<button id="backToCanvas" class="secondary" type="button">Back to canvas</button>
|
||
<span>Check how this work looks on the island. Works are permanent in Collection; island placement is temporary exhibition.</span>
|
||
</div>
|
||
|
||
<header class="hud topHud islandTopBar">
|
||
<div class="brandBlock">
|
||
<div class="logo">Pixel Island</div>
|
||
<div class="subline">A rotating island exhibition for tiny pixel works.</div>
|
||
</div>
|
||
<div class="authorCard accountCard utilityControl" aria-label="Local account">
|
||
<div class="accountFields">
|
||
<label class="accountField">ID
|
||
<input id="accountId" type="text" readonly placeholder="auto" />
|
||
</label>
|
||
<label class="accountField">Name
|
||
<input id="authorName" type="text" maxlength="24" value="Local Artist" />
|
||
</label>
|
||
<label class="accountField">Pass
|
||
<input id="accountPass" type="text" maxlength="32" placeholder="auto" />
|
||
</label>
|
||
</div>
|
||
<button id="createAccount" class="miniButton" type="button">Generate account</button>
|
||
<small id="accountNote">Save + Place creates a local ID, name, and password. Change the password after creation.</small>
|
||
</div>
|
||
</header>
|
||
|
||
<div id="analogClock" class="analogClock" aria-label="Island day-night clock" title="10 min = 1 island day">
|
||
<div class="analogClockDial">
|
||
<span id="analogClockHand" class="analogClockHand"></span>
|
||
</div>
|
||
<div id="phaseLabel" class="phaseLabel analogClockLabel">Day</div>
|
||
<div class="phaseBar analogPhaseBar" aria-hidden="true"><span id="phaseBar"></span></div>
|
||
</div>
|
||
|
||
<section id="studioDrawer" class="studioDrawer" aria-label="Pixel Studio">
|
||
<div class="drawerHead">
|
||
<div>
|
||
<h1>Pixel Studio</h1>
|
||
<p>Make static scenery or moving island life.</p>
|
||
</div>
|
||
<button id="closeEditor" class="ghostButton" title="Close">×</button>
|
||
</div>
|
||
|
||
<nav class="tabs" aria-label="Studio tabs">
|
||
<button class="tab active" data-tab="draw">Create</button>
|
||
<button class="tab" data-tab="library">Collection</button>
|
||
<button class="tab" data-tab="settings">Settings</button>
|
||
<button class="tab devOnly" data-tab="data">Data</button>
|
||
</nav>
|
||
|
||
<div class="drawerBody">
|
||
<section id="tab-draw" class="tabPanel active">
|
||
<div class="card editorCard heroEditor">
|
||
<div class="quickSetupBar twoCols">
|
||
<label class="field inlineField">Canvas size
|
||
<select id="assetSize">
|
||
<option value="8" selected>8×8</option>
|
||
<option value="16">16×16</option>
|
||
<option value="32">32×32</option>
|
||
<option value="64">64×64</option>
|
||
</select>
|
||
</label>
|
||
<label class="field inlineField">Role
|
||
<select id="assetCategory">
|
||
<option value="human">Human</option>
|
||
<option value="animal">Animal</option>
|
||
<option value="nature" selected>Nature</option>
|
||
<option value="building">Building</option>
|
||
<option value="ship">Ship</option>
|
||
<option value="other">Other</option>
|
||
</select>
|
||
</label>
|
||
</div>
|
||
|
||
<label class="field compactNameField inlineField">Name
|
||
<input id="assetName" type="text" maxlength="32" placeholder="Tiny bakery, round tree, island pup..." />
|
||
</label>
|
||
<div class="editorTop compactEditorTop">
|
||
<div id="sideSwitcher" class="sideSwitcher" hidden aria-label="Sprite direction">
|
||
<button id="editLeft" title="Left-facing sprite">◀ Left</button>
|
||
<button id="editRight" class="active" title="Right-facing sprite is the main/front direction">▶ Right</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="paintLayout">
|
||
<canvas id="paintCanvas" width="512" height="512" aria-label="Pixel editor"></canvas>
|
||
<div class="palettePanel">
|
||
<div id="paletteGrid" class="paletteGrid" aria-label="Color palette"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="toolRow editorToolRow">
|
||
<button id="toolBrush" class="tool toolBrush active" title="B">✎ Draw</button>
|
||
<button id="toolErase" class="tool toolErase" title="E">⌫ Erase</button>
|
||
<button id="toolFill" class="tool toolFill" title="F">▣ Fill</button>
|
||
<button id="toolPick" class="tool toolPick" title="I">◆ Pick</button>
|
||
</div>
|
||
<div class="toolRow editorHistoryRow">
|
||
<button id="undoPaint" class="tool toolUndo" type="button" disabled>↶ Undo</button>
|
||
<button id="redoPaint" class="tool toolRedo" type="button" disabled>↷ Redo</button>
|
||
</div>
|
||
<div id="advancedToolGroup" class="advancedToolGroup" hidden>
|
||
<div class="toolRow advancedDrawRow">
|
||
<button id="toolLine" class="tool toolLine advancedTool" title="L">/ Line</button>
|
||
<button id="toolRect" class="tool toolRect advancedTool" title="R">□ Rect</button>
|
||
<button id="toolSelect" class="tool toolSelect advancedTool" title="S">▥ Select</button>
|
||
<button id="toolDoor" class="tool toolDoor advancedTool buildingOnly">▤ Door</button>
|
||
<button id="clearPaint" class="tool danger advancedTool">× Clear</button>
|
||
</div>
|
||
<div class="toolRow advancedActionRow">
|
||
<button id="outlinePaint" class="tool toolOutline advancedTool" type="button">▧ Outline</button>
|
||
<button id="flipHorizontal" class="tool toolFlipH advancedTool" type="button">⇄ Flip H</button>
|
||
<button id="flipVertical" class="tool toolFlipV advancedTool" type="button">⇅ Flip V</button>
|
||
<button id="clearSelection" class="tool toolClearSel advancedTool" type="button" disabled>◇ Clear Sel</button>
|
||
</div>
|
||
</div>
|
||
<div class="advancedRow">
|
||
<button id="toggleAdvanced" class="tool toolAdvanced" type="button">+ Advanced</button>
|
||
<button id="toolLight" class="tool toolLight advancedOnly" hidden>✹ Light</button>
|
||
<button id="toolDepth" class="tool toolDepth advancedOnly" hidden>▨ Depth</button>
|
||
<button id="toolParticle" class="tool toolParticle advancedOnly" hidden>⁕ Particle</button>
|
||
<button id="depthHigh" class="tool advancedOnly" type="button" hidden>▲ High</button>
|
||
<button id="depthLow" class="tool advancedOnly" type="button" hidden>▼ Low</button>
|
||
<div id="particleDirectionWrap" class="particleControl particleControlGroup advancedOnly" hidden>
|
||
<label>Direction
|
||
<select id="particleDirection">
|
||
<option value="up" selected>↑ Up</option>
|
||
<option value="down">↓ Down</option>
|
||
<option value="left">← Left</option>
|
||
<option value="right">→ Right</option>
|
||
</select>
|
||
</label>
|
||
<small id="particleRangeStatus">Particle: paint cells like Light/Depth. Shift/right-click clears a cell.</small>
|
||
</div>
|
||
<span id="advancedHint" class="hint" hidden>Advanced tools stay below the basic draw row. Select tool: left-click selects, right-click clears selection. Particle paints emitter cells with palette color and direction. Shift/right-click clears a particle cell.</span>
|
||
</div>
|
||
<input id="paintColor" type="color" value="#6bd06b" hidden />
|
||
<div id="editHint" class="hint">Palette color is used for pixels, lights, particles, and depth marks. Shortcuts: B/E/F/I/L/R/S, Ctrl/Cmd+Z, arrows move a selection.</div>
|
||
</div>
|
||
|
||
<div class="card stack summonCard">
|
||
<div class="cardTitle">Finish</div>
|
||
<div class="actionRow finishActions">
|
||
<button id="saveAndPlace" class="primary bigPrimary">Save + Place on Island</button>
|
||
<button id="saveAsset" class="secondary">Save to Collection</button>
|
||
</div>
|
||
</div>
|
||
<div id="lineageNote" class="lineageNote"></div>
|
||
</section>
|
||
|
||
<section id="tab-library" class="tabPanel">
|
||
<div class="card stack">
|
||
<div class="libraryHead">
|
||
<div class="cardTitle">Collection</div>
|
||
<button id="showHiddenAssets" class="tool" type="button">Hidden</button>
|
||
</div>
|
||
<p class="hint">Your works are permanent here. The island is a temporary exhibition: place your own new or past works; remix only copies lineage and pixels to the canvas.</p>
|
||
<div id="hiddenAssetPanel" class="hiddenAssetPanel" hidden>
|
||
<div class="cardTitle">Hidden</div>
|
||
<div id="hiddenAssetList" class="assetList compactAssetList"></div>
|
||
</div>
|
||
<div id="likedCodex" class="likedCodex"></div>
|
||
<div id="assetList" class="assetList"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="tab-settings" class="tabPanel">
|
||
<div class="card stack">
|
||
<div class="cardTitle">Settings</div>
|
||
<p class="hint">Turn major visual systems on or off. Server decides the public 250 exhibition slots; this local cap only filters your view.</p>
|
||
<div class="toggleList">
|
||
<label class="checkRow"><input id="settingLights" type="checkbox" checked /> <span>Lights & glow</span></label>
|
||
<label class="checkRow"><input id="settingParticles" type="checkbox" checked /> <span>Particles & ambient FX</span></label>
|
||
<label class="checkRow"><input id="settingDayNight" type="checkbox" checked /> <span>Day / night cycle</span></label>
|
||
<label class="field displayLimitField">Island display cap
|
||
<input id="displayLimit" type="number" min="25" max="500" step="25" value="250" />
|
||
</label>
|
||
</div>
|
||
<div id="rotationStats" class="syncStats"></div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="tab-data" class="tabPanel devOnly">
|
||
<div class="card stack">
|
||
<div class="cardTitle">Local save</div>
|
||
<p class="hint">Works are durable collection works; island placements are temporary exhibition objects. Export JSON if you want to move or test a local world.</p>
|
||
<div class="actionRow wrap">
|
||
<button id="exportData">Export full JSON</button>
|
||
<button id="exportCompact">Export compact</button>
|
||
<button id="exportSnapshot">Export snapshot</button>
|
||
<button id="exportAssetBundle">Export asset bundle</button>
|
||
<button id="importData">Import JSON</button>
|
||
<button id="exportPng" type="button">Export PNG</button>
|
||
<button id="importPng" type="button">Import PNG</button>
|
||
<input id="pngImportInput" type="file" accept="image/png,image/*" hidden />
|
||
<button id="resetAll" class="danger">Reset all</button>
|
||
</div>
|
||
<div id="syncStats" class="syncStats"></div>
|
||
<textarea id="dataBox" rows="10" placeholder="Exported JSON appears here."></textarea>
|
||
</div>
|
||
|
||
<div class="card stack">
|
||
<div class="cardTitle">Phase 5 guardrails</div>
|
||
<p class="hint">Local pre-server checks for object volume, orphaned placements, terrain compatibility, hidden objects, and report logs.</p>
|
||
<div id="guardrailStats" class="syncStats"></div>
|
||
<div class="actionRow wrap">
|
||
<button id="validateWorld" type="button">Validate world</button>
|
||
<button id="exportModerationReport" type="button">Export moderation report</button>
|
||
<button id="clearReports" class="danger" type="button">Clear reports</button>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
</div>
|
||
</section>
|
||
|
||
<div id="selectionBubble" class="selectionBubble" hidden>
|
||
<div id="bubbleName" class="bubbleName">Name</div>
|
||
<div id="bubbleAuthor" class="bubbleAuthor">by Author</div>
|
||
<div id="bubbleRemixFrom" class="bubbleRemixFrom" hidden></div>
|
||
<div id="bubbleRemixCount" class="bubbleRemixCount">Remixed: 0</div>
|
||
<div class="bubbleVotes">
|
||
<button id="voteUp" type="button">▲</button>
|
||
<span id="voteScore">0</span>
|
||
<button id="voteDown" type="button">▼</button>
|
||
</div>
|
||
<div class="bubbleActions">
|
||
<button id="bubbleRemix" class="bubbleRemixPrimary" type="button">Remix</button>
|
||
<button id="bubbleTeleport" type="button" hidden>Teleport to source</button>
|
||
</div>
|
||
<button id="bubbleMenu" class="bubbleMenuButton" type="button">Menu</button>
|
||
<div id="bubbleMenuActions" class="bubbleMenuActions" hidden>
|
||
<button id="bubbleReport" type="button">Report</button>
|
||
<button id="bubbleHide" type="button" hidden>Hide</button>
|
||
</div>
|
||
</div>
|
||
<div id="toast" class="toast" hidden></div>
|
||
|
||
<div id="reportDialog" class="reportDialog" hidden>
|
||
<div class="reportCard" role="dialog" aria-modal="true" aria-labelledby="reportTitle">
|
||
<div id="reportTitle" class="cardTitle">Report object</div>
|
||
<p class="hint">Select a reason for reporting <strong id="reportObjectName">this object</strong>.</p>
|
||
<label class="field">Reason
|
||
<select id="reportReason">
|
||
<option value="inappropriate">Inappropriate / sexual</option>
|
||
<option value="harassment">Harassment / hate</option>
|
||
<option value="spam">Spam / flooding</option>
|
||
<option value="copyright">Copyright / stolen work</option>
|
||
<option value="other">Other</option>
|
||
</select>
|
||
</label>
|
||
<div class="actionRow wrap reportActions">
|
||
<button id="reportSubmit" class="danger" type="button">Report + Hide</button>
|
||
<button id="reportCancel" type="button">Cancel</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script src="./js/core-utils.js"></script>
|
||
<script src="./js/module-loader.js"></script>
|
||
<script src="./js/state-index.js"></script>
|
||
<script src="./js/rotation-policy.js"></script>
|
||
<script src="./js/lighting.js"></script>
|
||
<script src="./js/phase2-sync.js"></script>
|
||
<script src="./app.js"></script>
|
||
</body>
|
||
</html>
|