note window opening

This commit is contained in:
nicwands
2026-02-23 11:42:22 -05:00
parent c436488f9d
commit 660b0825c5
18 changed files with 9687 additions and 9232 deletions

1
.env Normal file
View File

@@ -0,0 +1 @@
VITE_DEV_SERVER_URL=http://localhost:5173

View File

@@ -2,30 +2,49 @@
const electron = require("electron");
const path = require("path");
const utils = require("@electron-toolkit/utils");
const icon = path.join(__dirname, "../../resources/icon.png");
function createWindow() {
const mainWindow = new electron.BrowserWindow({
const mainWindow2 = new electron.BrowserWindow({
width: 354,
height: 549,
show: false,
autoHideMenuBar: true,
...process.platform === "linux" ? { icon } : {},
webPreferences: {
preload: path.join(__dirname, "../preload/index.js"),
sandbox: false
}
});
mainWindow.on("ready-to-show", () => {
mainWindow.show();
mainWindow2.on("ready-to-show", () => {
mainWindow2.show();
});
mainWindow.webContents.setWindowOpenHandler((details) => {
mainWindow2.webContents.setWindowOpenHandler((details) => {
electron.shell.openExternal(details.url);
return { action: "deny" };
});
if (utils.is.dev && process.env["ELECTRON_RENDERER_URL"]) {
mainWindow.loadURL(process.env["ELECTRON_RENDERER_URL"]);
mainWindow2.loadURL(process.env["ELECTRON_RENDERER_URL"]);
} else {
mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"));
mainWindow2.loadFile(path.join(__dirname, "../renderer/index.html"));
}
}
function createNoteWindow(noteId) {
const noteWindow = new electron.BrowserWindow({
width: 354,
height: 549,
autoHideMenuBar: true,
webPreferences: {
preload: path.join(__dirname, "preload.js"),
contextIsolation: true,
nodeIntegration: false
}
});
if (utils.is.dev && process.env["ELECTRON_RENDERER_URL"]) {
noteWindow.loadURL(
`${process.env["ELECTRON_RENDERER_URL"]}/note/${noteId}`
);
} else {
mainWindow.loadFile(path.join(__dirname, "../renderer/index.html"), {
path: `/notes/${noteId}`
});
}
}
electron.app.whenReady().then(() => {
@@ -33,11 +52,14 @@ electron.app.whenReady().then(() => {
electron.app.on("browser-window-created", (_, window) => {
utils.optimizer.watchWindowShortcuts(window);
});
electron.ipcMain.on("ping", () => console.log("pong"));
console.log(electron.app.getPath("userData"));
createWindow();
electron.app.on("activate", function() {
if (electron.BrowserWindow.getAllWindows().length === 0) createWindow();
});
electron.ipcMain.on("open-note-window", (_, noteId) => {
createNoteWindow(noteId);
});
});
electron.app.on("window-all-closed", () => {
if (process.platform !== "darwin") {

View File

@@ -1,7 +1,11 @@
"use strict";
const electron = require("electron");
const preload = require("@electron-toolkit/preload");
const api = {};
const api = {
openNoteWindow: (noteId) => {
electron.ipcRenderer.send("open-note-window", noteId);
}
};
if (process.contextIsolated) {
try {
electron.contextBridge.exposeInMainWorld("electron", preload.electronAPI);

18538
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -35,11 +35,13 @@
"@tiptap/vue-3": "^3.19.0",
"@vueuse/core": "^14.2.1",
"electron-updater": "^6.3.9",
"fecha": "^4.2.3",
"gsap": "^3.14.2",
"lenis": "^1.3.17",
"sass": "^1.97.3",
"sass-embedded": "^1.97.3",
"tempus": "^1.0.0-dev.17"
"tempus": "^1.0.0-dev.17",
"vue-router": "^5.0.3"
},
"devDependencies": {
"@vitejs/plugin-vue": "^6.0.2",

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

View File

@@ -1,7 +1,6 @@
import { app, shell, BrowserWindow, ipcMain } from 'electron'
import { join } from 'path'
import { electronApp, optimizer, is } from '@electron-toolkit/utils'
import icon from '../../resources/icon.png?asset'
function createWindow() {
// Create the browser window.
@@ -10,7 +9,6 @@ function createWindow() {
height: 549,
show: false,
autoHideMenuBar: true,
...(process.platform === 'linux' ? { icon } : {}),
webPreferences: {
preload: join(__dirname, '../preload/index.js'),
sandbox: false,
@@ -35,6 +33,29 @@ function createWindow() {
}
}
function createNoteWindow(noteId) {
const noteWindow = new BrowserWindow({
width: 354,
height: 549,
autoHideMenuBar: true,
webPreferences: {
preload: join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
},
})
if (is.dev && process.env['ELECTRON_RENDERER_URL']) {
noteWindow.loadURL(
`${process.env['ELECTRON_RENDERER_URL']}/note/${noteId}`,
)
} else {
mainWindow.loadFile(join(__dirname, '../renderer/index.html'), {
path: `/notes/${noteId}`,
})
}
}
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
@@ -49,8 +70,7 @@ app.whenReady().then(() => {
optimizer.watchWindowShortcuts(window)
})
// IPC test
ipcMain.on('ping', () => console.log('pong'))
console.log(app.getPath('userData'))
createWindow()
@@ -59,6 +79,11 @@ app.whenReady().then(() => {
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) createWindow()
})
// Open note in new window
ipcMain.on('open-note-window', (_, noteId) => {
createNoteWindow(noteId)
})
})
// Quit when all windows are closed, except on macOS. There, it's common
@@ -69,6 +94,3 @@ app.on('window-all-closed', () => {
app.quit()
}
})
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

View File

@@ -1,20 +1,24 @@
import { contextBridge } from 'electron'
import { contextBridge, ipcRenderer } from 'electron'
import { electronAPI } from '@electron-toolkit/preload'
// Custom APIs for renderer
const api = {}
const api = {
openNoteWindow: (noteId) => {
ipcRenderer.send('open-note-window', noteId)
},
}
// Use `contextBridge` APIs to expose Electron APIs to
// renderer only if context isolation is enabled, otherwise
// just add to the DOM global.
if (process.contextIsolated) {
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
try {
contextBridge.exposeInMainWorld('electron', electronAPI)
contextBridge.exposeInMainWorld('api', api)
} catch (error) {
console.error(error)
}
} else {
window.electron = electronAPI
window.api = api
window.electron = electronAPI
window.api = api
}

View File

@@ -1,7 +1,7 @@
<template>
<lenis root :options="{ duration: 1 }">
<div :class="classes" :style="styles">
<editor />
<router-view :key="$route.fullPath" />
</div>
</lenis>
</template>
@@ -11,7 +11,6 @@ import Lenis from './components/Lenis.vue'
import { ref, computed, onMounted } from 'vue'
import loadFonts from '@fuzzco/font-loader'
import { useWindowSize } from '@vueuse/core'
import Editor from './components/editor/Index.vue'
const { height } = useWindowSize()

View File

@@ -0,0 +1,27 @@
import { useRouter } from 'vue-router'
export function useOpenNote() {
const router = useRouter()
function openNote(noteId, options = {}) {
const { newWindow = true } = options
// Electron environment check
const isElectron =
typeof window !== 'undefined' &&
window.api &&
typeof window.api.openNoteWindow === 'function'
if (newWindow && isElectron) {
window.api.openNoteWindow(noteId)
return
}
// Fallback to SPA navigation
router.push(`/note/${noteId}`)
}
return {
openNote,
}
}

View File

@@ -27,12 +27,12 @@ const breakpoints = {
const viewports = {
mobile: {
width: 440,
width: 200,
height: 956,
},
desktop: {
width: 1728,
height: 1117,
width: 354,
height: 549,
},
}

View File

@@ -1,5 +1,11 @@
import './styles/main.scss'
import { createApp } from 'vue'
import App from './App.vue'
import { router } from './plugins/router'
createApp(App).mount('#app')
const app = createApp(App)
// Plugins
app.use(router)
app.mount('#app')

View File

@@ -0,0 +1,14 @@
import { createRouter, createWebHistory } from 'vue-router'
import Directory from '../views/Directory.vue'
import Editor from '../views/Editor.vue'
const routes = [
{ path: '/', name: 'directory', component: Directory },
{ path: '/note/:id', name: 'note', component: Editor },
]
export const router = createRouter({
history: createWebHistory(),
routes,
})

View File

@@ -4,7 +4,7 @@
font-family: var(--font-display);
font-weight: 400;
letter-spacing: -0.02em;
line-height: 1;
line-height: 1.3;
font-size: size-vw(30px);
}

View File

@@ -36,10 +36,10 @@ $layout: (
//internal process, do not touch
:root {
--layout-column-count: #{list.nth(map.get($layout, 'columns-count'), 1)};
--layout-column-gap: #{mobile-vw(
--layout-column-gap: #{size-vw(
list.nth(map.get($layout, 'columns-gap'), 1)
)};
--layout-margin: #{mobile-vw(list.nth(map.get($layout, 'margin'), 1))};
--layout-margin: #{size-vw(list.nth(map.get($layout, 'margin'), 1))};
--layout-width: calc(100vw - (2 * var(--layout-margin)));
--layout-column-width: calc(
(
@@ -51,14 +51,6 @@ $layout: (
) /
var(--layout-column-count)
);
@include desktop {
--layout-column-count: #{list.nth(map.get($layout, 'columns-count'), 2)};
--layout-column-gap: #{desktop-vw(
list.nth(map.get($layout, 'columns-gap'), 2)
)};
--layout-margin: #{desktop-vw(list.nth(map.get($layout, 'margin'), 2))};
}
}
.layout-block {

View File

@@ -14,8 +14,7 @@
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
background: var(--theme-bg);
color: var(--theme-fg);
background: var(--black);
font-synthesis: none;
text-rendering: optimizeLegibility;
@@ -37,6 +36,10 @@ h4,
h5,
h6 {
@include h1;
&.mono {
@include h1-mono;
}
}
.p,
p,
@@ -46,7 +49,20 @@ input,
pre {
@include p;
}
.bold {
font-weight: 700;
}
#app {
min-height: 100vh;
}
// Text selection
::selection {
color: var(--theme-bg);
background: var(--theme-accent);
}
::-moz-selection {
color: var(--theme-bg);
background: var(--theme-accent);
}

View File

@@ -0,0 +1,145 @@
<template>
<main class="directory layout-block">
<button
class="capitula"
v-for="(capitula, i) in capitulum"
@click="openNote(capitula.slug)"
>
<span class="index">{{ String(i + 1).padStart(2, '0') }}.</span>
<span class="title h1">{{ capitula.title }}</span>
</button>
<h2 class="label">Summarium</h2>
<div class="summarium">
<button
class="summaria"
v-for="summaria in summarium"
@click="openNote(summaria.slug)"
>
<span class="date">{{ formatDate(summaria.date) }}</span>
<span class="title bold">{{ summaria.title }}</span>
</button>
</div>
</main>
</template>
<script setup>
import { format } from 'fecha'
import { useOpenNote } from '@/composables/useOpenNote'
const capitulum = [
{
slug: 'category-name',
date: new Date(),
title: 'Category Name',
},
{
slug: 'alternate-tuning',
date: new Date(),
title: 'Alternate Tuning',
},
{
slug: 'hungarian-citizenship',
date: new Date(),
title: 'Hungarian Citizenship Application and More and More',
},
]
const summarium = [
{
slug: 'birthday-dinner',
date: new Date(),
title: 'Birthday Dinner Recipe to make for Gina',
},
{
slug: 'to-do-reminders',
date: new Date(),
title: 'To-Do Reminders',
},
{
slug: 'client-feedback',
date: new Date(),
title: 'Client Feedback',
},
]
const { openNote } = useOpenNote()
const formatDate = (date) => {
return format(date, 'MM/DD/YYYY')
}
</script>
<style lang="scss">
.directory {
padding-top: size-vw(18px);
.capitula {
display: grid;
grid-template-columns: size-vw(26px) 1fr;
width: 100%;
position: relative;
padding: size-vw(5px) 0 size-vw(15px);
cursor: pointer;
.index {
margin-top: size-vw(19px);
}
.title {
display: block;
width: 100%;
@include line-clamp(2);
}
&::after {
content: '----------------------------------------';
position: absolute;
bottom: 0;
left: 0;
}
&:hover {
color: var(--theme-accent);
}
}
.label {
text-transform: uppercase;
margin: size-vw(17px) 0 size-vw(24px);
@include p;
}
.summarium {
display: flex;
flex-direction: column;
gap: size-vw(14px);
.summaria {
grid-template-columns: auto 1fr;
display: grid;
gap: size-vw(20px);
cursor: pointer;
.title {
width: size-vw(159px);
position: relative;
&::after {
content: '(open)';
position: absolute;
bottom: 0;
right: 0;
transform: translateX(100%);
font-weight: 700;
opacity: 0;
}
}
&:hover {
color: var(--theme-accent);
.title::after {
opacity: 1;
}
}
}
}
}
</style>

View File

@@ -17,33 +17,6 @@
</div>
</bubble-menu>
<floating-menu :editor="editor">
<div class="floating-menu">
<button
@click="
editor.chain().focus().toggleHeading({ level: 1 }).run()
"
:class="{
active: editor.isActive('heading', { level: 1 }),
}"
>
H1
</button>
<button
@click="editor.chain().focus().toggleBulletList().run()"
:class="{ active: editor.isActive('bulletList') }"
>
Bullet List
</button>
<button
@click="editor.chain().focus().toggleOrderedList().run()"
:class="{ active: editor.isActive('orderedlist') }"
>
Number List
</button>
</div>
</floating-menu>
<editor-content :editor="editor" class="editor-wrap" />
</div>
</template>
@@ -111,7 +84,7 @@ onBeforeUnmount(() => {
<style lang="scss">
.editor {
padding-top: size-vw(8px);
// padding-top: 100px;
padding-bottom: size-vw(20px);
h1 {
font-weight: 700 !important;
@@ -134,7 +107,8 @@ onBeforeUnmount(() => {
font-style: italic;
}
hr::before {
content: '-----------------------------------------';
content: '----------------------------------------';
@include p;
}
ul {
list-style-type: disc;
@@ -173,7 +147,6 @@ onBeforeUnmount(() => {
}
}
.floating-menu,
.bubble-menu {
display: flex;
gap: size-vw(5px);