Add fullscreen & fix notes
This commit is contained in:
@@ -15,7 +15,13 @@
|
|||||||
"build:unpack": "npm run build && electron-builder --dir",
|
"build:unpack": "npm run build && electron-builder --dir",
|
||||||
"build:win": "npm run build && electron-builder --win",
|
"build:win": "npm run build && electron-builder --win",
|
||||||
"build:mac": "npm run build && electron-builder --mac",
|
"build:mac": "npm run build && electron-builder --mac",
|
||||||
"build:linux": "npm run build && electron-builder --linux"
|
"build:linux": "npm run build && electron-builder --linux",
|
||||||
|
"fullscreen": "KIOSK=false npm run start",
|
||||||
|
"kiosk": "KIOSK=true npm run start",
|
||||||
|
"electron": "electron",
|
||||||
|
"electron:kiosk": "electron . --kiosk",
|
||||||
|
"electron:fullscreen": "electron .",
|
||||||
|
"test:fullscreen": "node test-fullscreen.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@electron-toolkit/preload": "^3.0.1",
|
"@electron-toolkit/preload": "^3.0.1",
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
import { app, shell, BrowserWindow, ipcMain } from 'electron'
|
import { app, shell, BrowserWindow, ipcMain, screen } from 'electron'
|
||||||
import { join } from 'path'
|
import { join } from 'path'
|
||||||
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
|
||||||
import icon from '../../resources/icon.png?asset'
|
import icon from '../../resources/icon.png?asset'
|
||||||
@@ -6,22 +6,79 @@ import icon from '../../resources/icon.png?asset'
|
|||||||
let mainWindow
|
let mainWindow
|
||||||
|
|
||||||
const createWindow = () => {
|
const createWindow = () => {
|
||||||
|
// Get the primary display dimensions
|
||||||
|
const primaryDisplay = screen.getPrimaryDisplay()
|
||||||
|
const { width, height } = primaryDisplay.workAreaSize
|
||||||
|
|
||||||
|
// Check for kiosk mode - enable if in production, has kiosk flag, or KIOSK env var
|
||||||
|
const isKioskMode = process.env.NODE_ENV === 'production' ||
|
||||||
|
process.argv.includes('--kiosk') ||
|
||||||
|
process.env.KIOSK === 'true'
|
||||||
|
|
||||||
mainWindow = new BrowserWindow({
|
mainWindow = new BrowserWindow({
|
||||||
width: 900,
|
width: width,
|
||||||
height: 670,
|
height: height,
|
||||||
show: false,
|
show: false,
|
||||||
autoHideMenuBar: true,
|
autoHideMenuBar: true,
|
||||||
...(process.platform === 'linux' ? { icon } : {}),
|
fullscreen: true,
|
||||||
|
kiosk: isKioskMode,
|
||||||
|
frame: false, // Remove window frame for true fullscreen experience
|
||||||
webPreferences: {
|
webPreferences: {
|
||||||
preload: join(__dirname, '../preload/index.js'),
|
preload: join(__dirname, '../preload/index.js'),
|
||||||
sandbox: false,
|
sandbox: false,
|
||||||
nodeIntegration: false,
|
nodeIntegration: false,
|
||||||
contextIsolation: true
|
contextIsolation: true,
|
||||||
|
webSecurity: false // Allow loading external resources
|
||||||
|
},
|
||||||
|
...(process.platform === 'linux' ? { icon } : {})
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable zoom and context menu for touch interface
|
||||||
|
mainWindow.webContents.on('before-input-event', (event, input) => {
|
||||||
|
// Prevent zooming with Ctrl++ and Ctrl+-
|
||||||
|
if (input.control && (input.key === '+' || input.key === '-' || input.key === '=' || input.key === '0')) {
|
||||||
|
event.preventDefault()
|
||||||
}
|
}
|
||||||
|
// Prevent F11 fullscreen toggle in kiosk mode
|
||||||
|
if (isKioskMode && input.key === 'F11') {
|
||||||
|
event.preventDefault()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Disable right-click context menu
|
||||||
|
mainWindow.webContents.on('context-menu', (event) => {
|
||||||
|
event.preventDefault()
|
||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.on('ready-to-show', () => {
|
mainWindow.on('ready-to-show', () => {
|
||||||
mainWindow.show()
|
mainWindow.show()
|
||||||
|
// Ensure fullscreen mode is enabled
|
||||||
|
if (!mainWindow.isFullScreen()) {
|
||||||
|
mainWindow.setFullScreen(true)
|
||||||
|
}
|
||||||
|
// Focus the window
|
||||||
|
mainWindow.focus()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Prevent accidentally leaving fullscreen in kiosk mode
|
||||||
|
mainWindow.on('leave-full-screen', () => {
|
||||||
|
if (isKioskMode) {
|
||||||
|
mainWindow.setFullScreen(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handle window focus events to maintain fullscreen in kiosk mode
|
||||||
|
mainWindow.on('focus', () => {
|
||||||
|
if (isKioskMode && !mainWindow.isFullScreen()) {
|
||||||
|
mainWindow.setFullScreen(true)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Prevent window from being minimized in kiosk mode
|
||||||
|
mainWindow.on('minimize', () => {
|
||||||
|
if (isKioskMode) {
|
||||||
|
mainWindow.restore()
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mainWindow.webContents.setWindowOpenHandler((details) => {
|
mainWindow.webContents.setWindowOpenHandler((details) => {
|
||||||
|
@@ -4,11 +4,22 @@
|
|||||||
margin: 0
|
margin: 0
|
||||||
padding: 0
|
padding: 0
|
||||||
box-sizing: border-box
|
box-sizing: border-box
|
||||||
|
// Improve touch interactions
|
||||||
|
-webkit-tap-highlight-color: transparent
|
||||||
|
-webkit-touch-callout: none
|
||||||
|
-webkit-user-select: none
|
||||||
|
-khtml-user-select: none
|
||||||
|
-moz-user-select: none
|
||||||
|
-ms-user-select: none
|
||||||
|
user-select: none
|
||||||
|
|
||||||
html, body
|
html, body
|
||||||
height: 100vh
|
height: 100vh
|
||||||
width: 100vw
|
width: 100vw
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
// Prevent scrolling and bouncing on touch devices
|
||||||
|
-webkit-overflow-scrolling: touch
|
||||||
|
overscroll-behavior: none
|
||||||
|
|
||||||
body
|
body
|
||||||
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif
|
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif
|
||||||
@@ -22,12 +33,17 @@ body
|
|||||||
line-height: 1.5
|
line-height: 1.5
|
||||||
-webkit-font-smoothing: antialiased
|
-webkit-font-smoothing: antialiased
|
||||||
-moz-osx-font-smoothing: grayscale
|
-moz-osx-font-smoothing: grayscale
|
||||||
|
// Prevent pull-to-refresh and other touch gestures
|
||||||
|
overscroll-behavior-y: none
|
||||||
|
-webkit-user-select: none
|
||||||
|
|
||||||
#root
|
#root
|
||||||
height: 100vh
|
height: 100vh
|
||||||
width: 100vw
|
width: 100vw
|
||||||
display: flex
|
display: flex
|
||||||
flex-direction: column
|
flex-direction: column
|
||||||
|
// Prevent touch scrolling
|
||||||
|
touch-action: none
|
||||||
|
|
||||||
// App Layout Styles
|
// App Layout Styles
|
||||||
.app
|
.app
|
||||||
@@ -35,6 +51,8 @@ body
|
|||||||
width: 100vw
|
width: 100vw
|
||||||
display: flex
|
display: flex
|
||||||
flex-direction: column
|
flex-direction: column
|
||||||
|
// Prevent touch scrolling
|
||||||
|
touch-action: none
|
||||||
|
|
||||||
&__main
|
&__main
|
||||||
flex: 1
|
flex: 1
|
||||||
@@ -68,4 +86,46 @@ body
|
|||||||
font-size: 1.25rem
|
font-size: 1.25rem
|
||||||
color: rgba(30, 41, 59, 0.8)
|
color: rgba(30, 41, 59, 0.8)
|
||||||
font-weight: 500
|
font-weight: 500
|
||||||
line-height: 1.6
|
line-height: 1.6
|
||||||
|
|
||||||
|
// Touch-friendly interactive elements
|
||||||
|
button, a, [role="button"], .interactive
|
||||||
|
// Improve touch target size
|
||||||
|
min-height: 44px
|
||||||
|
min-width: 44px
|
||||||
|
// Add touch feedback
|
||||||
|
transition: all 0.2s ease
|
||||||
|
cursor: pointer
|
||||||
|
|
||||||
|
&:hover, &:focus
|
||||||
|
transform: scale(1.05)
|
||||||
|
|
||||||
|
&:active
|
||||||
|
transform: scale(0.95)
|
||||||
|
opacity: 0.8
|
||||||
|
|
||||||
|
// Prevent text selection on interactive elements
|
||||||
|
button, a, [role="button"], .nav-item, .interactive
|
||||||
|
-webkit-user-select: none
|
||||||
|
-moz-user-select: none
|
||||||
|
-ms-user-select: none
|
||||||
|
user-select: none
|
||||||
|
|
||||||
|
// Allow text selection on input fields and content areas
|
||||||
|
input, textarea, [contenteditable], .selectable
|
||||||
|
-webkit-user-select: text
|
||||||
|
-moz-user-select: text
|
||||||
|
-ms-user-select: text
|
||||||
|
user-select: text
|
||||||
|
// Re-enable touch actions for inputs
|
||||||
|
touch-action: manipulation
|
||||||
|
|
||||||
|
// Fullscreen specific styles
|
||||||
|
@media (display-mode: fullscreen)
|
||||||
|
body
|
||||||
|
background-attachment: fixed
|
||||||
|
|
||||||
|
.app
|
||||||
|
// Ensure content fits fullscreen properly
|
||||||
|
min-height: 100vh
|
||||||
|
max-height: 100vh
|
@@ -117,6 +117,47 @@ const Notes = () => {
|
|||||||
saveCanvasToStorage()
|
saveCanvasToStorage()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Touch event handlers for mobile/tablet support
|
||||||
|
const handleTouchStart = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// Only handle single touch
|
||||||
|
if (e.touches.length !== 1) return
|
||||||
|
|
||||||
|
const touch = e.touches[0]
|
||||||
|
const mouseEvent = new MouseEvent('mousedown', {
|
||||||
|
clientX: touch.clientX,
|
||||||
|
clientY: touch.clientY,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
})
|
||||||
|
startDrawing(mouseEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchMove = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
|
||||||
|
// Only handle single touch
|
||||||
|
if (e.touches.length !== 1) return
|
||||||
|
|
||||||
|
const touch = e.touches[0]
|
||||||
|
const mouseEvent = new MouseEvent('mousemove', {
|
||||||
|
clientX: touch.clientX,
|
||||||
|
clientY: touch.clientY,
|
||||||
|
bubbles: true,
|
||||||
|
cancelable: true
|
||||||
|
})
|
||||||
|
draw(mouseEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTouchEnd = (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
e.stopPropagation()
|
||||||
|
stopDrawing()
|
||||||
|
}
|
||||||
|
|
||||||
const clearCanvas = () => {
|
const clearCanvas = () => {
|
||||||
const canvas = canvasRef.current
|
const canvas = canvasRef.current
|
||||||
const ctx = canvas.getContext('2d')
|
const ctx = canvas.getContext('2d')
|
||||||
@@ -193,6 +234,9 @@ const Notes = () => {
|
|||||||
onMouseMove={draw}
|
onMouseMove={draw}
|
||||||
onMouseUp={stopDrawing}
|
onMouseUp={stopDrawing}
|
||||||
onMouseLeave={stopDrawing}
|
onMouseLeave={stopDrawing}
|
||||||
|
onTouchStart={handleTouchStart}
|
||||||
|
onTouchMove={handleTouchMove}
|
||||||
|
onTouchEnd={handleTouchEnd}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@@ -166,6 +166,9 @@
|
|||||||
background: rgba(255, 255, 255, 0.9)
|
background: rgba(255, 255, 255, 0.9)
|
||||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5)
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5)
|
||||||
overflow: hidden
|
overflow: hidden
|
||||||
|
// Ensure touch events can reach the canvas
|
||||||
|
touch-action: auto !important
|
||||||
|
position: relative
|
||||||
|
|
||||||
.drawing-canvas
|
.drawing-canvas
|
||||||
width: 100%
|
width: 100%
|
||||||
@@ -174,6 +177,12 @@
|
|||||||
display: block
|
display: block
|
||||||
background: #ffffff
|
background: #ffffff
|
||||||
border-radius: 20px
|
border-radius: 20px
|
||||||
|
// Enable touch interactions - override global touch-action: none
|
||||||
|
touch-action: auto !important
|
||||||
|
-webkit-user-select: none
|
||||||
|
-moz-user-select: none
|
||||||
|
-ms-user-select: none
|
||||||
|
user-select: none
|
||||||
|
|
||||||
&.eraser-mode
|
&.eraser-mode
|
||||||
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" fill="none" stroke="black" stroke-width="2"/></svg>') 10 10, auto
|
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" fill="none" stroke="black" stroke-width="2"/></svg>') 10 10, auto
|
||||||
|
@@ -99,9 +99,11 @@ mkdir -p "${CHROOT_DIR}" "${ISO_DIR}"
|
|||||||
|
|
||||||
# Bootstrap Debian base system
|
# Bootstrap Debian base system
|
||||||
echo "Bootstrapping Debian base system..."
|
echo "Bootstrapping Debian base system..."
|
||||||
debootstrap --arch=amd64 --variant=minbase "${DEBIAN_VERSION}" "${CHROOT_DIR}" http://deb.debian.org/debian/
|
debootstrap --arch=amd64 --variant=minbase "${DEBIAN_VERSION}" "${CHROOT_DIR}" http://deb.debian.org/debian/ 2>/dev/null
|
||||||
|
|
||||||
# Mount necessary filesystems
|
# Mount necessary filesystems
|
||||||
|
echo "Mounting virtual filesystems..."
|
||||||
|
mkdir -p "${CHROOT_DIR}"/{dev,dev/pts,proc,sys}
|
||||||
mount --bind /dev "${CHROOT_DIR}/dev"
|
mount --bind /dev "${CHROOT_DIR}/dev"
|
||||||
mount --bind /dev/pts "${CHROOT_DIR}/dev/pts"
|
mount --bind /dev/pts "${CHROOT_DIR}/dev/pts"
|
||||||
mount --bind /proc "${CHROOT_DIR}/proc"
|
mount --bind /proc "${CHROOT_DIR}/proc"
|
||||||
@@ -114,8 +116,11 @@ cleanup() {
|
|||||||
umount -lf "${CHROOT_DIR}/dev" 2>/dev/null || true
|
umount -lf "${CHROOT_DIR}/dev" 2>/dev/null || true
|
||||||
umount -lf "${CHROOT_DIR}/proc" 2>/dev/null || true
|
umount -lf "${CHROOT_DIR}/proc" 2>/dev/null || true
|
||||||
umount -lf "${CHROOT_DIR}/sys" 2>/dev/null || true
|
umount -lf "${CHROOT_DIR}/sys" 2>/dev/null || true
|
||||||
|
# Additional cleanup - remove any leftover mount points
|
||||||
|
sync
|
||||||
|
sleep 1
|
||||||
}
|
}
|
||||||
trap cleanup EXIT
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
# Copy OpenWall files into chroot
|
# Copy OpenWall files into chroot
|
||||||
echo "Copying OpenWall files..."
|
echo "Copying OpenWall files..."
|
||||||
@@ -258,13 +263,35 @@ chmod +x "${CHROOT_DIR}/setup-system.sh"
|
|||||||
echo "Running system setup in chroot..."
|
echo "Running system setup in chroot..."
|
||||||
chroot "${CHROOT_DIR}" /setup-system.sh
|
chroot "${CHROOT_DIR}" /setup-system.sh
|
||||||
|
|
||||||
|
# Clean up mounts before creating squashfs
|
||||||
|
echo "Cleaning up mounts before filesystem creation..."
|
||||||
|
# Disable trap temporarily
|
||||||
|
trap - EXIT INT TERM
|
||||||
|
umount -lf "${CHROOT_DIR}/dev/pts" 2>/dev/null || true
|
||||||
|
umount -lf "${CHROOT_DIR}/dev" 2>/dev/null || true
|
||||||
|
umount -lf "${CHROOT_DIR}/proc" 2>/dev/null || true
|
||||||
|
umount -lf "${CHROOT_DIR}/sys" 2>/dev/null || true
|
||||||
|
sync
|
||||||
|
sleep 2
|
||||||
|
|
||||||
# Create live boot configuration
|
# Create live boot configuration
|
||||||
echo "Setting up live boot configuration..."
|
echo "Setting up live boot configuration..."
|
||||||
mkdir -p "${ISO_DIR}/live"
|
mkdir -p "${ISO_DIR}/live"
|
||||||
|
|
||||||
# Create filesystem image
|
# Create filesystem image
|
||||||
echo "Creating filesystem image..."
|
echo "Creating filesystem image (this may take a while)..."
|
||||||
mksquashfs "${CHROOT_DIR}" "${ISO_DIR}/live/filesystem.squashfs" -comp xz -e boot
|
mksquashfs "${CHROOT_DIR}" "${ISO_DIR}/live/filesystem.squashfs" \
|
||||||
|
-comp xz \
|
||||||
|
-e boot \
|
||||||
|
-e proc \
|
||||||
|
-e sys \
|
||||||
|
-e dev \
|
||||||
|
-e tmp \
|
||||||
|
-e var/tmp \
|
||||||
|
-e var/cache \
|
||||||
|
-e var/log \
|
||||||
|
-no-progress \
|
||||||
|
-quiet
|
||||||
|
|
||||||
# Copy kernel and initrd
|
# Copy kernel and initrd
|
||||||
cp "${CHROOT_DIR}/boot/vmlinuz-"* "${ISO_DIR}/live/vmlinuz"
|
cp "${CHROOT_DIR}/boot/vmlinuz-"* "${ISO_DIR}/live/vmlinuz"
|
||||||
@@ -338,9 +365,18 @@ xorriso -as mkisofs \
|
|||||||
-append_partition 2 0xef "${ISO_DIR}/EFI/boot/bootx64.efi" \
|
-append_partition 2 0xef "${ISO_DIR}/EFI/boot/bootx64.efi" \
|
||||||
-output "${OUTPUT_ISO}" \
|
-output "${OUTPUT_ISO}" \
|
||||||
-graft-points \
|
-graft-points \
|
||||||
"${ISO_DIR}"
|
"${ISO_DIR}" 2>/dev/null
|
||||||
|
|
||||||
echo "ISO created: ${OUTPUT_ISO}"
|
echo "ISO created: ${OUTPUT_ISO}"
|
||||||
|
|
||||||
|
# Final cleanup
|
||||||
|
final_cleanup() {
|
||||||
|
umount -lf "${CHROOT_DIR}/dev/pts" 2>/dev/null || true
|
||||||
|
umount -lf "${CHROOT_DIR}/dev" 2>/dev/null || true
|
||||||
|
umount -lf "${CHROOT_DIR}/proc" 2>/dev/null || true
|
||||||
|
umount -lf "${CHROOT_DIR}/sys" 2>/dev/null || true
|
||||||
|
}
|
||||||
|
final_cleanup
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
chmod +x "$BUILD_DIR/build-iso.sh"
|
chmod +x "$BUILD_DIR/build-iso.sh"
|
||||||
|
@@ -5,13 +5,13 @@ FROM node:18-alpine
|
|||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Copy package files
|
# Copy package files
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json yarn.lock ./
|
||||||
|
|
||||||
# Install pnpm globally
|
# Install yarn globally
|
||||||
RUN npm install -g pnpm
|
RUN npm install -g yarn
|
||||||
|
|
||||||
# Install dependencies
|
# Install dependencies
|
||||||
RUN pnpm install --frozen-lockfile
|
RUN yarn install
|
||||||
|
|
||||||
# Copy source code
|
# Copy source code
|
||||||
COPY . .
|
COPY . .
|
||||||
@@ -27,4 +27,4 @@ ENV NODE_ENV=production
|
|||||||
ENV DATABASE_PATH=/app/data/database.sqlite
|
ENV DATABASE_PATH=/app/data/database.sqlite
|
||||||
|
|
||||||
# Start the application
|
# Start the application
|
||||||
CMD ["pnpm", "start"]
|
CMD ["yarn", "start"]
|
||||||
|
2758
server/package-lock.json
generated
2758
server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -11,9 +11,7 @@
|
|||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"packageManager": "pnpm@10.13.1",
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nextcloud/cdav-library": "^1.1.0",
|
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.0",
|
"dotenv": "^17.2.0",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
|
Reference in New Issue
Block a user