Skip to content

Commit 636fa78

Browse files
committed
Add island right-dock feature
1 parent c85e642 commit 636fa78

15 files changed

Lines changed: 1256 additions & 47 deletions

celstomp/celstomp-app.js

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -350,6 +350,8 @@
350350

351351

352352
function renderBounds() {
353+
fxctx.setTransform(1, 0, 0, 1, 0, 0);
354+
fxctx.clearRect(0, 0, fxCanvas.width, fxCanvas.height);
353355
setTransform(bctx);
354356
setTransform(fxctx);
355357
bctx.fillStyle = "#2a2f38";
@@ -358,6 +360,10 @@
358360
bctx.fillRect(0, 0, contentW, contentH);
359361
bctx.strokeRect(0, 0, contentW, contentH);
360362
drawRectSelectionOverlay(fxctx);
363+
drawLineToolPreview(fxctx);
364+
drawRectToolPreview(fxctx);
365+
drawGrid(fxctx);
366+
drawGuides(fxctx);
361367
}
362368

363369
function onionCompositeOperation() {
@@ -404,6 +410,12 @@
404410
function clearFx() {
405411
fxctx.setTransform(1, 0, 0, 1, 0, 0);
406412
fxctx.clearRect(0, 0, fxCanvas.width, fxCanvas.height);
413+
setTransform(fxctx);
414+
drawRectSelectionOverlay(fxctx);
415+
drawLineToolPreview(fxctx);
416+
drawRectToolPreview(fxctx);
417+
drawGrid(fxctx);
418+
drawGuides(fxctx);
407419
}
408420

409421
function wireBrushButtonRightClick() {
@@ -901,6 +913,95 @@
901913
safeSetValue(snapValue, v);
902914
updateHUD();
903915
});
916+
const gridToggle = $("tlGridBtn");
917+
const gridSnapToggle = $("tlGridSnapBtn");
918+
const rulersToggle = $("tlRulersBtn");
919+
const guideSnapToggle = $("tlGuideSnapBtn");
920+
const addHGuideBtn = $("addHGuideBtn");
921+
const addVGuideBtn = $("addVGuideBtn");
922+
const clearGuidesBtn = $("clearGuidesBtn");
923+
const guideModeHint = $("guideModeHint");
924+
const gridSizeInput = $("tlGridSize");
925+
926+
let guidePlacementMode = null;
927+
928+
if (gridToggle) {
929+
gridToggle.addEventListener("click", e => {
930+
gridEnabled = !gridEnabled;
931+
gridToggle.classList.toggle("active", gridEnabled);
932+
queueRenderAll();
933+
});
934+
}
935+
if (gridSizeInput) {
936+
gridSizeInput.addEventListener("change", e => {
937+
const v = Math.max(8, Math.min(128, parseInt(e.target.value) || 32));
938+
gridSize = v;
939+
e.target.value = v;
940+
queueRenderAll();
941+
});
942+
}
943+
if (gridSnapToggle) {
944+
gridSnapToggle.addEventListener("click", e => {
945+
gridSnap = !gridSnap;
946+
gridSnapToggle.classList.toggle("active", gridSnap);
947+
});
948+
}
949+
if (rulersToggle) {
950+
rulersToggle.addEventListener("click", e => {
951+
rulersEnabled = !rulersEnabled;
952+
rulersToggle.classList.toggle("active", rulersEnabled);
953+
queueRenderAll();
954+
});
955+
}
956+
if (guideSnapToggle) {
957+
guideSnapToggle.addEventListener("click", e => {
958+
guideSnap = !guideSnap;
959+
guideSnapToggle.classList.toggle("active", guideSnap);
960+
});
961+
}
962+
function setGuidePlacementMode(mode) {
963+
guidePlacementMode = mode;
964+
if (addHGuideBtn) addHGuideBtn.classList.toggle("active", mode === "horizontal");
965+
if (addVGuideBtn) addVGuideBtn.classList.toggle("active", mode === "vertical");
966+
if (guideModeHint) {
967+
guideModeHint.hidden = !mode;
968+
guideModeHint.textContent = mode === "horizontal" ? "Click Canvas To Place H Guide" : mode === "vertical" ? "Click Canvas To Place V Guide" : "";
969+
}
970+
if (!mode) {
971+
document.body.classList.remove("guide-place-mode");
972+
document.body.classList.remove("guide-place-h");
973+
document.body.classList.remove("guide-place-v");
974+
} else {
975+
document.body.classList.add("guide-place-mode");
976+
document.body.classList.toggle("guide-place-h", mode === "horizontal");
977+
document.body.classList.toggle("guide-place-v", mode === "vertical");
978+
}
979+
}
980+
if (addHGuideBtn) {
981+
addHGuideBtn.addEventListener("click", e => {
982+
if (guidePlacementMode === "horizontal") {
983+
setGuidePlacementMode(null);
984+
} else {
985+
setGuidePlacementMode("horizontal");
986+
}
987+
});
988+
}
989+
if (addVGuideBtn) {
990+
addVGuideBtn.addEventListener("click", e => {
991+
if (guidePlacementMode === "vertical") {
992+
setGuidePlacementMode(null);
993+
} else {
994+
setGuidePlacementMode("vertical");
995+
}
996+
});
997+
}
998+
if (clearGuidesBtn) {
999+
clearGuidesBtn.addEventListener("click", () => {
1000+
guides = [];
1001+
queueRenderAll();
1002+
});
1003+
}
1004+
window.__celstompSetGuidePlacementMode = setGuidePlacementMode;
9041005
function rebuildTimelineKeepFrame() {
9051006
const cur = currentFrame;
9061007
buildTimeline();

celstomp/css/components/island.css

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,50 @@
5454
cursor: grabbing;
5555
}
5656

57+
.islandDock.drag-lock-candidate {
58+
outline: 2px solid rgba(92, 179, 255, 0.6);
59+
outline-offset: -2px;
60+
}
61+
62+
.islandDock.right-locked {
63+
left: auto !important;
64+
right: var(--dock-gap);
65+
top: calc(var(--header-h) + var(--dock-gap));
66+
bottom: calc(var(--timeline-h) + var(--dock-gap-bottom));
67+
height: auto !important;
68+
width: min(320px, 36vw);
69+
}
70+
71+
.islandDock.right-locked .islandHeader {
72+
background: rgba(92, 179, 255, 0.12);
73+
border-bottom-color: rgba(92, 179, 255, 0.35);
74+
}
75+
76+
.islandLockHint {
77+
position: fixed;
78+
top: calc(var(--header-h) + 14px);
79+
right: 10px;
80+
width: 180px;
81+
height: calc(100vh - var(--header-h) - var(--timeline-h) - 24px);
82+
border: 2px dashed rgba(92, 179, 255, 0.32);
83+
border-radius: 10px;
84+
background: rgba(92, 179, 255, 0.08);
85+
color: rgba(180, 225, 255, 0.9);
86+
display: none;
87+
align-items: center;
88+
justify-content: center;
89+
text-align: center;
90+
font-size: 11px;
91+
letter-spacing: 0.08em;
92+
text-transform: uppercase;
93+
z-index: 39;
94+
pointer-events: none;
95+
}
96+
97+
.islandLockHint.active {
98+
display: flex;
99+
}
100+
57101
.islandTitle{
58102
font-size: 12px;
59103
letter-spacing: 0.08em;
@@ -222,6 +266,30 @@
222266
overflow-x: hidden;
223267
}
224268

269+
.islandSideBody {
270+
scrollbar-width: thin;
271+
scrollbar-color: rgba(255,255,255,0.24) rgba(0,0,0,0.22);
272+
}
273+
274+
.islandSideBody::-webkit-scrollbar {
275+
width: 10px;
276+
}
277+
278+
.islandSideBody::-webkit-scrollbar-track {
279+
background: rgba(0,0,0,0.24);
280+
border-radius: 999px;
281+
}
282+
283+
.islandSideBody::-webkit-scrollbar-thumb {
284+
background: rgba(255,255,255,0.24);
285+
border-radius: 999px;
286+
border: 2px solid rgba(0,0,0,0.22);
287+
}
288+
289+
.islandSideBody::-webkit-scrollbar-thumb:hover {
290+
background: rgba(255,255,255,0.34);
291+
}
292+
225293
.islandSideGrid{
226294
display: flex;
227295
flex-direction: column;

celstomp/css/components/layers.css

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,30 @@ body.swatch-reordering{
136136
border-radius: 0px;
137137
}
138138

139+
#islandLayersSlot {
140+
scrollbar-width: thin;
141+
scrollbar-color: rgba(255,255,255,0.24) rgba(0,0,0,0.22);
142+
}
143+
144+
#islandLayersSlot::-webkit-scrollbar {
145+
width: 10px;
146+
}
147+
148+
#islandLayersSlot::-webkit-scrollbar-track {
149+
background: rgba(0,0,0,0.24);
150+
border-radius: 999px;
151+
}
152+
153+
#islandLayersSlot::-webkit-scrollbar-thumb {
154+
background: rgba(255,255,255,0.24);
155+
border-radius: 999px;
156+
border: 2px solid rgba(0,0,0,0.22);
157+
}
158+
159+
#islandLayersSlot::-webkit-scrollbar-thumb:hover {
160+
background: rgba(255,255,255,0.34);
161+
}
162+
139163
#islandLayersSlot #layerSeg{
140164
display: flex !important;
141165
flex-direction: column !important;

celstomp/css/components/overlays.css

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,124 @@
120120
opacity: .95;
121121
}
122122

123+
.canvasTextEntry {
124+
position: fixed;
125+
inset: 0;
126+
display: none;
127+
align-items: center;
128+
justify-content: center;
129+
background: rgba(0, 0, 0, 0.32);
130+
z-index: 9400;
131+
}
132+
133+
.canvasTextEntry.open {
134+
display: flex;
135+
}
136+
137+
.canvasTextEntryCard {
138+
width: min(420px, calc(100vw - 24px));
139+
border: 1px solid rgba(255, 255, 255, 0.16);
140+
background: rgba(16, 20, 28, 0.96);
141+
border-radius: 10px;
142+
box-shadow: 0 16px 42px rgba(0, 0, 0, 0.45);
143+
padding: 12px;
144+
display: grid;
145+
gap: 10px;
146+
}
147+
148+
.canvasTextEntryLabel {
149+
font-size: 12px;
150+
color: #c7d0db;
151+
letter-spacing: 0.04em;
152+
text-transform: uppercase;
153+
}
154+
155+
.canvasTextEntryInput {
156+
width: 100%;
157+
min-height: 36px;
158+
border: 1px solid rgba(255, 255, 255, 0.14);
159+
background: rgba(0, 0, 0, 0.28);
160+
color: #e2e8f0;
161+
border-radius: 8px;
162+
padding: 8px 10px;
163+
}
164+
165+
.canvasTextEntryInput:focus {
166+
outline: none;
167+
border-color: rgba(92, 179, 255, 0.8);
168+
box-shadow: 0 0 0 2px rgba(92, 179, 255, 0.2);
169+
}
170+
171+
.canvasTextEntryOptions {
172+
display: grid;
173+
grid-template-columns: repeat(2, minmax(0, 1fr));
174+
gap: 8px;
175+
}
176+
177+
.canvasTextEntryOpt {
178+
display: grid;
179+
gap: 4px;
180+
}
181+
182+
.canvasTextEntryOpt > span {
183+
font-size: 11px;
184+
color: #b7c3d2;
185+
letter-spacing: 0.03em;
186+
}
187+
188+
.canvasTextEntrySelect,
189+
.canvasTextEntryNum {
190+
width: 100%;
191+
min-height: 32px;
192+
border: 1px solid rgba(255, 255, 255, 0.14);
193+
background: rgba(0, 0, 0, 0.28);
194+
color: #e2e8f0;
195+
border-radius: 8px;
196+
padding: 6px 8px;
197+
}
198+
199+
.canvasTextEntrySelect:focus,
200+
.canvasTextEntryNum:focus {
201+
outline: none;
202+
border-color: rgba(92, 179, 255, 0.8);
203+
box-shadow: 0 0 0 2px rgba(92, 179, 255, 0.2);
204+
}
205+
206+
.canvasTextEntryOptCheck {
207+
align-items: center;
208+
grid-template-columns: auto 1fr;
209+
gap: 8px;
210+
border: 1px solid rgba(255, 255, 255, 0.12);
211+
border-radius: 8px;
212+
background: rgba(255, 255, 255, 0.04);
213+
padding: 6px 8px;
214+
}
215+
216+
.canvasTextEntryOptCheck input {
217+
margin: 0;
218+
}
219+
220+
.canvasTextEntryActions {
221+
display: flex;
222+
justify-content: flex-end;
223+
gap: 8px;
224+
}
225+
226+
.canvasTextEntryBtn {
227+
min-height: 34px;
228+
padding: 0 12px;
229+
border-radius: 8px;
230+
border: 1px solid rgba(255, 255, 255, 0.14);
231+
background: rgba(255, 255, 255, 0.06);
232+
color: #d0d8e2;
233+
}
234+
235+
.canvasTextEntryBtnPrimary {
236+
border-color: rgba(92, 179, 255, 0.6);
237+
background: rgba(92, 179, 255, 0.18);
238+
color: #9fd5ff;
239+
}
240+
123241
.infoList{
124242
margin: 0;
125243
padding-left: 18px;

0 commit comments

Comments
 (0)