2026-06-01 18:27:25 +09:00
<!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 >
2026-06-02 21:43:57 +09:00
< button id = "openEditor" class = "drawDockButton" type = "button" title = "Open Pixel Studio" > < span > ✎ DRAW< / span > < span id = "drawQuotaBadge" class = "quotaBadge drawQuotaBadge" aria-live = "polite" > < / span > < / button >
2026-06-02 02:35:22 +09:00
< 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 >
2026-06-02 16:26:44 +09:00
< span > Check how this work looks on the island. Works are permanent in Collection; island placement is temporary exhibition.< / span >
2026-06-02 02:35:22 +09:00
< / div >
2026-06-01 18:27:25 +09:00
2026-06-02 16:26:44 +09:00
< header class = "hud topHud islandTopBar" >
< div class = "brandBlock" >
2026-06-01 18:27:25 +09:00
< div class = "logo" > Pixel Island< / div >
2026-06-02 16:26:44 +09:00
< div class = "subline" > A rotating island exhibition for tiny pixel works.< / div >
2026-06-01 18:27:25 +09:00
< / div >
2026-06-02 16:26:44 +09:00
< 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 >
2026-06-02 21:43:57 +09:00
< small id = "accountNote" > Save + Place creates a local account for island publishing.< / small >
2026-06-01 18:27:25 +09:00
< / div >
< / header >
2026-06-02 16:26:44 +09:00
< 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 >
2026-06-01 18:27:25 +09:00
< 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 >
2026-06-01 21:17:09 +09:00
< button id = "closeEditor" class = "ghostButton" title = "Close" > × < / button >
2026-06-01 18:27:25 +09:00
< / div >
< nav class = "tabs" aria-label = "Studio tabs" >
2026-06-02 16:26:44 +09:00
< button class = "tab active" data-tab = "draw" > Create< / button >
< button class = "tab" data-tab = "library" > Collection< / button >
2026-06-02 17:19:36 +09:00
< button class = "tab" data-tab = "settings" > Settings< / button >
2026-06-02 16:26:44 +09:00
< button class = "tab devOnly" data-tab = "data" > Data< / button >
2026-06-01 18:27:25 +09:00
< / nav >
< div class = "drawerBody" >
< section id = "tab-draw" class = "tabPanel active" >
< div class = "card editorCard heroEditor" >
2026-06-02 21:43:57 +09:00
< div class = "creationMetaRow" aria-label = "Work setup" >
< input id = "assetName" class = "metaInput metaName" type = "text" maxlength = "32" placeholder = "Name" aria-label = "Name" / >
< input id = "assetHeight" class = "metaInput metaDimension" type = "number" min = "1" max = "64" step = "1" value = "8" placeholder = "H" aria-label = "Height" / >
< span class = "dimensionSeparator" aria-hidden = "true" > × < / span >
< input id = "assetWidth" class = "metaInput metaDimension" type = "number" min = "1" max = "64" step = "1" value = "8" placeholder = "W" aria-label = "Width" / >
< select id = "assetCategory" class = "metaInput metaRole" aria-label = "Role" required >
< option value = "" disabled selected > Role< / option >
< option value = "human" > Human< / option >
< option value = "animal" > Animal< / option >
< option value = "bird" > Bird< / option >
< option value = "nature" > Nature< / option >
< option value = "building" > Building< / option >
< option value = "ship" > Ship< / option >
< option value = "other" > Other< / option >
< / select >
2026-06-01 18:27:25 +09:00
< / div >
< div class = "editorTop compactEditorTop" >
< div id = "sideSwitcher" class = "sideSwitcher" hidden aria-label = "Sprite direction" >
2026-06-01 21:17:09 +09:00
< 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 >
2026-06-01 18:27:25 +09:00
< / div >
< / div >
< div class = "paintLayout" >
2026-06-02 23:45:38 +09:00
< div class = "paintMainColumn" >
< canvas id = "paintCanvas" width = "512" height = "512" aria-label = "Pixel editor" > < / canvas >
< div class = "toolRow editorToolRow basicEditorTools" >
< 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 editorUtilityRow" >
< button id = "toggleAdvanced" class = "tool toolAdvanced" type = "button" > + Advanced< / button >
< button id = "undoPaint" class = "tool toolUndo" type = "button" disabled > ↶ Undo< / button >
< button id = "redoPaint" class = "tool toolRedo" type = "button" disabled > ↷ Redo< / button >
< / div >
< / div >
2026-06-01 18:27:25 +09:00
< div class = "palettePanel" >
< div id = "paletteGrid" class = "paletteGrid" aria-label = "Color palette" > < / div >
< / div >
< / div >
2026-06-02 23:45:38 +09:00
< div class = "advancedPanel" >
< div class = "advancedPanelHeader" > Advanced settings< / 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 >
2026-06-02 16:26:44 +09:00
< / div >
2026-06-02 23:45:38 +09:00
< div class = "advancedRow extraAdvancedRow" >
< button id = "toolLight" class = "tool toolLight advancedOnly" hidden > Light< / button >
< button id = "toolParticle" class = "tool toolParticle advancedOnly" hidden > Particle< / button >
< button id = "toolDepth" class = "tool toolDepth advancedOnly" hidden > Depth< / 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 >
2026-06-02 16:26:44 +09:00
< / div >
2026-06-02 23:45:38 +09:00
< span id = "advancedHint" class = "hint" hidden > Advanced reveals line, rectangle, selection, flip, light, depth, and particles.< / span >
< 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 >
2026-06-01 18:27:25 +09:00
< / div >
< / div >
< div class = "card stack summonCard" >
< div class = "cardTitle" > Finish< / div >
< div class = "actionRow finishActions" >
2026-06-02 21:43:57 +09:00
< button id = "saveAndPlace" class = "primary bigPrimary" > < span > Save + Place on Island< / span > < span id = "finishQuotaBadge" class = "quotaBadge finishQuotaBadge" aria-live = "polite" > < / span > < / button >
2026-06-02 16:26:44 +09:00
< button id = "saveAsset" class = "secondary" > Save to Collection< / button >
2026-06-01 18:27:25 +09:00
< / div >
< / div >
< div id = "lineageNote" class = "lineageNote" > < / div >
< / section >
< section id = "tab-library" class = "tabPanel" >
< div class = "card stack" >
2026-06-01 21:17:09 +09:00
< div class = "libraryHead" >
2026-06-02 16:26:44 +09:00
< div class = "cardTitle" > Collection< / div >
2026-06-01 21:17:09 +09:00
< button id = "showHiddenAssets" class = "tool" type = "button" > Hidden< / button >
< / div >
2026-06-02 02:35:22 +09:00
< 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 >
2026-06-01 21:17:09 +09:00
< div id = "hiddenAssetPanel" class = "hiddenAssetPanel" hidden >
2026-06-02 00:05:03 +09:00
< div class = "cardTitle" > Hidden< / div >
2026-06-01 21:17:09 +09:00
< div id = "hiddenAssetList" class = "assetList compactAssetList" > < / div >
< / div >
2026-06-02 02:35:22 +09:00
< div id = "likedCodex" class = "likedCodex" > < / div >
2026-06-01 18:27:25 +09:00
< div id = "assetList" class = "assetList" > < / div >
< / div >
< / section >
2026-06-02 17:19:36 +09:00
< 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 >
2026-06-02 16:26:44 +09:00
< section id = "tab-data" class = "tabPanel devOnly" >
2026-06-01 18:27:25 +09:00
< div class = "card stack" >
< div class = "cardTitle" > Local save< / div >
2026-06-02 16:26:44 +09:00
< 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 >
2026-06-01 18:27:25 +09:00
< div class = "actionRow wrap" >
2026-06-01 22:48:15 +09:00
< 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 >
2026-06-01 18:27:25 +09:00
< button id = "importData" > Import JSON< / button >
2026-06-02 02:35:22 +09:00
< 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 / >
2026-06-01 18:27:25 +09:00
< button id = "resetAll" class = "danger" > Reset all< / button >
< / div >
2026-06-01 22:48:15 +09:00
< div id = "syncStats" class = "syncStats" > < / div >
2026-06-01 18:27:25 +09:00
< textarea id = "dataBox" rows = "10" placeholder = "Exported JSON appears here." > < / textarea >
< / div >
2026-06-02 00:05:03 +09:00
< 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 >
2026-06-01 18:27:25 +09:00
< / 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" >
2026-06-01 21:17:09 +09:00
< button id = "voteUp" type = "button" > ▲< / button >
2026-06-01 18:27:25 +09:00
< span id = "voteScore" > 0< / span >
2026-06-01 21:17:09 +09:00
< button id = "voteDown" type = "button" > ▼< / button >
2026-06-01 18:27:25 +09:00
< / div >
< div class = "bubbleActions" >
2026-06-02 16:26:44 +09:00
< 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 >
2026-06-02 00:05:03 +09:00
< button id = "bubbleReport" type = "button" > Report< / button >
2026-06-01 18:27:25 +09:00
< button id = "bubbleHide" type = "button" hidden > Hide< / button >
< / div >
< / div >
< div id = "toast" class = "toast" hidden > < / div >
2026-06-02 00:05:03 +09:00
< 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 >
2026-06-01 18:27:25 +09:00
< / div >
2026-06-02 17:19:36 +09:00
< 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 >
2026-06-01 22:48:15 +09:00
< script src = "./js/phase2-sync.js" > < / script >
2026-06-01 18:27:25 +09:00
< script src = "./app.js" > < / script >
< / body >
< / html >