Sno Station
Sno Station은 AI 에이전트를 하나의 팀으로 묶는 오픈소스 워크스테이션으로, 로컬 머신에서 에이전트 협업을 가능하게 함
아직 감지된 업데이트가 없습니다. GitHub 릴리스가 나오면 여기에 보입니다.
개인 계정의 공개 저장소 소유자는 GitHub로 로그인해 운영자를 확인하고 소개를 직접 관리할 수 있습니다. 관리자 승인은 필요하지 않습니다.
로그인한 뒤 확인 파일을 만들어 이 저장소의 기본 브랜치에 30분 안에 커밋해 주세요. 다른 브랜치나 포크의 파일은 인정하지 않습니다. 파일을 올린 뒤 아래에서 확인하면 관리자 승인 없이 관리할 수 있습니다. 자격을 유지하려면 파일을 남겨 두세요.
로그인이 필요하면 로그인 화면으로 이동합니다. 로그인 후 이곳으로 돌아와 원하는 확인 버튼을 다시 눌러 주세요.
프로젝트 폴더에서 /nomorevibe verify를 실행하면 소유권을 확인하고 소개를 직접 관리할 수 있습니다.
1분 만에 확인하기MemDesk is a kanban board, notebook and calendar, inside Gmail. Every card is an email, every note a message in your own mailbox – no server, no database. Chrome on your computer, an app on your phone.
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A Kanban board, a notebook and your week, inside Gmail.
Every card is an email. Every note is a message in your own mailbox.
Your calendar with your tasks in it. No server, no database, nothing to sign up for.
📋 A board made of your email
Columns are Gmail labels and cards are threads. Drag a card and its label
moves with it; drop it on Done and it leaves the Inbox. Give a card your
own title, a note and a colour without touching the email itself. Light or
dark, as Chrome is.README 전문 보기 ↗Columns are Gmail labels and cards are threads. Drag a card and its label moves with it; drop it on Done and it leaves the Inbox. Give a card your own title, a note and a colour without touching the email itself. Light or dark, as Chrome is.
While you read an email, one button in Gmail's top bar says where it is on the board, and files it in a column without opening the board.
Headings, bold and italics, lists, checklists you tick, links, tables you can sort, boards of cards you drag between columns, and pictures you size with a drag. Notes save as you type, and a paste from Word, Google Docs, Excel, a web page or Markdown arrives formatted, a table as a table; paste a screenshot and it is in. Folders nest as deep as you like, and fold away when the tree gets long; star the notes and folders you use most and they wait at the top, on every device.
Open the notes and you are already in it, cursor blinking: one note for whatever needs writing down right now, and the same one on every computer and phone, so a line jotted on the train is waiting at your desk. On the phone it fills the screen under the search box; in Chrome it is open whenever no other note is, and pinned at the top of the list - and it fills the spare corner of the calendar's week in two rows, so you can jot things down as you plan.
The words you searched for are marked in the list and in the open note, with a find bar to step from one to the next.
A third tab: your Google Calendar with Google Tasks woven into it. The week by the hour, as in Google Calendar (with a second time zone beside yours if you like), or as two rows; the month, or the next four weeks as one list. A task with a date sits in its day, ready to tick; the ones without a date, and any that are overdue, wait at the side; a task made from an email opens the email. Every calendar and task list shows or hides with one click. On the phone it is the week as two columns of days, with the month as the eighth, swiped to the next week - or the whole month, a tap on a day opening its week. And it works both ways: open an event and change it, tick a task off, drag either to another day or an event to another time, drag an event's bottom edge to change when it ends, click an empty hour to add one, or press + on a day and type "Dentist 14:30" - it all goes straight to Google Calendar and Google Tasks.
A home-screen app with the board, the notes and the calendar: the board one column to a screen, swiped sideways; the notes opening straight onto your Scratchpad, ready to type, with the same editor, folders and search as in Chrome; your week as two columns of days. It opens on the phone's own copy of the Scratchpad, so you can type before Gmail has even answered; if it was changed on your computer meanwhile, both changes are kept. The board, too, shows at once as you last saw it, and catches up with Gmail behind it. And a panel in the Gmail app puts the open email on the board, one tap for its column.
There is no MemDesk server and no account to make. The board and the notes are views of your own mailbox, and the calendar of your own Google Calendar and Tasks, through Google's API, from your own browser. (A licence key, once licences are on sale, is checked with Lemon Squeezy, the shop that sells them: the key, and nothing of your mail.) MemDesk never sends mail and never deletes mail; in the calendar it deletes an event or a task only when you ask, confirm, and let its Undo pass. See Privacy.
A Kanban board and a notes system inside Gmail, for one person, backed entirely by Gmail itself.
Every card is a Gmail thread and every column is a Gmail label (_Board/To do,
_Board/Doing, _Board/Waiting and _Board/Done to start with). Moving a card
moves the label. Every note is a message in your own mailbox, never sent, filed
under _Notes. There is no separate database and no server: the board and the
notes are views of your mailbox. Because they are ordinary labels and messages,
they show up in the Gmail app on your phone too, so you can file a thread from
the train and see it on the board later, or look up a note. The leading
underscore sorts both labels to the top of Gmail's label list. The board and the
notes editor themselves only exist in desktop Chrome; on a phone, the phone
panel (a small Gmail add-on you install for yourself, see below) puts the
open email in a column from inside the Gmail app, and the phone app puts
the board, the notes, with the full editor, and the calendar on your home
screen.
The Calendar tab reads and changes Google Calendar and Google Tasks with a sign-in of its own, asked for the first time you open it, so the board and the notes never depend on it.
Version 0.39.1 (called Supermail until 0.17.1). Plain JavaScript, Manifest V3, no build step and no runtime dependencies for the extension; the phone panel is one generated Apps Script file.
Sent a link to MemDesk? Then INSTALL.md is all you need: add it to Chrome, click Connect Gmail, done - about two minutes.
Setting up your own copy from this repository? SETUP.md walks through it step by step:
chrome://extensions, and connect it to Gmail through a free Google Cloud
project of your own. About 15 minutes, once.It works with Google Workspace and with @gmail.com accounts.
The manifest carries a public key, so the extension ID - and with it the
redirect URI that every user's OAuth client needs - is the same on every
install:
| Extension ID | lfeogecmdohgofbifobhkolapmjikdih |
| Redirect URI | https://lfeogecmdohgofbifobhkolapmjikdih.chromiumapp.org/ |
The matching private key is not in the repository and is not needed to load the
extension. It only matters if you ever pack a .crx.
chrome://extensions/shortcuts. Press Esc to close it._Board) so Gmail nests them in the sidebar.Board to _Board, which renames every column label under
it) is followed rather than answered with a fresh, empty label. New columns
go under whatever parent the existing ones share._Board, so every computer
and the phone app show the same board. Saving the columns replaces that
message; the old one goes to Trash, like an old version of a note. Each
computer and phone also keeps a copy, used when Gmail cannot be reached.
The first time a computer with columns of its own opens the board, its
layout becomes the one in Gmail. The phone app's own copy never does, as
it may be older; save the columns on the phone to make its layout the one.Card order within each column is stored in this browser (storage.local). The
column layout is kept in Gmail (above), with a copy in storage.sync; your card
edits are stored in storage.sync, so they follow your Chrome profile to other
computers. All of it is kept per Gmail account.
_Notes - _Notes/Work,
_Notes/Work/Clients - so the same tree shows in Gmail's label list on the
phone._Notes (never in your Inbox, never sent, and never
shown in the list of notes), and changing them replaces it. A favourite
note you delete, or folder you remove, just stops showing. On a phone they
are at the top of the folders, behind the button that says where you are.How it works: a note is a message placed straight into your mailbox with
messages.insert, labelled _Notes (and its folder's label, if it is in one)
and nothing else - not Inbox, not unread. It is addressed to you, with you as
Reply-To, but it is from "Notes" at a reserved address that can never send or
receive mail ([email protected]): Gmail files anything from your own address
under Sent, whatever labels it was given. A note is two renderings of the same
content: an HTML part, which is the record - Gmail shows it, formatting and all,
and the editor reads it back - and a plain-text part with bullets, numbers and
☐ / ☑ for Gmail's previews and plain-text mail clients. The editor reads the HTML
with its own small reader rather than the browser's HTML parser, and keeps only
what the toolbar can make: anything else becomes text. It carries an X-Gkb-Note header with the note's own id.
Gmail messages cannot be changed once stored, so saving inserts a new version
and moves the previous one to Trash. That makes Gmail's Trash a 30-day version
history: open an old version there to copy text back. If two versions are ever
both live (a save cut short, or two computers saving at once), the newest wins
and the other is moved to Trash the next time the list loads. A note saved on
another computer or phone while you were editing it is merged rather than
overwritten, as soon as this one hears of it (Chrome and the phone app both
check before every save): the newer version comes in with your edits on top,
and a paragraph changed on both sides is kept in both versions.
A picture in a note is a message of its own, written once and never changed,
and the note's HTML names it by its message id. So saving a note sends only
its text, however many pictures it has, and the old versions in Trash hold no
copies of them. The picture's message is shaped like a note - from "Notes",
with an X-Gkb-Note id of its own, so the rules for what may be inserted and
trashed hold for it unchanged - and carries X-Gkb-Picture. It has no label,
so it sits in All Mail and in no list; its subject says which note it was put
in. A new picture is held in the page, made smaller, until the note is saved:
the save puts it in first, then writes the note naming it. A picture pasted
in from another note is saved as a copy of its own, so every note owns its
pictures. After a save, the pictures the note no longer has go to Trash (each
read first: only a picture of ours), and deleting a note takes its pictures
there too, Undo bringing them back. Pictures are read whole (format=raw) as
they come into view, a few at a time, and the most recent are kept in the
page. One that is in Trash while the note's saved version still has it (taken
out on another computer as this one put it back) comes back out of Trash when
the note shows it.
Anything else filed under _Notes shows up too - an email you sent yourself
from your phone, say. Its bold, italic, lists and links come across; the rest
reads as text. It is marked "From an email".
Editing it saves a new note in its place and takes the email off the list
(it keeps the email, just without the _Notes label); Delete does the same.
Like the columns, the notes label is followed by id, so you can rename _Notes
in Gmail and the notes follow.
MemDesk is free while it is in preview: the logo's menu says Licence: free while in preview, and nothing is checked or kept.
Once licences are on sale:
Chrome's sync storage is small: 100 KB in all, and at most 512 entries. Each edited card takes one entry, so there is room for hundreds of renamed cards, fewer if every one carries a long note (notes are capped at 500 characters, titles at 200). Taking a card off the board deletes its edit. Moving it to Done keeps it. If the storage ever fills, saving an edit says so rather than failing silently.
A Gmail add-on for your own account, built with Google Apps Script, that shows at the bottom of an open email in the Gmail app on your phone (and in the side panel of Gmail on a computer). It does one thing: it puts the open email on the board, as the button next to Board does in Chrome. (Until 0.21.0 it also showed and edited notes. On a phone, the notes, with the full editor, and the calendar are in the phone app.)
_Board is a column, To do, Doing, Waiting and Done first in that order,
any others after them alphabetically, and only Done archives. If you change
which column archives in the extension, the panel will not know. With no
board labels at all yet, it says so rather than making them: open the board
once in Chrome or in the phone app.Setting it up takes about five minutes: a new Apps Script project with
two files pasted in, then Deploy → Test deployments → Install. The steps
are in SETUP.md, part 2. To update, paste
the new Code.gs (and appsscript.json, when it changes) over the old one at
script.google.com/home and save; the first
line of Code.gs says which version it is.
What it is allowed to do (the panel and the phone app are one script,
with one set of permissions): read and change your mail's labels and insert
messages (gmail.modify, the same as the extension), run as a Gmail add-on and
see which message is open (gmail.addons.execute,
gmail.addons.current.message.metadata), read your list of calendars and
read and change their events and your tasks for the phone app's calendar
(calendar.readonly, calendar.events, tasks), and call
Google's APIs (script.external_request, only to gmail.googleapis.com and
the Calendar and Tasks APIs). It keeps the
extension's rules in its own code: it inserts only notes, moves to Trash only
messages it has itself checked are notes, never adds Trash, Spam or Inbox to
anything, and never sends. It runs in Google's Apps Script under your account;
nothing goes anywhere else. To remove it: Deploy > Test deployments >
Uninstall, and delete the project.
How it is built: addon/Code.gs is generated by node tools/build-addon.mjs
from the shared note code in src/lib/ (the same files the extension loads)
and the panel's own files in addon/src/, with small stand-ins for the browser
APIs Apps Script lacks (addon/src/shims.js). npm test fails if it is out of
date, and runs the whole panel against the preview's fake Gmail in a stand-in
for Apps Script (tests/addon.test.js).
The board and the notes on your phone's home screen: the extension's own board and Notes view, served full-screen by the same Apps Script project as the phone panel, and added to the home screen from Chrome. The same Board and Notes tabs, columns and cards, notes, folders, search with the words marked, formatting editor, checklists, find and autosave as in Chrome, laid out for a phone. (Opened on a computer, it is the board and notes as in Gmail, in a tab of their own.)
/mail/mu/mp/…#cv/All Mail/<id>): the usual address shows
only the inbox there, and the Gmail app cannot be sent to one email from a
web page - every way in was tried on Android, and opened the inbox.
The columns are the same as in Chrome: the layout kept in Gmail. Until
there is one, they are your _Board labels as Gmail has them. Card edits
are the app's own, the same on every phone and computer you open it on
(but separate from the extension's, which Chrome keeps).Setting it up is one more step in the phone panel's project: Deploy →
Test deployments → Web app, open its address in Chrome on the phone, and
Add to Home screen. See SETUP.md, part 3.
The address ends in /dev: it always runs the code last saved, and only you
can open it.
How it works: doGet in Code.gs serves the page, which is
addon/app/index.html with the extension's own files packed into it
(src/content/board.js, store.js, notes.js, note-editor.js,
calendar.js, ui.js, styles.js and the shared code). They go in as base64 inside one small loader script, which runs
them in order and names any that fails: Apps Script takes a page's inline
scripts out and runs them itself, and an app made of many plain scripts did
not survive that (it came up blank). With them come addon/app/remote.js, which gives the notes a store, and
the board its Gmail and its settings, that ask the script through
google.script.run instead of the extension's background worker (a board's
worth of cards in one round trip); addon/app/remote-board.js, the column
layout (Gmail's, or until there is one, the labels); and addon/app/shell.js, which hands the
page to the board (ns.boardFrame) and adds the phone layout and the back
gesture.
On the script's side, addon/src/app-server.js keeps the worker's rules: it
inserts only notes, moves to Trash or back only messages it has itself
checked are notes, takes an email kept as a note off the list without
touching it otherwise, and deletes a folder only when Gmail says it is empty;
for the board it reads labels and threads, makes and renames labels, and
changes the labels on a conversation - never Trash, Spam or the Inbox, never
anything sent or deleted; for the calendar it makes the same four reads as
the extension (the calendar list, a calendar's events, the task lists, a
list's tasks), and the same changes - one event or one task added, changed
or deleted, with only the fields the editor edits, and an event's version
named so that one changed in Google meanwhile is refused - and nothing else.
The app's card edits, and its copy of the column layout, are kept in the
script's user properties; the layout itself it reads and saves in Gmail
(appLayout, appSaveLayout), moving to Trash only an older version of the
layout. It runs as you, under the phone panel's permissions, which since
0.17.0 include the calendar, and since 0.24.0 changing it. Google does not
ask for new permissions by itself once a script has been allowed some, so if
the calendar (or changing it) has not been allowed yet, it says so with an
Allow button, which opens Google's page for the script (the function
allowCalendar, run once in the script editor, does the same).
The privacy policy is PRIVACY.md. In detail:
gmail.googleapis.com, and, for the calendar, to the Calendar and Tasks
APIs (www.googleapis.com/calendar, tasks.googleapis.com) and Google's
userinfo endpoint - from your browser. The phone panel runs in Google's Apps
Script, under your own account, and talks only to the same APIs (see
The phone panel for what it may do).api.lemonsqueezy.com/v1/licenses/): the key, and
the first time a name for the activation - nothing of the mailbox, and
never a Google token (a test fails if the phone app's script would send
one there). While MemDesk is in preview, it is not asked anything. See
The licence.chrome.storage.session). It is held in memory, is not readable by the Gmail
page or by the content scripts, and is gone when Chrome closes. The implicit
grant issues no refresh token, so nothing long-lived exists anywhere. Tokens
last an hour and are renewed silently while you are signed in to Google.gmail.modify is broad. It permits reading mail
(including message bodies), changing labels, archiving, moving to Trash, and
technically sending mail. It does not permit permanent deletion; that
needs the full https://mail.google.com/ scope.INBOX when archiving. For the
notes, it reads the messages under _Notes and its folders (bodies
included), inserts new notes, moves notes between folders, moves its own old
versions and deleted notes to Trash, and creates, renames and deletes empty
folders under _Notes; it inserts each picture in a note as a message of
its own with no label, reads it back whole, and moves the pictures taken out
of a note, or of a deleted note, to Trash. It never
sends and never permanently deletes, never touches Spam, and moves nothing
to Trash but its own notes and their pictures. The background worker enforces this with an
allow-list: any other Gmail API call, any DELETE, any attempt to add
or as a label, and any insert that is not a note (a message carrying
the header, filed under user labels only - never Inbox, Sent,
Drafts, Spam, Trash or unread) is refused before a token is even fetched.
Before trashing a message, the worker reads that message's headers itself and
refuses unless it is a note. The only label it will delete is an empty notes
folder: before a , it reads the label, the full label list, and
whether any message is still filed under it, and refuses anything that is not
a folder under the notes label with no notes and no subfolders.These depend on Gmail's page rather than on a documented interface. All of them
live in src/content/gmail-hooks.js, and each fails quietly.
document.title
("Inbox (3) - [email protected] - Gmail"), falling back to the aria-label of the
Google Account avatar link. If Gmail changes both, the board says it cannot
tell which account the tab belongs to.data-legacy-thread-id attribute on the
conversation's subject heading, polled once a second while the tab is visible.
If Gmail drops the attribute, the "Add to board" button simply stops
appearing. Everything else keeps working.header#gb, role="banner") and its search form (form[role="search"]):
they go after the search box's column, before Gmail's own icons, and back
in within two seconds if Gmail draws the bar afresh. If Gmail changes
either, the buttons float at the bottom left instead, as they always could.location.hash to #all/<threadId>, using the
legacy hex id the API returns. Gmail currently accepts these and redirects to
its newer ids.response_type=token) still works for Web
application clients, but Google discourages it. If it is ever retired, the
fix is authorisation code with PKCE in the same launchWebAuthFlow call.None of these, nor real OAuth, can be exercised outside real Gmail. The tests below cover everything else.
The display name appears in exactly these places:
manifest.json: "name"manifest.json: "action"."default_title"src/shared/ns.js: APP_NAMEREADME.md: the titleaddon/appsscript.json: "addOns"."common"."name", the phone panel's name
(its cards take theirs from APP_NAME; rebuild addon/Code.gs after a rename)APP_NAME at runtime, so no edit is needed.RUNS in tools/icon-svg.py; then run it and
tools/make-icons.mjs again, and copy the new path into LOGO in
src/content/ui.js (a test says if they differ).The licence (LICENSE), the guides (INSTALL.md, SETUP.md, PUBLISHING.md, PRIVACY.md),
the help pages (docs/), store/listing.md and the website (site/index.html, and the demo's site/demo/) use the name in their
text, and link to the repository by its address. The pictures take the name from APP_NAME: run
tools/readme-shots.mjs again.
Nothing internal carries the name: not the gkb namespace, the storage keys
(clientId, columns:<email>, order:<email>), the CSS classes or the element
ids. A rename therefore leaves stored data, the extension ID (derived from the
key) and the redirect URI unchanged. tests/static.test.js fails if the name
turns up anywhere else. The app name on the Cloud consent screen is set
separately in the Cloud console. One exception, from before the last rename:
the phone app keeps its settings in the browser under supermail., its name
until 0.17.1, and keeps that prefix so they survive.
Unit tests (Node 18 or later, no installs):
npm test # same as: node --test tests/*.test.js
These cover auth URL building and redirect parsing, the proxy's allow-list
(Gmail's and the calendar's), the calendar's dates, weeks and views and how
events and tasks become day items (in a time zone with summer time), order
merging, the label arithmetic for moves, entity decoding, address parsing,
account-from-title detection, relative dates, and static checks on the source:
no HTML-string sinks, the name only in the rename spots, and the preview's
script list matching the manifest. Node 22's runner does not accept a bare
directory (node --test tests/), so the glob form is used. Node expands it
itself, so it works on Windows too.
Browser checks use playwright-core, which is deliberately not a dependency.
Either install it without saving it (node_modules/ is gitignored), or point
PLAYWRIGHT_CORE at an existing copy:
npm i --no-save playwright-core
npm run test:preview # (a) content scripts against a fake Gmail
npm run test:extension # (b) the real unpacked extension
npm run test:app # (c) the phone app at a phone's size
npm run test:demo # (d) the website's demo, built and served
CHROMIUM_PATH chooses the browser. It must be full Chromium, because
chrome-headless-shell cannot load extensions. Otherwise the newest full
Chromium found in /opt/pw-browsers, in PLAYWRIGHT_BROWSERS_PATH, or in
Playwright's own folder on Windows is used, and failing those, whatever
Playwright picks.SCREENS_DIR is where screenshots go. The default is a folder in the system
temp directory.dev/preview.html under Trusted Types and drives it: drag between
and within columns, the ⋯ menu, search-add, column settings, Esc, the dock
button, dark mode, the connect and setup states, a failing move, and label
creation. It checks the fake mailbox's labels after each step. And the
calendar, against a fake Calendar and Tasks: the week, month and agenda,
the keys, showing and hiding sources, connecting it, Tasks not allowed, and
the narrow week; the week by the hour and its second time zone; and
changing it: the editor, ticking, dragging to a day and to a time, an
event's bottom edge, adding (with the + and in the hours), deleting with
its Undo, and every change refused that should be (changed in Google
meanwhile, not allowed yet, failing).--load-extension. It tries new headless first and
falls back to xvfb-run if the service worker does not appear. It checks the
worker, the extension ID, the setup page, the allow-lists, and that the content
scripts inject into a stand-in mail.google.com page and reach the worker.google.script.run wired to the real Code.gs running in the Apps Script
stand-in against the fake Gmail: the list and folder chips, opening a note,
typing and formatting with autosave, ticking a box, the back gesture,
search, a new note, saving on switching away, the board, the calendar's
phone week with its chips and swipes, and dark mode.Code.gs running beside it
in the page, a Scratchpad save through it, the board and the week; that
links to Google say so instead of leaving; and that a reload starts
afresh.memdesk.app/demo/ is MemDesk with a made-up
mailbox, notes and calendar, built by tools/build-site.mjs from the real
code - nothing written for the demo alone but its bar and its glue
(site/demo/):
?showcase mode: the
extension's content scripts in a fake Gmail, opening on the board. The
demo's bar takes the place of the developer's strip./demo/phone/) it is the phone app's page, as
Code.gs serves it. Code.gs itself runs in a hidden frame of the page,
with Apps Script's services standing in (dev/apps-script-services.js,
shared with the tests) against the same fake Gmail, Calendar and Tasks;
site/demo/run.js hands it each google.script.run call. On a computer
the app sits in a phone's frame.main, so
the demo is always the version released.Open dev/preview.html straight from disk in Chrome. It runs the real content
scripts against a fake chrome.* and an in-memory mailbox of about 25 invented
threads. A strip at the top toggles an open thread, sends the shortcut, and
switches between states: ?state=auth_required, ?state=not_configured,
?fail=modify, ?fresh (no labels yet), ?page=3 (truncated columns) and
?latency=600. ?showcase swaps the test notes for tidy ones in a deeper
folder tree, for pictures; ?pictures adds a note with two pictures in it
(one of them in Trash). The calendar has a fake Calendar and Tasks of its
own, a week of invented events and tasks around the current one;
?calendar=signin makes it ask to connect first, and ?calendar=notasks
answers as if Tasks had not been allowed.
icons/icon.svg is the drawing: "Md" on a violet circle, a member of the
Supervertaler family of icons. python3 tools/icon-svg.py draws it (the letters are turned into paths from Liberation
Sans Bold, so it needs fonttools), and node tools/make-icons.mjs renders it
with Chromium to icons/icon-{16,32,48,128,192}.png. The mark inside the app
is the same drawing, kept in step by a test.
node tools/readme-shots.mjs takes them: the real board and notes in the dev
preview, and the real phone app, against the fake mailbox in its tidy
?showcase mode, framed in a browser window and phones, into images/.
A link to any page of memdesk.app, pasted into WhatsApp, LinkedIn, Slack,
iMessage or X, shows images/og-image.jpg: the logo, large, on the landing
page's violet. WhatsApp's small preview is the middle of the picture shrunk to
about 115 pixels, where anything smaller than the logo cannot be read; the
apps print the page's title beside it. python tools/og-image.py draws it
with Pillow and numpy, from icons/icon-512.png. Run it again when the icon
changes, and raise SOCIAL_VERSION in tools/build-site.mjs, because the
apps keep the picture they fetched. It stops, rather than write a picture,
if the file would reach 300 KB (above which WhatsApp shows none).
tools/build-site.mjs puts the og: and twitter: tags in every page, from
its own title and description, and tests/site.test.js checks them.
manifest.json
src/shared/ns.js namespace, APP_NAME, storage keys
src/lib/ pure logic, shared by content scripts, worker and tests
util.js entities, addresses, account detection, dates, pool
auth.js auth URL, redirect parsing, API allow-list
calendar-logic.js the calendar: dates and weeks, views, events and tasks
as day items, what may be asked of Calendar and Tasks
board-logic.js columns, order merge, move label diffs, summaries, card edits
notes-logic.js building and reading note messages and pictures' messages,
what may be inserted
note-format.js the formatting model: HTML out and back in, plain text,
Markdown and pasted HTML in
search-logic.js the words in a query, where they occur, excerpts
src/background/sw.js OAuth (launchWebAuthFlow), the Gmail API proxy, and the
calendar's own sign-in and proxy
src/content/ classic scripts, in manifest order
gmail-hooks.js every assumption about Gmail's page
api.js, store.js messaging and the shared data layer
notes-store.js notes: list, read, save (insert + trash), delete
ui.js, styles.js DOM builder, icons, menus, toasts, shadow hosts
board.js, dock.js the overlay (header, tabs, board) and the corner buttons
pictures.js pictures in notes: made smaller, held until saved, read and kept
note-editor.js the formatted editor and its toolbar
notes.js the Notes tab: list, editor, autosave; the calendar's scratchpad tile
calendar-store.js Calendar and Tasks: sources, and what is on in a range
calendar.js the Calendar tab: week, month, agenda, phone week
main.js wiring
src/options/ setup page
dev/ preview page, fake Gmail, fake Calendar and Tasks
addon/ the phone panel and phone app (Apps Script)
appsscript.json its manifest
Code.gs generated: shared note code + addon/src + the app's page
src/shims.js btoa, TextEncoder, URL and friends for Apps Script
src/panel-logic.js pure: board columns from labels, moves, Gmail's ids
src/gmail.js, store.js Gmail over UrlFetchApp, and the notes and board on it
src/cards.js the panel's cards and what their buttons do
src/app-server.js the phone app's server side: notes, folders, board,
calendar reads, the page
src/triggers.js the top-level functions Apps Script calls
app/index.html the phone app's page, filled in by the build
app/remote.js its notes store, over google.script.run
app/shell.js its full-screen frame and phone layout
tests/ unit tests; tests/e2e/ browser checks;
helpers/apps-script.js, a stand-in Apps Script
(its services in dev/apps-script-services.js)
site/ the website, memdesk.app; site/demo/ the demo's bar and glue
docs/ the help pages, memdesk.app/docs/: the manual for the person
using it, one Markdown file a page (README.md the overview)
icons/icon.svg the icon; icon-*.png are rendered from it
images/ the README's pictures, and the link preview's
tools/icon-svg.py draws icons/icon.svg
tools/make-icons.mjs renders the icon PNGs
tools/readme-shots.mjs takes the README's pictures
tools/og-image.py draws images/og-image.jpg, the link preview's picture
tools/build-addon.mjs builds addon/Code.gs
tools/build-site.mjs builds the website, its help pages and its demo
SETUP.md step-by-step setup of your own copy
INSTALL.md installing from the store and a shared phone app
PUBLISHING.md for the publisher: the store build, the shared sign-in
PRIVACY.md the privacy policy
store/ the Chrome Web Store listing and its pictures
tools/package-extension.mjs the store build (dist/, not committed)
LICENSE the licence: source available
Source available. Anyone may read, audit and build MemDesk's code, to check what it does with their mail, notes and calendar; using it needs a licence from the copyright holder. Until licences are on sale, a free preview licence covers anyone who sets up a copy for themselves from this repository, or was given one by the copyright holder. The terms are in LICENSE.
Earlier versions of MemDesk were published under the MIT licence, and anyone who has a copy of one of those may keep using it on those terms. This licence covers every version since it was added.
from:anna is:unread_Board: moving a card takes the board's labels
on and off, and never one of yours.@supervertaler.com, say) so colours its cards too. The board's own
_Board labels, the notes' and Gmail's never count; with several coloured
labels, the first two by name show, and the first gives the stripe. A
colour you set with Edit card… wins over them._Notesfrom: or before: work, they are just
not highlighted. Open a result and every match in the note is highlighted,
without touching its text: a bar above the title says "2 of 5", and its
arrows, F3 and Shift+F3 step through them. The ✕ on that bar clears the
search.
The search box above it runs Gmail's own search inside your notes, so it
finds words anywhere in a note and takes Gmail syntax (before:2026/09/01).
New starts a note.- ,
1. , [] , [x] or #, ##, ### and a space at the start of a line
turns it into that list or heading. Enter on an empty list item ends the list;
Backspace at the start of a list item or heading turns it back into text.
Click a link (on a phone, tap it) to open it in a new tab, in a note and
on a board's cards alike. To change a link's words, drag over them to
select them, or put the cursor beside the link and move into it with the
arrow keys; Ctrl+K then changes where it goes.[Picture].**bold**, *italic*, ~~struck~~, [links](…), # headings, - ,
1. and - [ ] lists, and | tables |. A list pasted into a list item
goes in at that item's level, and pasting in the middle of a line carries
the rest of the line along after it. Ctrl+Shift+V pastes the text alone,
exactly as copied. Ctrl+Z straight after a paste takes it back (one paste
at a time). Dropping text onto a note does nothing; dropping a picture
puts it in.TRASHSPAMX-Gkb-NoteDELETEgmail.modify alone, with include_granted_scopes=false. Google treats
every client in a Cloud project as one app, so incremental auth would fold
any other Gmail grant in the same project (a gmail.metadata one, say) into
this token - and Gmail then applies metadata-scope rules to the whole token,
and search (q=) stops working.calendar.readonly (the list of calendars),
calendar.events (their events), tasks and email (to check, through
Google's userinfo endpoint, that it is the account in the tab - the
calendar's token cannot read Gmail's profile). None of them reaches a
calendar itself or its sharing. The worker lets through only four kinds of
GET - the calendar list, a calendar's events, the task lists and a list's
tasks, and one event on its own (a series, for its rule) - and one event
or one task added (POST), changed (PATCH) or deleted (DELETE), with
only the fields the editor edits, how it repeats included: never guests
(who would be sent invitations), never a calendar or a task list. A change
to an event carries its version (If-Match), so one changed in Google
meanwhile is refused rather than overwritten or deleted unseen. A delete is
sent only after it is confirmed and its Undo has passed. The token lives in
session storage like Gmail's, and the two never mix
(include_granted_scopes=false on both). Events and tasks are shown, never
stored.