fix: enhance Docker build process and improve file explorer resizing functionality
parent
34dd56b789
commit
0d5d440a56
|
|
@ -3,7 +3,7 @@ name: Publish Docker Image
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "master" ]
|
branches: [ "master" ]
|
||||||
# You can also trigger this on release tags:
|
# Uncomment to also trigger on release tags:
|
||||||
# tags: [ 'v*.*.*' ]
|
# tags: [ 'v*.*.*' ]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
|
|
@ -23,6 +23,9 @@ jobs:
|
||||||
with:
|
with:
|
||||||
submodules: recursive
|
submodules: recursive
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
- name: Log in to GitHub Container Registry (GHCR)
|
- name: Log in to GitHub Container Registry (GHCR)
|
||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
|
|
@ -36,9 +39,6 @@ jobs:
|
||||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
- name: Setup Docker Buildx
|
|
||||||
uses: docker/setup-buildx-action@v3
|
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v5
|
uses: docker/metadata-action@v5
|
||||||
|
|
@ -48,10 +48,12 @@ jobs:
|
||||||
docker.io/${{ secrets.DOCKERHUB_USERNAME }}/velxio
|
docker.io/${{ secrets.DOCKERHUB_USERNAME }}/velxio
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
uses: docker/build-push-action@v5
|
uses: docker/build-push-action@v6
|
||||||
with:
|
with:
|
||||||
context: .
|
context: .
|
||||||
file: Dockerfile.standalone
|
file: Dockerfile.standalone
|
||||||
push: true
|
push: true
|
||||||
tags: ${{ steps.meta.outputs.tags }}
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
labels: ${{ steps.meta.outputs.labels }}
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
cache-from: type=gha
|
||||||
|
cache-to: type=gha,mode=max
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
# ---- Stage 1: Build frontend and wokwi-libs ----
|
# ---- Stage 1: Build frontend and wokwi-libs ----
|
||||||
FROM node:20 AS frontend-builder
|
FROM node:20-slim AS frontend-builder
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy package.json for metadata generation and common dependencies
|
# Copy root package.json (metadata/scripts)
|
||||||
COPY package.json .
|
COPY package.json .
|
||||||
RUN npm install
|
RUN npm install
|
||||||
|
|
||||||
|
|
@ -22,35 +22,34 @@ COPY wokwi-libs/wokwi-elements/ wokwi-libs/wokwi-elements/
|
||||||
WORKDIR /app/wokwi-libs/wokwi-elements
|
WORKDIR /app/wokwi-libs/wokwi-elements
|
||||||
RUN npm install && npm run build
|
RUN npm install && npm run build
|
||||||
|
|
||||||
# Build frontend and generate metadata
|
# Build frontend
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY frontend/ frontend/
|
COPY frontend/ frontend/
|
||||||
COPY scripts/ scripts/
|
COPY scripts/ scripts/
|
||||||
WORKDIR /app/frontend
|
WORKDIR /app/frontend
|
||||||
# Explicitly install frontend dependencies
|
RUN npm install && npm run build
|
||||||
RUN npm install
|
|
||||||
# Now run the build which includes metadata generation
|
|
||||||
RUN npm run build
|
|
||||||
|
|
||||||
|
|
||||||
# ---- Stage 2: Final Production Image ----
|
# ---- Stage 2: Final Production Image ----
|
||||||
FROM python:3.12-slim
|
FROM python:3.12-slim
|
||||||
|
|
||||||
# Install system dependencies, arduino-cli, and nginx
|
# Install system dependencies and nginx
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||||
curl \
|
curl \
|
||||||
ca-certificates \
|
ca-certificates \
|
||||||
nginx \
|
nginx \
|
||||||
&& curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh | sh \
|
|
||||||
&& mv bin/arduino-cli /usr/local/bin/ \
|
|
||||||
&& rm -rf bin \
|
|
||||||
&& apt-get purge -y curl \
|
|
||||||
&& apt-get autoremove -y \
|
|
||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/*
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Initialize arduino-cli and install AVR & RP2040 cores
|
# Install arduino-cli into /usr/local/bin directly (avoids touching /bin)
|
||||||
RUN arduino-cli core update-index \
|
RUN curl -fsSL https://raw.githubusercontent.com/arduino/arduino-cli/master/install.sh \
|
||||||
|
| BINDIR=/usr/local/bin sh
|
||||||
|
|
||||||
|
# Initialize arduino-cli config, add RP2040 board manager URL, then install cores
|
||||||
|
RUN arduino-cli config init \
|
||||||
|
&& arduino-cli config add board_manager.additional_urls \
|
||||||
|
https://github.com/earlephilhower/arduino-pico/releases/download/global/package_rp2040_index.json \
|
||||||
|
&& arduino-cli core update-index \
|
||||||
&& arduino-cli core install arduino:avr \
|
&& arduino-cli core install arduino:avr \
|
||||||
&& arduino-cli core install rp2040:rp2040
|
&& arduino-cli core install rp2040:rp2040
|
||||||
|
|
||||||
|
|
@ -73,7 +72,6 @@ COPY --from=frontend-builder /app/frontend/dist /usr/share/nginx/html
|
||||||
COPY deploy/entrypoint.sh /app/entrypoint.sh
|
COPY deploy/entrypoint.sh /app/entrypoint.sh
|
||||||
RUN chmod +x /app/entrypoint.sh
|
RUN chmod +x /app/entrypoint.sh
|
||||||
|
|
||||||
# Expose port 80 for the unified web server
|
|
||||||
EXPOSE 80
|
EXPOSE 80
|
||||||
|
|
||||||
CMD ["/app/entrypoint.sh"]
|
CMD ["/app/entrypoint.sh"]
|
||||||
|
|
|
||||||
|
|
@ -108,6 +108,22 @@ body {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Resize handle for the file explorer sidebar */
|
||||||
|
.explorer-resize-handle {
|
||||||
|
width: 4px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
cursor: col-resize;
|
||||||
|
background: #2a2a2a;
|
||||||
|
transition: background 0.15s;
|
||||||
|
position: relative;
|
||||||
|
z-index: 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.explorer-resize-handle:hover,
|
||||||
|
.explorer-resize-handle:active {
|
||||||
|
background: #007acc;
|
||||||
|
}
|
||||||
|
|
||||||
.editor-wrapper {
|
.editor-wrapper {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
.file-explorer {
|
.file-explorer {
|
||||||
width: 195px;
|
width: 100%;
|
||||||
flex-shrink: 0;
|
|
||||||
background: #252526;
|
background: #252526;
|
||||||
border-right: 1px solid #333;
|
border-right: 1px solid #333;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,10 @@ const BOTTOM_PANEL_MIN = 80;
|
||||||
const BOTTOM_PANEL_MAX = 600;
|
const BOTTOM_PANEL_MAX = 600;
|
||||||
const BOTTOM_PANEL_DEFAULT = 200;
|
const BOTTOM_PANEL_DEFAULT = 200;
|
||||||
|
|
||||||
|
const EXPLORER_MIN = 120;
|
||||||
|
const EXPLORER_MAX = 500;
|
||||||
|
const EXPLORER_DEFAULT = 210;
|
||||||
|
|
||||||
const resizeHandleStyle: React.CSSProperties = {
|
const resizeHandleStyle: React.CSSProperties = {
|
||||||
height: 5,
|
height: 5,
|
||||||
flexShrink: 0,
|
flexShrink: 0,
|
||||||
|
|
@ -42,6 +46,7 @@ export const EditorPage: React.FC = () => {
|
||||||
const [saveModalOpen, setSaveModalOpen] = useState(false);
|
const [saveModalOpen, setSaveModalOpen] = useState(false);
|
||||||
const [loginPromptOpen, setLoginPromptOpen] = useState(false);
|
const [loginPromptOpen, setLoginPromptOpen] = useState(false);
|
||||||
const [explorerOpen, setExplorerOpen] = useState(true);
|
const [explorerOpen, setExplorerOpen] = useState(true);
|
||||||
|
const [explorerWidth, setExplorerWidth] = useState(EXPLORER_DEFAULT);
|
||||||
const user = useAuthStore((s) => s.user);
|
const user = useAuthStore((s) => s.user);
|
||||||
|
|
||||||
const handleSaveClick = useCallback(() => {
|
const handleSaveClick = useCallback(() => {
|
||||||
|
|
@ -110,6 +115,27 @@ export const EditorPage: React.FC = () => {
|
||||||
document.addEventListener('mouseup', onUp);
|
document.addEventListener('mouseup', onUp);
|
||||||
}, [bottomPanelHeight]);
|
}, [bottomPanelHeight]);
|
||||||
|
|
||||||
|
const handleExplorerResizeMouseDown = useCallback((e: React.MouseEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const startX = e.clientX;
|
||||||
|
const startWidth = explorerWidth;
|
||||||
|
|
||||||
|
const onMove = (ev: MouseEvent) => {
|
||||||
|
const delta = ev.clientX - startX;
|
||||||
|
setExplorerWidth(Math.max(EXPLORER_MIN, Math.min(EXPLORER_MAX, startWidth + delta)));
|
||||||
|
};
|
||||||
|
const onUp = () => {
|
||||||
|
document.body.style.cursor = '';
|
||||||
|
document.body.style.userSelect = '';
|
||||||
|
document.removeEventListener('mousemove', onMove);
|
||||||
|
document.removeEventListener('mouseup', onUp);
|
||||||
|
};
|
||||||
|
document.body.style.cursor = 'col-resize';
|
||||||
|
document.body.style.userSelect = 'none';
|
||||||
|
document.addEventListener('mousemove', onMove);
|
||||||
|
document.addEventListener('mouseup', onUp);
|
||||||
|
}, [explorerWidth]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="app">
|
<div className="app">
|
||||||
<AppHeader />
|
<AppHeader />
|
||||||
|
|
@ -120,8 +146,15 @@ export const EditorPage: React.FC = () => {
|
||||||
className="editor-panel"
|
className="editor-panel"
|
||||||
style={{ width: `${editorWidthPct}%`, display: 'flex', flexDirection: 'row' }}
|
style={{ width: `${editorWidthPct}%`, display: 'flex', flexDirection: 'row' }}
|
||||||
>
|
>
|
||||||
{/* File explorer sidebar */}
|
{/* File explorer sidebar + resize handle */}
|
||||||
{explorerOpen && <FileExplorer onSaveClick={handleSaveClick} />}
|
{explorerOpen && (
|
||||||
|
<>
|
||||||
|
<div style={{ width: explorerWidth, flexShrink: 0, display: 'flex', overflow: 'hidden' }}>
|
||||||
|
<FileExplorer onSaveClick={handleSaveClick} />
|
||||||
|
</div>
|
||||||
|
<div className="explorer-resize-handle" onMouseDown={handleExplorerResizeMouseDown} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* Editor main area */}
|
{/* Editor main area */}
|
||||||
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minWidth: 0 }}>
|
<div style={{ flex: 1, display: 'flex', flexDirection: 'column', overflow: 'hidden', minWidth: 0 }}>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue