Docker instances reported most deployment fields as unknown. Report how
and when the container image was built, separately from how the
instance was deployed:
- Dockerfile: optional EDGE_EVER_BUILD_CREATED_AT, EDGE_EVER_BUILD_TRIGGER
and EDGE_EVER_BUILD_METHOD build args, empty by default.
- /api/health (self-hosted-bun only): optional buildCreatedAt and
buildProvenance. deploymentVersionCreatedAt and deployment keep their
meaning and stay runtime-supplied; Cloudflare responses are unchanged.
- docker-image.yml: official images record the build time and their
provenance. Draft-release builds dispatched with release_tag are
github_release, main pushes main_push, other dispatches manual.
The container test checks the health response for a metadata build
with runtime overrides and for a default self-build; the post-publish
audit checks the published image.
- Add an explicit `docker` deployment method. It is never inferred from
the runtime, and the registry source still comes only from
EDGE_EVER_CONTAINER_IMAGE.
- Web/desktop show "Image built at" and "Image build source" for Docker;
Android shows "Image built at". Older clients ignore the new fields.
- Document the build args and runtime variables in both Docker guides.
Co-authored-by: tianma-if <yingwaizhiying@gmail.com>
Opening a shared note on a phone meant copying the link to another
device. Add a collapsible "QR code" section to the note and notebook
share dialogs that renders the public link as a scannable code.
- Rendered with qrcode.react 4.2.0 (SVG, no dependencies), loaded with
the share dialogs only.
- Dark modules on a white panel in every theme so phone cameras can
read it; labelled for screen readers, no native title tooltip.
- Hidden for password-protected links, so the code never replaces the
password the recipient still has to receive separately.
- Copy in all four catalogs.
Co-authored-by: tianma-if <yingwaizhiying@gmail.com>
* fix(web): show the localized label for untitled notes everywhere
Untitled notes are stored with the zh-CN default title, and several views
only replaced an empty title, so they showed 无标题笔记 in every locale.
The visible cases were the public share page (heading and browser tab),
the public notebook site, AI companion cards and mentions, the note link
picker, link text inserted for a note, and the title of a duplicated
note.
Add getMemoDisplayTitle() next to DEFAULT_MEMO_TITLE and use it in those
views, matching what MemoCard already does with isDefaultMemoTitle.
* fix(web): localize diagrams and content label on the public share page
The public share page passed zh-CN to read-only diagrams for every
language other than English, so Japanese and Polish visitors saw Chinese
diagram labels. Resolve the locale with resolveSupportedLocale(), which
the diagram renderer already supports for all four languages.
Also translate the note content aria-label, which was hard-coded in
English.
---------
Co-authored-by: tianma-if <yingwaizhiying@gmail.com>
Public share and form pages only added a robots meta tag from JavaScript,
so the HTML for /share/* and /form/* was served without any indexing or
caching directive, and the form page had none at all. Error responses
from /api/public/* also lacked X-Robots-Tag.
Send one policy everywhere a token-bearing link is served:
X-Robots-Tag: noindex, nofollow, noarchive, nosnippet, noimageindex
Cache-Control: private, no-store
Referrer-Policy: no-referrer
- /api/public/*: shared middleware in the application, so it applies on
Cloudflare and Docker, including 404/403 responses. Routes that already
set Cache-Control keep it.
- /share/* and /form/* HTML: _headers rules for Cloudflare static assets
and the self-hosted static server.
- Referrer-Policy keeps the share token out of Referer headers sent to
linked sites.
robots.txt is deliberately not used to block these paths: a disallowed
URL can still be indexed from external links, because crawlers never see
the noindex header.
Co-authored-by: tianma-if <yingwaizhiying@gmail.com>
The README rewrite in d635c45f changed the "Build & Verify" step but left
the product site's manual deployment copy unchanged, so the
"product site deployment prompts mirror the root READMEs" regression test
fails on main and on every open PR.
* refactor: remove unused imports, variables and parameters
Remove the 110 bindings reported by no-unused-vars:
- 78 unused imports. Nine whole value-import statements disappear:
shadcn/ui input, tooltip, button and switch, MobileWebClipCapture,
AccountSecurityModal and @/lib/api in plugin-host. None of these
modules has top-level side effects beyond displayName assignments,
and @/lib/api is still imported by 60 other web modules. The other
removals are type-only imports or bindings from modules that stay
imported, so module evaluation is unchanged.
- Dead helpers: appendResourceMarkdown, handleTogglePin (mobile),
CheckCircleCheck, moveTargetTitle, moveTargetSelectRef,
handleMoveTargetSelectKeyDown (memo list), copiedLabel (share
dialog), and an unused palette and flowchart in the diagram editor.
- Component props that are passed but never read are dropped from the
destructuring only. Their prop types and call sites are unchanged,
so no caller changes behaviour.
- Tests: iterate Object.values() where keys were unused, and drop
unused fixture bindings and callback parameters.
* ci: enforce no-unused-vars in oxlint
Turn the rule on with the conventional escapes: names starting with an
underscore and rest siblings used to omit a property
(`({ id, ...item }) => item`) are allowed.
The API rejects a save that changes the title while most of the note
content disappears with an English message, and clients showed it
verbatim in every locale.
- shared: add editor.saveState.errorReason.suspiciousOverwrite in all
four catalogs and getMemoSaveErrorReasonKey(), matching the error code
or the server message; the API reuses the exported code and message.
- desktop sync details: translate the item by lastErrorCode.
- Android: map the stored message to its zh-CN source text so the
localized components translate it.
- web editor: show the reason in the save status tooltip and label
instead of the bare "Could not save".
The automatic sync runner reset runningRef inside a finally block, after
awaiting refreshSyncQueueItems(). If reading the queue threw there,
runningRef stayed true and every later call returned early as if a pass
were still running, so automatic sync stopped until the app restarted.
A failing getMobileSyncRetryDelay() also surfaced as an unhandled
rejection from the void-ed run.
Move the settle step into settleMobileSyncPass(), where refreshing the
visible queue and reading the retry delay each handle their own errors
and the runner is always marked idle. Ordering is unchanged: refresh,
mark idle, run a pass requested meanwhile, otherwise schedule the retry.
This also removes the return inside finally reported by
no-unsafe-finally.
Co-authored-by: tianma-if <yingwaizhiying@gmail.com>
* refactor: resolve oxlint correctness findings without behavior changes
- Snapshot iterations that mutate their collection with Array.from()
instead of a spread (live childNodes, self-removing disposers, a Set
extended inside its own loop).
- Drop empty spread fallbacks, redundant regex escapes, Boolean() casts
inside negations, a rethrow-only try/catch and statement ternaries.
- Use startsWith/endsWith for anchored literal checks in the extension.
- List U+FE0F explicitly in the mobile attachment emoji class; the
matched set is unchanged.
- Fix the WeChat copy fixture, where \oint in a template literal lost its
backslash and the block formula never exercised \oint.
- Document the intentional control-character regexes (filename
sanitizing and input validation), the throwing provider stream test,
Astro's generated type reference and the mobile auto-sync finally
block with narrow inline exceptions.
* ci: run oxlint correctness rules on pull requests
Pin oxlint 1.88.0 and enable only its correctness category across apps/
and packages/, excluding build output, vendored code and the iOS app.
Findings are errors and the script passes --deny-warnings, so CI fails
on any finding. no-unused-vars stays off until unused code is removed
in a dedicated pass.
The shape-library trigger opened its menu on every pointerenter. When the
editor replaced the note list under a pointer that did not move, or when
the pointer only passed over the toolbar, the menu opened by itself and
stayed open. For mind maps and flowcharts that menu was modal, so the
back-to-list button could not be pressed until the menu was closed. On
touch screens pointerenter fires right before the pointerdown that
toggles the menu, so a tap could open it and close it again.
- Open the menu on hover only for mouse pointers.
- Make the mind-map and flowchart insert menu non-modal, as the
architecture library already is.