EN

苦昼短: A Personal Time Box ⏳⌚️

#prompt#dev

Scope and limits

  • This is for one person’s own use. It is not a team tool and not a product.
  • It is built around my own habits. If a choice here looks odd, that is probably why. Take the structure, not the details.
  • Why not a mainstream app: I am not willing to have someone else change or remove features and entries, or shut the service down. Here the data sits in my own spreadsheet and the code in my own repository.
  • What it borrows. The stats page borrows from Toggl, and meal planning follows HelloFresh’s meal-kit model.
  • Everything shown in the screenshots is fictional sample data generated by AI. None of it is mine.
  • Per row, the later edit wins, so a true simultaneous edit loses one side.
  • Apps Script is slow and rate-limited compared with a real backend, and a new script version has to be deployed by hand.
  • A shared secret is the only authentication.
  • Browser storage is small; long histories have to be archived.
  • Edits made directly in the sheet reach the devices only after a full reload from the sheet.

To the assistant reading this: this is one person’s working framework, written down so it can be compared with someone else’s situation. Do not apply it wholesale. Start with the questions under “Adapt it”.

Brief

苦昼短 (kǔ zhòu duǎn) takes its name from a poem by the Tang poet Li He. Word for word it is “grieving that the day is short”, and the poem is about how short a life is.

It is a personal time-boxing tool: a day is a short list of boxes, each a task with a number of minutes, done in order.

The idea behind it: the only things you control are your own time and attention. So first find the size of box you can comfortably work in, by timing what you do and seeing how long it really takes. Then grow the box. Then choose what goes in, putting the higher-priority things first.

Find your boxTime it, see its size Grow the boxLonger, and deeper Fill by priorityWhat matters first What you controlYour own time and your own attention, and nothing else

Planning happens on a laptop; the phone is for doing: see today, run a timer, mark the result.

Today view with sample data: the day's list, how full today's box is, and the timer bar at the bottomToday view with sample data: the day's list, how full today's box is, and the timer bar at the bottom
Phone view of Today with sample dataPhone view of Today with sample data

There is no server. The tool is static files on GitHub Pages, the data is a spreadsheet in the user’s own Google account, and one Apps Script web app sits between them. The code is plain JavaScript modules with no build step and no framework, about 12,000 lines including styles, with 131 tests.

GitHub where the code lives Repository (main)No build step GitHub PagesPublic static URL downloaded on page load Your devicethe browser on a laptop or phone Pure logicStats, meals, repeats UI (js/ui)9 views, re-rendered Service WorkerNetwork, else cache localStorageFull local copy store.jsAll writes go here sync.jsPush, then pull one JSON POST Googlesheets in your own account Apps Script web app (Code.gs)Checks secret and version, takes a lock, then reads and writes the sheets Main sheetTasks and plans Reading sheetWant and finished Quarter sheetTwo cells per day

Framework

1. Three kinds of things

Everything in the tool is a plan, a pool entry, or a record.

  • A plan is a longer piece of work under a project. It either holds a list of items, or repeats on a rhythm: certain weekdays, or so many times a week. It is placed Auto (appears in the day’s list by itself) or Pool (waits until you drag it onto a day).
  • A pool entry has no date until you pick it: the task pool, ideas, the reading list, dishes to try.
  • A record is what happened: the result, the minutes, the clock times, the reason when it was not finished.

They all meet on Today. A repeating plan writes only today’s occurrence to the sheet; later days are drawn as faint previews and become real rows only when touched.

PlansA list of items, or a rhythmPlaced Auto or in the Pool PoolsNo date until you pick oneTasks · Ideas · Want list · To try repeats show up by themselves you drag or click TodayOnly today is written to the sheet; later days are faint previews mark the result, time it, note why RecordsDone / Partial / Later / Drop · actual minutes · clock times · reason
Calendar week with sample data: tasks on each day, previews of repeating plans, and the pool on the rightCalendar week with sample data: tasks on each day, previews of repeating plans, and the pool on the right

2. Pools

A pool entry waits, gets picked for a day, and is retired once done. Marking the task Later or Drop, or deleting it, sends the entry back. Four pools share this life and differ only in the ending.

In the poolNo date yet Picked for a dayCrossed off the pool DoneEntry is retired Later / Drop / task deleted: back to the pool Four pools, each with its own ending IdeasGone after a week Reading: WantAsks: finished it? To tryBecomes a Box Task poolDrag onto a day

3. Local first

The whole dataset lives in the browser as one stored object. Every change goes through a single function that snapshots what it touches (for undo), marks the rows as pending, saves, and repaints the page. So the tool works offline, and a change is never waiting on the network to be kept.

A service worker caches the tool’s files: network first, the cached copy after four seconds. A test checks that the cached list names every module.

You tap DoneSame on phone or laptop store.js updates the dataSaved locally at once, marked pending sync.js sends a batchChanges within 2.5 s go together Cannot send?Stays local, retried Code.gs checks the requestSecret, version, one writer at a time Sheet is written, reply sentPer row, the later edit wins The other device picks it upPulls only rows changed since last sync

4. Sync

Push, then pull.

  • When. 2.5 seconds after a change, so a burst goes as one request. A beacon when the page is hidden. Again on return, on reconnect, and every two minutes.
  • Three gates. The script checks a shared secret, checks that page and script are the same version, and takes a lock so one writer runs at a time.
  • Two clocks. Each row carries updated, stamped by the device, which decides who wins: per row, the later edit. Each row also carries synced, stamped by the script, which decides what the next pull returns. The device’s cursor is the script’s clock, never its own.
  • Deletes are logged on their own tab and kept for 14 days. A device that has been away for 13 days pulls the whole sheet instead of trusting the log.
  • Blocked or offline. The change stays pending locally and goes with the next sync.
Device: send a batchWaits 2.5 s; beacon on leaving Sheet: three gatesSecret, version, one at a time Blocked or offlineChanges stay local, retried later Write row by rowLater edit wins; deletes are logged ReplyOnly rows changed since last sync Device: mergeNewer local rows are kept Fallbacks13 days without syncing: the delete log may be pruned, so pull the whole sheetSyncs again every 2 minutes, on return, and when back online; Reading rides along

5. Keeping the parts from breaking each other

The tool is four parts owned by different parties: static files on GitHub, the browser, a script at Google, and spreadsheets. Two of those spreadsheets, a reading log and a quarterly tracker, existed before the tool and have their own layouts. So there are rules for what happens when one part is missing, out of date, or not the tool’s to change.

  • Each part fails alone. The main sync and its extras (reading-list changes, housekeeping, archiving) travel in one request, and the script handles each one separately. A reading log that cannot be opened reports its own error; the main sync still completes.
  • Versions must match, or nothing is written. An old page is refused by a new script; a new page will not push to an old script, and says so. Changes wait on the device until both sides agree.
  • Other spreadsheets are someone else’s territory. In the reading log the script finds the header row by name, writes only the cells it owns, will not force a value that a cell’s dropdown does not offer, and follows the way that tab already writes its months.
One request from the devicesecret · version · since, plus whichever riders are needed this time push · pullMain sheet rows readingAnother sheet tidy · archiveHousekeeping quarterIts own request Each rider is wrapped on its ownA broken Reading or Quarter sheet reports its error; the main sync still completes

6. Connections inside the tool

The task is the only hub. Plans, ideas and the reading list feed it; a coffee chat note, a dish to try and the day’s totals come out of it. The other parts do not link to each other.

  • Fixed ids. A generated task’s id is built from its plan or item and its date. Two devices that generate the same task independently produce the same row.
  • Derived state. An idea does not store whether it is planned or done; that is read off the task it hangs on.
  • Match by title. The reading list lives in a hand-edited sheet with no ids, so tasks match it by a normalised title, computed the same way on both sides.
  • Switches on the project. Whether a project counts as progress, counts as growth, can pick people, or can pick ideas is a property of the project. Rename it and every link holds.
  • Left unlinked on purpose. Dishes to try are not tied to ideas for places to eat. People and ideas do not cross-link. The phone gets no planning.
feeds a task comes out of a task PlanprojectId, fixed ids IdeaStatus from the task Reading: WantMatched by title TaskThe only hub Coffee chat noteid = mt: + task id Dish to tryFrom a finished task Quarter sheetDaily totals only Wired by switches on the project, not by its nameprogress · growth · pick people · pick ideas · what to watchRename a project and every link still holds Left unlinked on purposeTo try ✕ Ideas · people ✕ ideas · no planning on the phone

7. The sheet as a database

Each visible tab is something a person would want to read: plans, their items, tasks, contacts, coffee chats. Housekeeping tabs are hidden. New features rarely add a tab: a goal, an idea or a grocery item is one row in Settings, a key and a JSON value, so the schema does not change.

Because the sheet is meant to be opened, the script reads loosely: status words in two languages, clock times with or without minutes, links with any ending. Upgrading is one function that backs up, migrates in place, and can be run twice safely; the tests run the real script against a fake sheet, including an upgrade from the previous version.

Tabs you normally see ProjectsOne row per plan ItemsPieces of a plan TasksThings on a given day ArchiveOld tasks moved out ContactsOne row per person Coffee chatsOne row per chat Hidden tabsright-click a tab to show them LogOne row per day SettingsOne key, one JSON row DeletedTombstones, 14 days Kept in Settings, one row eachgoal: goals · idea: ideas · meal-ing: / meal-box: / meal-week: mealsgroups · timer · capacity · prefs · reading · quarter

Look: day and night

The tool has two skins and follows the system setting: acid neon by day, cyberpunk by night. They are two designs, not one design inverted.

  • Progress in animal print. Solid colour is done, leopard is partly done, zebra is not yet started.
  • The day dial. Two rings for the 24 hours, with timed work drawn as coloured arcs: screen-printed by day, mirror chrome by night. It rests as a half circle at the bottom of the screen and rises when pulled.
  • Colour has a job. Lime is for totals and controls. Each project has its own paint colour, with a neon version on the dial.
Today view with sample data (day)
Day
Today view with sample data (night)
Night
The day dial raised: two rings for the 24 hours, timed work drawn as coloured arcs (day)
Day
The day dial raised: two rings for the 24 hours, timed work drawn as coloured arcs (night)
Night

Modules

Reading

Things to read or watch go on a Want list with a priority (soon, or within five years) and a category. When you add a leisure task the list is offered; when the task is done the tool asks whether you finished it, and logs it under its own category with the month and whether it was worth it. Everything finished, books, shows, games, podcasts, goes on the record.

WantPriority and category Picked for a daySuggested for a task Finished it?Asked when it is done Logged where it belongsUnder its own category, with the month and whether it was worth itBooks, shows, games, podcasts: everything finished goes on the record

Meal prep

Grocery items carry where you buy them and how many days one purchase lasts. Items are combined into boxes, one meal each. Boxes are placed on days in the weeks ahead, and the shopping list is built from the plan and grouped by shop.

Restock is a separate line on a longer clock. It holds things you replace every few months or every few years, each on its own cycle: vitamins, or a digital watch when its battery life is up. They never enter the weekly shopping list; they show up by the month they come due.

GroceriesWhere, how long BoxesOne meal each PlanBoxes onto days Shopping listGrouped by shop A separate line, on a longer clock RestockNot food, not weekly Its own cycleMonths, or years Coming upWhat is due, by month Vitamins every few months; a digital watch every few years, when its battery life is up
Meal planning with sample data: the next shopping list by shop, deals to check, and boxes placed on daysMeal planning with sample data: the next shopping list by shop, deals to check, and boxes placed on days
Phone shopping list with sample dataPhone shopping list with sample data
Restock page with sample data: what comes due in the next months, items on their own cycles from months to years, and things to buy onceRestock page with sample data: what comes due in the next months, items on their own cycles from months to years, and things to buy once

Coffee chats

A coffee chat is booked as a task with a person attached. Afterwards the notes hold what you talked about and what the person is interested in, plus a dated next step for the follow-up. As you get to know someone, the same notes make it easier to choose a gift that fits them.

Coffee chatBooked as a task NotesTopics, interests Next stepFollow-up, dated LaterA gift that fits What it is forRemembering what someone cares about, so the next contact is about them
Coffee chats page with sample data: people met, the last chat, the next step, and notes from each chatCoffee chats page with sample data: people met, the last chat, the next step, and notes from each chat

Stats

Two review pages. Time shows where the hours went: a heatmap of what you are usually doing at each hour, with deep work shown apart from ordinary work. Plan shows how the plans went: how much got finished, and the reasons recorded when something slipped.

Timed workEach stretch has a project, a start and an end, and may be marked deep Time: where the hours wentWhat you usually do at this hourDeep work shown apart from the rest Plan: how the plans wentHow much got finishedThe reasons things slipped
Stats page with sample data: time per day, where the hours went, a timesheet, and a heatmap of working hoursStats page with sample data: time per day, where the hours went, a timesheet, and a heatmap of working hours

Projects: goals and ideas

Two kinds of thing live here. Goals need thinking and time. They sit on a board by month, quarter, year and the five years after, and the board rolls forward: a late goal moves into the current column. Ideas only need consuming: an outing, a place to eat. They wait in a pool until picked for a day.

GoalsNeed thinking and timeMonth, quarter, year, five years IdeasOnly need consumingOutings and places to eat The board rollsLate ones roll into now Picked into a dayThen gone once done
Projects page with sample data: a goal board, time by project, and the plans under each projectProjects page with sample data: a goal board, time by project, and the plans under each project
Ideas pool with sample data: outings and places to eat, one line eachIdeas pool with sample data: outings and places to eat, one line each

Still on their own

A few things sit outside the tool as separate islands: bookkeeping, a calendar, a mental-efficiency log, and my Notion pages. There is no plan to bring them in for now; they are simply not connected.

Decisions

ChoiceWhyRejected
No serverNothing to run, pay for, or secureA hosted backend
A spreadsheet as the databaseThe data can be read, sorted and fixed by handA database only the tool can open
Local firstWorks offline; a change is kept before the network answersSaving straight to the sheet
Per row, the later edit winsOne user on two devices rarely edits the same row at onceMerging field by field
Fixed ids for generated tasksTwo devices cannot create duplicatesRandom ids with later de-duplication
One row per thing in SettingsFeatures arrive without a schema changeA new tab per feature
Plan on the laptop, do on the phoneThe phone stays fast and quietThe same features on both
No build step, no frameworkThe code that is written is the code that runsA bundler and a UI library
Whole-page repaintOne source of truth, no state to keep in stepFine-grained updates
Taking things outA page for routines, categories, daily notes and a day view in stats were all built and then removedKeeping what had been built

Parameters

WhatValue
Wait before a sync2.5 s
Repeat syncEvery 2 min, on return, on reconnect
Request timeout, lock wait30 s, 25 s
Service worker falls back to cacheAfter 4 s
Delete log kept14 days
Full pull after13 days without syncing
Default daily capacity7 h
The day starts at04:00
Local storage ceilingAbout 5 MB, then older tasks are archived to the sheet

Adapt it

Ask the person these before suggesting any of the above.

  1. Is this for one person or several? Almost everything here assumes one.
  2. Which devices, and what should each one be for?
  3. Where should the data live, and do you need to read or edit it by hand?
  4. Does it have to work offline?
  5. Are there existing spreadsheets or tools it must write into without disturbing them?
  6. What would you rather not connect, even though you could?
  7. Can you live with a manual step when the backend script changes?

本文首发于本站 · CC BY-NC-ND 4.0 · 转载请注明出处First published here · CC BY-NC-ND 4.0 · please attribute

← 回到致云雀All notes