In Good Company — Marc’s Singapore

Live guide: https://marcs-singapore.val.run/

A personal collection of Singapore recommendations, with an interactive map, friends’ original advice, a private notebook and shared comments.

What is live

  • 73 entries and 82 attributed recommendations from six personal emails.
  • 30 dining entries, 9 bars, 5 outings, 11 escapes, 13 shops and 5 practical tips.
  • 44 mapped entries: 34 in Singapore and 10 regional destinations. Entries at the same building share a marker with a place chooser.
  • Search, category/person/neighbourhood filters, map/list views, and responsive place details.
  • Val Town sign-in. Private saved/visited lists and notes are scoped to each account. Comments are visible to everyone with the guide link; their author or Marc can remove them. Marc can add places.
  • Recommendation data and user content live in the project’s SQLite database. There is no email account connection or inbox access in the deployed app.
  • Only Marc’s signed-in session receives original Gmail message links. Email addresses, signatures, contact details and unrelated conversations are not published.

The guide is publicly readable and requests no search-engine indexing, matching the existing launcher’s approach. Noindex is not an access restriction.

Hosting

Val Town project: marc1/in-good-company

HTTP entry: index.ts. Frontend assets use Val Town’s immutable versioned URLs. The /source route opens the project source. The Sites launcher’s Singapore card now opens the live guide; its other links remain as before.

The UI uses React 18.2, Twind, and Leaflet 1.9.4. SVG illustrations are decorative original drawings rather than venue photography. The map uses OpenStreetMap tiles with visible attribution. Account sessions use Val Town’s OAuth middleware; tokens never reach the client response.

Map and content notes

The map now has a definite responsive height, waits for layout before initializing and refreshes after resizing. This fixes the original zero-height container bug. Pins follow active filters and open the corresponding recommendation.

Locations are approximate building, venue, park or destination points. Chains without a selected branch, online services, and ambiguous locations are left without guessed pins. The location note on each entry explains this; directions and venue websites remain available. Prices and planning tips are attributed to the original email, not presented as live menus or opening hours.

A one-time, cached OpenStreetMap location lookup was performed using an identified client at no more than one request every 1.2 seconds. Geocoding is not called by website visitors. Any future use of the public Nominatim service must follow its usage policy: identify the app, cache responses, stay within the shared request limit, and do not use it for autocomplete.

Validation

  • The production page and data endpoint return HTTP 200; the launcher returns 200 and contains the new guide link.
  • Browser verification: map tiles and pins render; pin selection opens the correct place; search/category filtering updates pins; mobile map height is 358px at a 390px viewport without horizontal overflow; desktop map height is 673px.
  • Production rejects unauthenticated writes (401) and cross-origin writes (403). Client console has no errors. Server logs show only a dependency deprecation warning.
  • Local integration checks exercise the actual production handlers against a temporary SQLite database: private-note and notebook isolation, note removal, comment visibility and moderation, owner-only place creation, and input limits all pass. Platform OAuth is stubbed only in this local test.
  • The live sign-in link reaches the Val Town login page. Completing a signed-in browser session requires the account holder’s login and was not performed during deployment.

Local maintenance

Run the local preview with node preview-server.cjs and open http://127.0.0.1:4317/. It is read-only and uses the local content/seed.json snapshot. Production user data is never written by this preview.

Run node check-source.cjs for syntax checks and node check-backend.cjs for the integration checks described above. Both run locally; the latter uses a disposable in-memory database.

The content directory is a local import snapshot with private source references; do not upload it as a public asset or commit it to a public repository. The one-time build/refinement scripts document the import work and are not the production data store. Make future content changes through the app or explicitly scoped SQLite updates; do not rerun the initial seed over newer content.