Skip to content

Latest commit

 

History

History
256 lines (172 loc) · 19.9 KB

File metadata and controls

256 lines (172 loc) · 19.9 KB

LocalShare Design Specification

LAN file and folder sharing tool. The desktop app starts a share; guests scan a QR code or enter an address in a desktop or mobile browser. This file is an implementation handoff document. All values, colors, and component behavior come from the current design. Do not change them by feel.


0. Design DNA

Warm cream background + brick-red accent, serif titles, and monospace technical values such as IP addresses, ports, paths, sizes, and dates. Restrained, quiet, and ticket-like. Use medium radii, light shadows, and generous spacing. Do not use gradient backgrounds, emoji, or colorful glow effects.


1. Color Tokens

All colors are generated by getTokens(t) and must be implemented as theme variables with light and dark mode support. The accent color can be user-selected.

1.1 Accent

Default: #df4f28 (brick red-orange). Available options: #df4f28 / #2a6fdb / #1f8a5b / #7a5ae0.

  • accentSoft: light mode rgba(223,79,40,0.12), dark mode rgba(223,79,40,0.18). Note: accentSoft should follow the selected accent hue at 12% / 18% opacity.

1.2 Neutral and Semantic Colors

Token Light Dark Purpose
bg #efeae1 #1b1814 Page / window background
surface #fdfbf7 #262219 Cards / inputs / ticket front
surfaceAlt #f5f0e7 #211e16 Hover state and secondary fills
field #efe9de #1d1a14 Address bar / port input background
ink #2a261d #f2eee5 Primary text
inkMute #8c8475 #a59d8c Secondary text and helper copy
inkFaint #b4ab99 #6d675a Weakest text / placeholders / dates
line #e7dfd1 #37322a Default borders and dividers
lineStrong #dacfbd #494238 Strong borders / dashed lines / focus
ok #2f9e57 #2f9e57 Success: port available, copy succeeded
danger #c43c1c #ef8a6e Danger / stop / invalid port
warn #b67708 #e0a83a Warning / occupied port

The design canvas neutral background is #f0eee9, very close to bg.


2. Typography

Use three font families with strict roles:

  • display for titles: "Source Serif 4", Georgia, serif, weight 600. Users can switch titles to sans; when titleSerif=false, display falls back to sans.
  • sans for body and controls: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, "PingFang SC", sans-serif.
  • mono for technical values: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace. All IP addresses, ports, URLs, paths, file sizes, item counts, and dates use mono.

Size / Weight Reference

Role Size Weight Family Notes
LocalShare wordmark 22-28 600 display Empty state 28, compact header 22
Client page title, e.g. Downloads 44 / 30 mobile 600 display Slightly tight display letter spacing
Section eyebrow 11-12 700 sans Accent color, modest letter spacing
Card / Settings title 16-21 600-700 display/sans
Body / list item name 13-16 500-600 sans
Helper / secondary text 11.5-12.5 400-600 sans inkMute
Technical values 11-14 500-700 mono Address, port, size, date

3. Spacing, Radius, Shadow, Border

  • Radius: buttons 10-12; cards / tickets 14-18; inputs 8-12; pills / status dots 999; file and folder glyph squares use size * 0.26~0.28.
  • Border: default 1px solid line; emphasis and dashed rules use lineStrong; ticket tear line uses 1.5px dashed lineStrong.
  • Window padding: desktop app card LSWin defaults to 20-24.
  • Shadows:
    • Desktop card: 0 1px 2px rgba(0,0,0,.10), 0 18px 50px rgba(40,30,15,.16)
    • Browser window: 0 1px 2px rgba(0,0,0,.10), 0 20px 60px rgba(40,30,15,.18)
    • Ticket / boarding pass: 0 10px 30px rgba(40,30,15,0.10)
    • QR card: 0 8px 24px rgba(40,30,15,0.08)
    • Dropdown / popover: 0 12px 34px rgba(40,30,15,.20)
    • Primary button: 0 1px 2px rgba(0,0,0,.12); stop button resting shadow 0 1px 2px rgba(0,0,0,.18), hover 0 4px 14px <danger>59

4. Iconography

Use custom 16x16 line icons with stroke=currentColor, strokeWidth 1.6-1.8, and rounded caps. No emoji and no colorful filled icons. Existing icon names: copy / open / globe / wifi / file / swap / stop / play / chevron / qr / help / check / phone / search / sort / x. New icons must keep the same visual weight and 16px grid.


5. Components

5.1 Buttons

Component Form Height Purpose
PrimaryBtn Solid accent, white text, weight 650 44, usually full width Primary positive action: choose files/folders, broadcast again
GhostBtn Transparent + line border; hover uses surfaceAlt / lineStrong 34 Secondary actions: change, reshare, view all
DangerBtn Solid danger, white text, weight 700 34 Destructive action: stop sharing. Must be visually prominent; do not render as ghost
  • IconBtn: 34x34 square, radius 10, for gear/help icon-only entries.
  • Button icons: 14-15px with gap 6-7 from text.

5.2 Status Pills

  • StatusPill: radius 999, surface background + line border, containing a status dot, label, vertical divider, and :port in mono.
    • States: broadcasting uses accent + pulsing dot; connected shows the visitor line with device name; stopped uses inkMute without pulse.
    • Visitor summaries: one visitor <name> is browsing; multiple visitors <name> and N others are browsing; unknown names fall back to the IP suffix. Long text truncates at the end.
  • Dot: status dot. live adds lsPulse expansion animation, 1.8s ease-out infinite.
  • Chip: radius 999 label. Accent state uses accentSoft background + accent text; normal state uses transparent background + line border.

5.3 Inputs and Switches

  • Port input: field background, mono, about 72px wide; mono prefix at left, e.g. 192.168.31.223 :.
  • Search input: surface background, search icon at left in inkMute, circular x clear button when non-empty.
  • Switch: 40x24, radius 999. On = accent background with handle on the right. locked lowers opacity to 0.55 and disables interaction, used for "Read and download, always on".

5.4 Cards and Containers

  • LSWin: borderless Mac utility window, 40px title bar with three traffic-light dots (#ff5f57/#febc2e/#28c840) and a flex-column content area. Every desktop app screen is wrapped in LSWin.
  • Ticket card / VariantB: surface card with radius 18. Top stub contains file metadata, middle tear rule uses side notches filled with bg plus a dashed line, bottom contains QR and address.
  • Bracket: L-shaped corner marks for client lists (lineStrong, 2px), used as a viewfinder decoration.

5.5 File and Folder Glyphs

  • FolderGlyph: rounded square, accentSoft background + accent folder shape.
  • FileGlyph / RowIcon: rounded square, color-coded by file type. Center shows extension in mono or a globe icon plus extension.

TYPE_TINT values: web #c4451f, spreadsheet #1f8a5b, image #2a6fdb, PDF #cf4444, Markdown #7a5ae0, document #a9772a. Background is the same color at 12-13% opacity. In dark mode, all type backgrounds use rgba(255,255,255,0.06).

5.6 QR and Address

  • QR: decorative placeholder in mocks, deterministic pseudo QR, white background, rounded corners, foreground #1a1813 (dark mode #0e0c09). Replace with a real QR code in implementation, but keep size, radius, white background, and padding.
  • CopyPill: field rounded bar. Left side is mono address with ellipsis; right side has copy button with green check for 1.3s after success and an open-in-browser button.

6. Required Product Behavior

6.1 Demo State

demoState has broadcasting / connected / stopped. It controls StatusPill, whether QR appears dimmed, and the bottom action button state: stop versus broadcast again.

6.2 Permission Model and Read-Only / Writable Linkage

Permission object: { read(always locked on), add, edit, del }. Use permSummary(perm) as the single derived source. Every sharing UI reads from it; do not hard-code labels in individual screens.

  • If any write permission is enabled (add/edit/del), writable=true.
  • tag: read-only -> Read-only; writable -> Read/write.
  • eyebrow: LAN · Read-only share versus LAN · Read/write share.
  • chips: read-only = [Read-only, Downloadable]; writable = [Downloadable, Uploadable?, Editable?, Deletable?] depending on enabled permissions.

Linked surfaces driven by the same permission state: empty-state eyebrow, folder-share card eyebrow and permission chips, single-file ticket "Sharing · read-only/read-write", Settings "Current: read-only/read-write" tag, bottom warning copy, and alternate VariantA/C eyebrows. Changing write permissions in Settings must update the wording at the top of other screens immediately.

6.3 Live Port Validation

Validate as the user types via validatePort:

  • Empty -> Enter a port number (error).
  • <1024 -> Must be >= 1024 · lower ports are reserved by the system (error).
  • >65535 -> Out of range · maximum port is 65535 (error).
  • Occupied sample set (80,443,3000,5000,5432,3306,8000,7890) -> Port X is already in use (warn) and show a Use :nextAvailablePort button that fills the next available port.
  • Otherwise -> Port available (ok).

Visuals: input/container border follows status color (ok keeps default line; warn/error use semantic colors). Right-side badge shows Available / In use / Invalid with check/help icon. Helper line follows status color; ok copy says changing the port restarts the service and shared links need updating.

6.4 Client Folder Page: Filter / Search / Sort

Toolbar sits below the title and above filter chips: search input on the left, sort dropdown on the right.

  • Type filters: All / Folders / Web / PDF / Images / Markdown / Tables / Documents. They must actually filter items; Folders means directories, the rest use item.type.
  • Sort dropdown: Default order / Name A-Z / Name Z-A / Newest first / Oldest first; selected item gets a check. Folders always group before files, then sort within each group by the selected key.
  • Count: while filtering/searching show N / 304 items; otherwise 304 items.
  • Right side of each row shows creation date in mono; when sorting by time, emphasize this column.
  • Empty result state: No matching files with helper copy.
  • When dropdown is open, add a transparent fixed overlay that closes it on click.
  • Parent directory row: non-root listings always have a fixed first row for "Back to parent", with a dashed icon border and muted text. It is excluded from filtering/search/sort and remains visible in empty directories. Breadcrumb segments, including root, are clickable with dotted underline affordance.
  • Open behavior: directories navigate in place; files open in a new tab via target=_blank, matching the external-open arrow at the row end.

6.5 Client Preview Pages: Markdown / JSON / CSV

Browser navigation to .md(.markdown) / .json(.geojson) / .csv(.tsv) renders a preview page instead of raw text. The preview shares the PreviewPage shell and uses the same URL as the file, so relative references resolve naturally.

  • Shell: kicker based on permission wording -> serif title from filename -> clickable breadcrumb with "View raw" mono link on the right -> viewfinder content card -> footer credit. Loading/failure states use muted mono copy: Loading... / Failed to parse · View raw.
  • ?raw=1 always returns original text; curl/scripts with non-HTML Accept headers automatically receive raw text.
  • Markdown: serif headings, mono code/tables, accent left border on blockquotes, rounded bordered images with max-width:100%. Render with vendored marked. Raw HTML is always escaped and shown as visible text, never executed.
  • JSON: collapsible tree using native details, first two levels expanded by default; type coloring for strings/number/boolean/null; lazy child construction; "show more" batches every 500 items; strings truncate at 200 characters and expand on click. Search traverses the full data tree and displays flat matches as full path + value up to 500 results. Toolbar meta shows Object · N keys · size.
  • CSV: first row is always header. RFC4180 parsing with quoted fields, "" escaping, and newlines inside fields. Auto-detect delimiter among , ; \t. Header click sorts columns with numeric comparison when sample values look numeric and ▲▼ indicators. Search filters by any cell. Meta shows N / total rows x M columns. DOM renders in batches of 1000 rows with "show more" for large files.

6.6 Network Visibility and Plain-HTTP Notice

  • Visible only on current network switch: in Settings under Listening Port. Row contains a title, helper copy, and Switch. Multi-interface copy: only selected network source can access it; other connected networks cannot. Single-interface copy: only current network can access it, and future networks will not. Default is off, meaning open on all local interfaces; when enabled, the share binds only to the selected source network.
  • Online visitor device names: the line below the share address no longer shows only a count. It uses a small green dot and visitor summary copy. Unknown names fall back to IP suffixes; line is single-line with end truncation.
  • Plain HTTP notice: two restrained gray inkMute placements with lock.open icon: under Access in Settings and after a separator at the end of the "Can't connect?" bubble. Copy: same-network transfers are not encrypted; on public Wi-Fi, others on the same network may see content; avoid sensitive files there. Do not use a colored warning box.

6.7 Text Transfer: Send / Receive

Screen.text, a secondary page with back navigation: header is back arrow + "Text Transfer" + settings gear. Brand name appears only on the home screen. There are two independent one-way channels: sharedText for Mac -> phone and inbox for phone -> Mac.

Screen sections, top to bottom:

  1. QR card / empty state: service running with URL -> QRCard (172x172, padding 18, radius 14) + explanatory text + CopyPill + fallback mono address. Otherwise show empty copy: send text or enable receiving, then scan.
  2. Send text card: surface radius 16, padding 16, line border. Section eyebrow -> PlainTextEditor (field, radius 12, mono 13, min-height 118, custom placeholder compatible with Chinese IME, accent caret) -> PrimaryBtn "Share" or "Update"; show "Withdraw" GhostBtn when already shared.
  3. Allow receiving row: surface radius 14, padding 14. Title + helper copy + Switch. Opt-in and off by default.
  4. Inbox card: visible only when receiving is enabled or messages exist. Header = accent status dot + title + unread badge + Clear action. List shows up to 12 items, then a count. Each row has TextGlyph, source device/IP and relative time, text preview up to 3 selectable lines, copy success check, and delete button.
  5. Stop transfer: DangerBtn "Stop", visible only while service is running and there is shared text or receiving is enabled.

Required behavior:

  • Token is session-scoped: updating text via setSharedText does not rotate the token, otherwise active visitors would be kicked and coexisting file-share links would break. Rotate only at session boundaries: changing share, stopping, clearing, or stopping text transfer.
  • Receive limits: each message <= 64 KB with 413 after read; inbox <= 100 items, dropping oldest. Only in-app indication, no system notifications.
  • Receive pulse dot: Text Transfer entry and running state use PulsingDot size 6 when either send or receive is active.
  • stopTextTransfer: withdraw text, clear draft, disable receiving, delete persistence, stop service, rotate token, and return home.
  • Persistence toggles default off and are separate: remember shared text versus remember received text. Remembering shared text restores the draft only and does not auto-broadcast.
  • Web side: sent text page escapes plain text, has a large Copy button with execCommand fallback because plain HTTP is not a secure context, and auto-links http(s). Receive form appears with the same conditions and placement as guest upload.

6.8 Language Switching / i18n

Settings Language control is a three-segment control matching Appearance segmented controls: Follow System / 中文 / English. Radius 9, height 34, gap 8, equal widths. Selected = accent background + white sans 13 semibold, no border. Unselected = surface + ink + line border, sans 13 medium. Language names are written in their own language; Follow System follows the current app language. Bind to persisted AppState.langPref values: system / zh / en.

Two language domains are independent:

  • Native app uses Settings (AppState.lang / Lang.current snapshot for menus and CLI).
  • Web UI is decided per request from browser Accept-Language with descending q values and q=0 skipped. It must never read the app setting. The desktop app can be Chinese while the visitor's phone UI is English, or the reverse.

Copy categories: static strings use L enum keys with compiler-enforced exhaustiveness; interpolated/plural/order-sensitive strings use LStr; JavaScript-facing strings use i18nJSON, where jsEscape escapes < to block </script>. Adding a language requires adding branches to both L and LStr.


7. Copywriting

  • Bilingual product UI: Simplified Chinese and English, routed through Lang.swift string tables. Keep copy short and restrained. Use · as the separator.
  • Technical values such as IP, port, path, size, item count, and date always use mono.
  • Destructive action verb: "Stop". Positive actions: choose files/folders, broadcast again, reshare.
  • Permission wording follows state: "read-only share" / "read/write", not translated mechanically.
  • Footer credit: Powered by LocalShare · Read-only, adjusted when writable.

8. Layout / Canvas Reference

Mocks use a design canvas with side-by-side surfaces. Implementation should be responsive; these are proportional references:

  • Desktop app screens: 400 x 720 borderless utility window.
  • Desktop browser: about 940px wide, including 48px browser chrome with traffic lights, navigation buttons, and a mono HTTPS/address bar.
  • Mobile client: iOS shell around 390 x 780 with compact address bar.

Configurable product settings should keep the exposed design tweaks: accent color, dark mode, and whether titles use serif.


9. Implementation Red Lines

Do: build themes from tokens; use mono for technical values; make Stop a solid red button; implement permission wording, port validation, search, and sort exactly; preserve shadows and radii.

Don't: gradient backgrounds, emoji, colorful filled icons, ghost-style Stop buttons, screen-local hard-coded "read-only" labels, submit-only port validation, or chips that highlight without filtering.