Skip to content

Commit d7deba4

Browse files
authored
Merge pull request #85 from AnuKritiW/fix/responsive-layout-resize
Responsive Layout Implementation
2 parents 941de3e + 1fa6643 commit d7deba4

File tree

4 files changed

+135
-9
lines changed

4 files changed

+135
-9
lines changed

src/components/EditorPanel.vue

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@
1717
:value="localCode"
1818
@change="onCodeChange"
1919
@mount="onEditorMount"
20-
style="height: 100%; width: 100%;"
20+
style="flex: 1; min-height: 0;"
2121
:options="editorOptions"
2222
/>
23+
<!-- flex 1 above ensures editor takes space within the container while still respecting footer element-->
2324
<template #footer>
2425
<n-button @click="runShader" block>Run Shader</n-button>
2526
</template>
@@ -145,7 +146,42 @@ onBeforeUnmount(() => {
145146
</script>
146147

147148
<style scoped>
149+
.panel {
150+
background-color: #1a1a1a;
151+
display: flex;
152+
flex-direction: column;
153+
height: 100%;
154+
overflow: hidden;
155+
}
156+
148157
.editor {
149-
grid-row: 1 / span 2; /* Span both rows */
158+
grid-row: 1;
159+
grid-column: 1;
160+
}
161+
162+
/* Ensure the card structure maintains proper layout */
163+
.editor :deep(.n-card) {
164+
height: 100%;
165+
display: flex;
166+
flex-direction: column;
167+
}
168+
169+
.editor :deep(.n-card__content) {
170+
flex: 1;
171+
display: flex;
172+
flex-direction: column;
173+
min-height: 0; /* Allow Monaco to shrink */
174+
overflow: hidden;
175+
}
176+
177+
/* Footer should never shrink; we need Run Shader button to always be visible */
178+
.editor :deep(.n-card__footer) {
179+
flex-shrink: 0;
180+
}
181+
182+
/* Monaco Editor container should flex within the card content */
183+
.editor :deep(.monaco-editor) {
184+
flex: 1;
185+
min-height: 0;
150186
}
151187
</style>

src/components/ResourcesPanel.vue

Lines changed: 56 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ const showMeshModalProxy = computed({
5454
size="small"
5555
embedded
5656
class="resources-card"
57+
style="grid-row: 2; grid-column: 1;"
5758
>
5859
<n-tabs type="segment" animated>
5960
<n-tab-pane name="textures" tab="Textures">
@@ -80,7 +81,7 @@ const showMeshModalProxy = computed({
8081
</n-tab-pane>
8182

8283
<n-tab-pane name="mesh" tab="Mesh">
83-
<div style="display:inline-flex; align-items:center; gap:8px; margin-top:8px;">
84+
<div class="mesh-buttons-row">
8485
<n-button @click="$emit('openMeshModal')">Select / Upload .OBJ Mesh</n-button>
8586
<n-button
8687
:disabled="!uploadedMesh.name"
@@ -92,7 +93,7 @@ const showMeshModalProxy = computed({
9293
</n-button>
9394
</div>
9495

95-
<div style="margin-top:12px; display: flex; align-items: center; gap: 12px;">
96+
<div class="mesh-actions-row">
9697
<n-button ghost size="small" @click="$emit('copyStarterCode')" :render-icon="renderClipboardIcon">
9798
Copy Starter Shader
9899
</n-button>
@@ -116,3 +117,56 @@ const showMeshModalProxy = computed({
116117
/>
117118
</n-card>
118119
</template>
120+
121+
<style scoped>
122+
.resources-card {
123+
height: 100%;
124+
display: flex;
125+
flex-direction: column;
126+
}
127+
128+
.resources-card :deep(.n-card) {
129+
height: 100%;
130+
display: flex;
131+
flex-direction: column;
132+
}
133+
134+
.resources-card :deep(.n-card__content) {
135+
flex: 1;
136+
overflow: hidden;
137+
}
138+
139+
/* Responsive mesh buttons */
140+
.mesh-buttons-row {
141+
display: flex;
142+
flex-wrap: wrap;
143+
align-items: center;
144+
gap: 8px;
145+
margin-top: 8px;
146+
}
147+
148+
.mesh-buttons-row .n-button {
149+
flex: 0 1 auto; /* don't grow, but can shrink */
150+
min-width: 0;
151+
max-width: 100%;
152+
white-space: nowrap;
153+
overflow: hidden;
154+
text-overflow: ellipsis;
155+
}
156+
157+
.mesh-actions-row {
158+
margin-top: 12px;
159+
display: flex;
160+
align-items: center;
161+
gap: 12px;
162+
}
163+
164+
.mesh-actions-row .n-button {
165+
flex: 0 1 auto; /* don't grow, but can shrink */
166+
min-width: 0;
167+
max-width: 100%;
168+
white-space: nowrap;
169+
overflow: hidden;
170+
text-overflow: ellipsis;
171+
}
172+
</style>

src/styles/app.css

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ html, body, #app, .n-layout {
22
margin: 0;
33
padding: 0;
44
height: 100%;
5+
background-color: #101014; /* Ensure dark background extends beyond viewport */
56
}
67

78
/* Root grid with two rows: header + panels */
@@ -13,26 +14,39 @@ html, body, #app, .n-layout {
1314

1415
.grid-container {
1516
display: grid;
16-
grid-template-columns: 1fr 1fr;
17-
grid-template-rows: 3fr 1fr;
17+
/* grid-template-columns: 1fr 1fr; */
18+
/* grid-template-rows: 3fr 1fr; */
19+
grid-template-columns: minmax(300px, 1fr) minmax(100px, 1fr);
20+
grid-template-rows: minmax(175px, 3fr) minmax(210px, 1fr);
1821
gap: 12px;
1922
padding: 12px;
2023
background-color: #101014;
2124
box-sizing: border-box;
2225
height: 100%; /* fill the minmax row */
26+
min-height: 0; /* Allow grid items to shrink below their content size */
27+
}
28+
29+
/* Ensure all grid items have proper height constraints */
30+
/* >* is a CSS child combinator selector. i.e. all direct children are selected */
31+
.grid-container > * {
32+
min-height: 0;
33+
height: 100%;
2334
}
2435

2536
/* Container for texture buttons inside 'Textures' tab */
2637
.texture-buttons {
2738
display: flex;
2839
flex-direction: row;
40+
flex-wrap: wrap;
2941
gap: 8px;
3042
padding: 8px;
3143
}
3244

3345
/* Base button layout: sizing and appearance */
3446
.texture-buttons .n-button {
35-
flex: 1;
47+
flex: 1 1 0; /* Equal flex basis ensures uniform sizing */
48+
min-width: 80px;
49+
max-width: calc(25% - 6px); /* Never exceed 1/4 of container width minus gap adjustment */
3650
aspect-ratio: 1 / .4; /* Makes them square */
3751
height: auto; /* Let height be defined by width via aspect ratio */
3852
padding: 0; /* Removes inner padding for tighter fit */

src/webgpu/renderer.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,20 @@ import { createUniforms } from './uniforms';
66
import { createPipeline } from './pipeline';
77

88
let currentFrameId: number | null = null;
9+
let currentCleanup: (() => void) | null = null;
910

1011
// Cancel any active render loop
1112
export function cancelCurrentRenderLoop() {
1213
if (currentFrameId !== null) {
1314
cancelAnimationFrame(currentFrameId);
1415
currentFrameId = null;
1516
}
17+
18+
// ensure ResizeObserver is disconnected before a new one is started
19+
if (currentCleanup) {
20+
currentCleanup();
21+
currentCleanup = null;
22+
}
1623
}
1724

1825
function runRenderPass(
@@ -102,8 +109,23 @@ export async function initWebGPU(
102109

103110
const { context, format } = configureCanvasContext(canvas, device);
104111

105-
canvas.width = canvas.clientWidth * window.devicePixelRatio;
106-
canvas.height = canvas.clientHeight * window.devicePixelRatio;
112+
// Set initial canvas size with device pixel ratio
113+
const updateCanvasSize = () => {
114+
const dpr = typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1; // fallback to 1 for tests
115+
canvas.width = canvas.clientWidth * dpr;
116+
canvas.height = canvas.clientHeight * dpr;
117+
};
118+
119+
updateCanvasSize();
120+
121+
// Add resize observer for responsive canvas resizing (only in browser environment)
122+
let cleanup: (() => void) | null = null;
123+
if (typeof window !== 'undefined' && 'ResizeObserver' in window) {
124+
const resizeObserver = new ResizeObserver(updateCanvasSize);
125+
resizeObserver.observe(canvas);
126+
cleanup = () => resizeObserver.disconnect();
127+
currentCleanup = cleanup;
128+
}
107129

108130
const mouse = { x: 0, y: 0, down: false };
109131

0 commit comments

Comments
 (0)