diff --git a/src/editor/commands.js b/src/editor/commands.js index bd826e55..5012d902 100644 --- a/src/editor/commands.js +++ b/src/editor/commands.js @@ -73,6 +73,10 @@ const openCreateNewBuffer = (editor) => () => { useHeynoteStore().openCreateBuffer("new") return true } +const createScratchBuffer = (editor) => () => { + useHeynoteStore().createScratchBuffer() + return true +} const closeCurrentTab = (editor) => () => { useHeynoteStore().closeCurrentTab() @@ -149,6 +153,7 @@ const HEYNOTE_COMMANDS = { openCommandPalette: cmd(openCommandPalette, "Editor", "Open command palette…"), openMoveToBuffer: cmd(openMoveToBuffer, "Block", "Move block to another buffer…"), openCreateNewBuffer: cmd(openCreateNewBuffer, "Buffer", "Create new buffer…"), + createScratchBuffer: cmd(createScratchBuffer, "Buffer", "Create new scratch buffer"), cut: cmd(cutCommand, "Clipboard", "Cut selection"), copy: cmd(copyCommand, "Clipboard", "Copy selection"), foldBlock: cmd(foldBlock, "Block", "Fold block"), diff --git a/src/editor/keymap.js b/src/editor/keymap.js index 72b78794..de3e7e2f 100644 --- a/src/editor/keymap.js +++ b/src/editor/keymap.js @@ -102,6 +102,7 @@ export const DEFAULT_KEYMAP = [ cmd("Mod-Shift-p", "openCommandPalette"), cmd("Mod-s", "openMoveToBuffer"), cmd("Mod-n", "openCreateNewBuffer"), + cmd("Mod-Shift-n", "createScratchBuffer"), cmd("Alt-Shift-f", "formatBlockContent"), cmd("Mod-Shift-Space", "toggleCheckbox"), @@ -299,7 +300,7 @@ export function getAllKeyBindingsForCommand(command, keymapName, userKeymap, ema "nothing", "toggleAlwaysOnTop", "toggleLeftPanel", - "openLanguageSelector", "openBufferSelector", "openCreateNewBuffer", "openMoveToBuffer", "openCommandPalette", + "openLanguageSelector", "openBufferSelector", "openCreateNewBuffer", "createScratchBuffer", "openMoveToBuffer", "openCommandPalette", "closeCurrentTab", "reopenLastClosedTab", "nextTab", "previousTab", "switchToTab1", "switchToTab2", "switchToTab3", "switchToTab4", "switchToTab5", "switchToTab6", "switchToTab7", "switchToTab8", "switchToTab9", "switchToLastTab" ]) diff --git a/src/stores/heynote-store.js b/src/stores/heynote-store.js index 6774ba3e..91bfcb86 100644 --- a/src/stores/heynote-store.js +++ b/src/stores/heynote-store.js @@ -1,12 +1,13 @@ import { toRaw, nextTick, watch } from 'vue'; import { defineStore } from "pinia" import { NoteFormat } from "../common/note-format" +import { filenameSlug } from "../common/sanitize-filename" import { toSafeBrowserLocale } from "../util/locale.js" import { useEditorCacheStore } from "./editor-cache" import { useSettingsStore } from "./settings-store" -import { - SCRATCH_FILE_NAME, WINDOW_FULLSCREEN_STATE, WINDOW_FOCUS_STATE, - SAVE_TABS_STATE, LOAD_TABS_STATE, CONTEXT_MENU_CLOSED +import { + SCRATCH_FILE_NAME, WINDOW_FULLSCREEN_STATE, WINDOW_FOCUS_STATE, + SAVE_TABS_STATE, LOAD_TABS_STATE, CONTEXT_MENU_CLOSED } from "../common/constants" @@ -285,6 +286,21 @@ export const useHeynoteStore = defineStore("heynote", { await toRaw(this.currentEditor).createNewBuffer(path, name) }, + /** + * Create a new buffer with an auto-generated "Scratch N" name (no modal). + * N is the highest existing Scratch index + 1, so deletions don't cause collisions. + */ + async createScratchBuffer() { + const scratchPathRe = /^scratch-(\d+)\.txt$/ + const maxN = Object.keys(this.buffers).reduce((max, path) => { + const match = path.match(scratchPathRe) + return match ? Math.max(max, parseInt(match[1], 10)) : max + }, 0) + const name = `Scratch ${maxN + 1}` + const path = filenameSlug(name) + ".txt" + await this.createNewBuffer(path, name) + }, + /** * Create a new note file at path, with name `name`, and content content * @param {*} path: File path relative to Heynote root diff --git a/tests/playwright/buffer-creation.spec.js b/tests/playwright/buffer-creation.spec.js index 56bde2ae..ddb050ab 100644 --- a/tests/playwright/buffer-creation.spec.js +++ b/tests/playwright/buffer-creation.spec.js @@ -125,6 +125,20 @@ Block C`) `)) }) +test("create scratch buffers via Mod+Shift+N without modal", async ({page}) => { + for (let i = 0; i < 3; i++) { + await page.locator("body").press(heynotePage.agnosticKey("Mod+Shift+N")) + await page.waitForTimeout(100) + await expect(page.locator(".new-buffer")).toHaveCount(0) + } + await page.waitForTimeout(AUTO_SAVE_INTERVAL + 50) + + const buffers = Object.keys(await heynotePage.getStoredBufferList()) + expect(buffers).toContain("scratch-1.txt") + expect(buffers).toContain("scratch-2.txt") + expect(buffers).toContain("scratch-3.txt") +}) + test("folder selector hides dot-prefixed folders", async ({ page }) => { await page.evaluate(async () => { await window.heynote.buffer.createDirectory(".secret")