diff --git a/README.md b/README.md index e7c1c4ff..2742597f 100644 --- a/README.md +++ b/README.md @@ -59,16 +59,26 @@ ## Context menu -Context menu allows you to quickly save selected text from any website to My Notes, or to transfer selected text to My Notes on other computers. +Context menu can be displayed on right-click on any website and based on the context used to quickly save: -To use Context menu, select the text on website, right-click and see "My Notes" Context menu. +
    +
  1. selected text
  2. +
  3. current page URL (right-click on an empty space)
  4. +
  5. image
  6. +
+ +Destination can be any local note, or My Notes on other computers. + +Options are based on the context but in general are: -Context menu has these options: +- `Save to [note name]` – Option for every note. My Notes doesn't have to be open. Google Drive Sync is not required. +- `Save to remotely open My Notes` – My Notes on other computers needs to be open. The same Google Account needs to be used. Google Drive Sync is not required. The destination note will be named `@received`. -- `Save to [note name]` – Option for every note. As you create new notes, they are automatically added to the list. My Notes doesn't have to be open. Google Drive Sync is not required. -- `Save to remotely open My Notes` – My Notes on other computers needs to be open. The same Google Account needs to be used. Google Drive Sync is not required. The destination note to save the text will be named **"@received"** (created automatically if it doesn't exist, otherwise updated). +There is a few general purpose notes that can be used as destination. Their name starts with `@` and they are created automatically when needed: -Context menu also allows you to save current page URL (no text selected) to **"@clipboard"** (created automatically if it doesn't exist, otherwise updated). +- `@received` +- `@clipboard` +- `@images`

@@ -87,17 +97,11 @@ Click on the **"Save"** button to save the custom theme. ## Command palette -Command palette is a window which you can open with `Cmd + P` (or `Ctrl + P`) and use your keyboard to quickly perform any of the following actions: - -
    -
  1. Find note(s) by their name, and open one. (default behavior)
  2. -
  3. Find note(s) by their content, and open one. (type ? first, then continue)
  4. -
  5. Find commands, and execute one. (type > first, then continue)
  6. -
+Command palette is a window which you can open with `Cmd + P` (or `Ctrl + P`) and use your keyboard to quickly find commands, and execute one. -To navigate between the results, use `Up` and `Down` arrow keys. +To navigate between the commands, use `Up` and `Down` arrow keys. -To open a selected note or execute a selected command, press `Enter`. +To execute a selected command, press `Enter`. The last executed command can be repeated with `Cmd + Shift + P` (or `Ctrl + Shift + P`). diff --git a/src/notes.tsx b/src/notes.tsx index cc7811ff..5edb666e 100644 --- a/src/notes.tsx +++ b/src/notes.tsx @@ -43,12 +43,15 @@ import { } from "notes/content/save"; import sendMessage from "shared/messages/send"; -import { getActiveFromUrl, getFocusOverride } from "notes/location"; +import { getActiveFromUrl, getFocusOverride, isOverview } from "notes/location"; +import Overview from "notes/overview/Overview"; import getFirstAvailableNoteName from "notes/filters/get-first-available-note-name"; import notesHistory from "notes/history"; import keyboardShortcuts, { KeyboardShortcut } from "notes/keyboard-shortcuts"; import { useKeyboardShortcut } from "notes/hooks/use-keyboard-shortcut"; -import { Command, toggleSidebar, toggleToolbar } from "notes/commands"; +import { + Command, toggleSidebar, toggleToolbar, toggleFocusMode, +} from "notes/commands"; import { exportNote } from "notes/export"; import { notesToSidebarNotes } from "notes/adapters"; @@ -64,6 +67,8 @@ const Notes = (): h.JSX.Element => { const [tabId, setTabId] = useState(undefined); const [initialized, setInitialized] = useState(false); + const [view] = useState<"default" | "overview">(isOverview() ? "overview" : "default"); + // Notifications const [notification, setNotification] = useState(undefined); @@ -378,8 +383,8 @@ const Notes = (): h.JSX.Element => { // Sidebar useEffect(() => { - document.body.classList.toggle("with-sidebar", sidebar); - }, [sidebar]); + document.body.classList.toggle("with-sidebar", view === "default" && sidebar); + }, [sidebar, view]); // Sidebar width useEffect(() => { @@ -429,8 +434,8 @@ const Notes = (): h.JSX.Element => { raw: note.raw || false, }); - document.title = note ? notesProps.active : "My Notes"; - }, [notesProps.active]); + document.title = (view === "default" && note) ? notesProps.active : "My Notes"; + }, [notesProps.active, view]); // Toolbar useEffect(() => { @@ -455,18 +460,10 @@ const Notes = (): h.JSX.Element => { }); }); keyboardShortcuts.subscribe(KeyboardShortcut.OnOpenOptions, chrome.runtime.openOptionsPage); - keyboardShortcuts.subscribe(KeyboardShortcut.OnToggleFocusMode, () => { - if (getFocusOverride()) { - return; - } - chrome.storage.local.get(["focus"], (local) => { - chrome.storage.local.set({ focus: !local.focus }); - }); - }); keyboardShortcuts.subscribe(KeyboardShortcut.OnToggleSidebar, toggleSidebar); - keyboardShortcuts.subscribe(KeyboardShortcut.OnToggleToolbar, toggleToolbar); + keyboardShortcuts.subscribe(KeyboardShortcut.OnToggleFocusMode, toggleFocusMode); keyboardShortcuts.subscribe(KeyboardShortcut.OnSync, () => sendMessage(MessageType.SYNC)); }, [os]); @@ -572,6 +569,12 @@ const Notes = (): h.JSX.Element => { }, 6000); // and then Auto Sync every 6 seconds }, [initialized, autoSync]); + if (view === "overview" && notesOrder && order) { + return ( + + ); + } + return ( {notification && ( diff --git a/src/notes/commands/index.ts b/src/notes/commands/index.ts index e6383a2f..747011ae 100644 --- a/src/notes/commands/index.ts +++ b/src/notes/commands/index.ts @@ -1,5 +1,5 @@ import dateUtils from "shared/date/date-utils"; -import { getFocusOverride } from "notes/location"; +import { getFocusOverride, isOverview } from "notes/location"; import table from "./table"; import highlight from "./highlight"; @@ -94,13 +94,15 @@ const commands: { [key in AvailableCommand]: Command } = { RemoveFormat, }; +const canToggle = () => !getFocusOverride() && !isOverview(); + const toggleSidebar: Command = () => { - if (getFocusOverride()) { + if (!canToggle()) { return; } chrome.storage.local.get(["focus"], (local) => { - if (!local.focus) { // toggle only if not in focus mode + if (!local.focus) { // toggle only if NOT in focus mode const hasSidebar = document.body.classList.toggle("with-sidebar"); chrome.storage.local.set({ sidebar: hasSidebar }); } @@ -108,18 +110,28 @@ const toggleSidebar: Command = () => { }; const toggleToolbar: Command = () => { - if (getFocusOverride()) { + if (!canToggle()) { return; } chrome.storage.local.get(["focus"], (local) => { - if (!local.focus) { // toggle only if not in focus mode + if (!local.focus) { // toggle only if NOT in focus mode const hasToolbar = document.body.classList.toggle("with-toolbar"); chrome.storage.local.set({ toolbar: hasToolbar }); } }); }; +const toggleFocusMode: Command = () => { + if (!canToggle()) { + return; + } + + chrome.storage.local.get(["focus"], (local) => { + chrome.storage.local.set({ focus: !local.focus }); + }); +}; + export { commands, @@ -133,4 +145,5 @@ export { toggleSidebar, toggleToolbar, + toggleFocusMode, }; diff --git a/src/notes/components/SidebarButtons.tsx b/src/notes/components/SidebarButtons.tsx index d50c023a..3af015d1 100644 --- a/src/notes/components/SidebarButtons.tsx +++ b/src/notes/components/SidebarButtons.tsx @@ -7,6 +7,7 @@ import SVG from "notes/components/SVG"; import FileSvgText from "svg/file.svg"; import GearSvgText from "svg/gear.svg"; import RefreshSvgText from "svg/refresh.svg"; +import GridSvgText from "svg/grid.svg"; import { importNoteFromTextFile } from "notes/import"; import sendMessage from "shared/messages/send"; import formatDate from "shared/date/format-date"; @@ -23,6 +24,7 @@ const SidebarButtons = ({ }: SidebarButtonsProps): h.JSX.Element => { const [dragOver, setDragOver] = useState(false); const openOptions = useCallback(() => chrome.runtime.openOptionsPage(), []); + const openOverview = useCallback(() => window.open("notes.html?overview", "_blank"), []); return (
+ + +
+ +
+
); }; diff --git a/src/notes/history.ts b/src/notes/history.ts index 35e1a0cf..ff2a2afe 100644 --- a/src/notes/history.ts +++ b/src/notes/history.ts @@ -1,13 +1,17 @@ +import { isOverview } from "./location"; + +type NoteNameFunc = (noteName: string) => void; + // Use replace when a note is renamed or deleted -const replace = (noteName: string): void => +const replace: NoteNameFunc = (noteName: string): void => window.history.replaceState({ noteName }, noteName, `?note=${noteName}`); // Use push when a note is created -const push = (noteName: string): void => +const push: NoteNameFunc = (noteName: string): void => window.history.pushState({ noteName }, noteName, `?note=${noteName}`); let attached = false; -const attach = (onPop: (noteName: string) => void): void => { +const attach = (onPop: NoteNameFunc): void => { if (attached) { return; } @@ -22,9 +26,10 @@ const attach = (onPop: (noteName: string) => void): void => { attached = true; }; -export default { - replace, - push, +const canProxy = () => !isOverview(); - attach, +export default { + replace: (noteName: string) => canProxy() && replace(noteName), + push: (noteName: string) => canProxy() && push(noteName), + attach: (onPop: NoteNameFunc) => canProxy() && attach(onPop), }; diff --git a/src/notes/location.ts b/src/notes/location.ts index b5913cfa..c822cc1f 100644 --- a/src/notes/location.ts +++ b/src/notes/location.ts @@ -1,2 +1,5 @@ -export const getFocusOverride = (): boolean => new URL(window.location.href).searchParams.get("focus") === ""; -export const getActiveFromUrl = (): string => new URL(window.location.href).searchParams.get("note") || ""; // Bookmark +const getParam = (name: string) => new URL(window.location.href).searchParams.get(name); + +export const getFocusOverride = (): boolean => getParam("focus") === ""; +export const getActiveFromUrl = (): string => getParam("note") || ""; +export const isOverview = (): boolean => getParam("overview") === ""; diff --git a/src/notes/overview/Overview.tsx b/src/notes/overview/Overview.tsx new file mode 100644 index 00000000..dbf2fcc0 --- /dev/null +++ b/src/notes/overview/Overview.tsx @@ -0,0 +1,25 @@ +import { h } from "preact"; +import { SidebarNote } from "notes/adapters"; + +interface OverviewProps { + notes: SidebarNote[] +} + +const Overview = ({ notes }: OverviewProps): h.JSX.Element => ( +
+ {notes.map((note) => ( +
+ +
+
+ ))} +
+); + +export default Overview; diff --git a/src/svg/grid.svg b/src/svg/grid.svg new file mode 100644 index 00000000..76b2eff2 --- /dev/null +++ b/src/svg/grid.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/static/notes.css b/static/notes.css index b1d58262..c26fceaf 100644 --- a/static/notes.css +++ b/static/notes.css @@ -90,6 +90,44 @@ hr { border-bottom: 1px solid var(--notification-border-color); } +/* Overview */ + +#notes-overview { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(600px, 1fr)); + column-gap: 1em; + row-gap: 1em; + padding: 1em; + overflow-y: auto; +} + +.note-tile { + display: inline-block; + height: 400px; + border: 1px solid var(--overview-tile-border-color); + border-radius: 3px; + overflow: auto; +} + +.note-tile::-webkit-scrollbar-track, +.note-tile::-webkit-scrollbar-thumb { + border-radius: 3px; +} + +.note-tile-title { + position: sticky; + left: 0; + top: 0; + background: var(--background-color); + padding: 1em; + font-size: 1.2em; + font-weight: bold; +} + +.note-tile-content { + padding: 1em; +} + /* Bar (for #sidebar-buttons and #toolbar) */ #toolbar, #sidebar-buttons { @@ -164,8 +202,8 @@ body:not(.with-sidebar) { left: 0 !important; } align-items: center; cursor: pointer; font-weight: bold; - padding: .5em 1em; - margin-top: 3px; + padding: .5em; + margin-bottom: 3px; border-radius: 3px; } diff --git a/static/themes/dark.css b/static/themes/dark.css index 94e1d234..4f31b7f8 100644 --- a/static/themes/dark.css +++ b/static/themes/dark.css @@ -107,6 +107,10 @@ --command-palette-active-item-text-color: white; + /* Overview */ + --overview-tile-border-color: #454545; + + /* Only in Options */ --keyboard-shortcut-background-color: #222; diff --git a/static/themes/light.css b/static/themes/light.css index ef4c7387..cf11d864 100644 --- a/static/themes/light.css +++ b/static/themes/light.css @@ -107,6 +107,10 @@ --command-palette-active-item-text-color: black; + /* Overview */ + --overview-tile-border-color: #dfe1e5; + + /* Only in Options */ --keyboard-shortcut-background-color: #e1e1e1;