Skip to content

Add Links list mode with URL previews and tweet embeds#26

Open
bclinkinbeard wants to merge 2 commits intomainfrom
codex/add-new-links-list-type-with-fetching
Open

Add Links list mode with URL previews and tweet embeds#26
bclinkinbeard wants to merge 2 commits intomainfrom
codex/add-new-links-list-type-with-fetching

Conversation

@bclinkinbeard
Copy link
Copy Markdown
Owner

Motivation

  • Introduce a new "Links" list type so users can save URLs as notes and get rich previews instead of plain text entries.
  • Tweets should embed inline for better reading, and non-tweet links should show a title and short excerpt with a link back to the original.

Description

  • Add a new links mode to the UI and mode selector and surface it in MODE_DESCRIPTIONS, the modal, list badges, and help copy (app.js, index.html).
  • Implement client-side link handling including normalization, tweet detection, twitframe embedding, per-URL preview caching, and UI population (app.js additions: normalizeLinkInput, isTweetUrl, populateLinkPreview, fetchLinkPreview usage, and recording guard for Links lists).
  • Add a server endpoint GET /api/link-preview that fetches upstream HTML with timeout/length limits and extracts og:*, twitter meta tags and <title> to return a safe preview JSON (api/link-preview.js).
  • Add styles for Links mode (badge, preview card, tweet iframe) and change the mode selector layout to support three modes (app.css).

Testing

  • Ran static check with node --check app.js, which completed successfully.
  • Ran the test suite via npm test (which executes node tests.js and node --test on unit tests), and all tests passed (297/297 tests passed).

Codex Task

@vercel
Copy link
Copy Markdown

vercel Bot commented Mar 29, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
voice-notes Ready Ready Preview Mar 29, 2026 7:48pm

Request Review

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5297701376

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app.js
Comment on lines 1213 to +1217
const isTextMode = currentEntryMode === 'text';
const currentMode = listDetailMode ? listDetailMode.dataset.mode : '';
const isLinks = currentMode === 'links';
if (hasList && isLinks && !isTextMode) {
currentEntryMode = 'text';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Recompute isTextMode after forcing Links mode

updateEntryModeUi calculates isTextMode before it forces currentEntryMode = 'text' for Links lists, so the rest of the function still behaves as if voice mode is active. When a user opens a Links list while currently in voice mode, the text entry panel is hidden (and the mode toggle is also hidden for Links), leaving no way to add a link until another UI refresh occurs.

Useful? React with 👍 / 👎.

Comment thread app.js
Comment on lines +20 to +21
accomplish: 'Track tasks with checkboxes and reordering.',
links: 'Save URLs and preview each link as a card.'
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve links mode across sync snapshots

This change introduces a new links mode in the client, but sync sanitization still collapses any non-accomplish mode to capture (sync-snapshot.js, sanitizeListRecord). As soon as a Links list is pushed to cloud, its mode is rewritten to capture and other devices (or restored data) lose Links behavior, which is a data-corrupting regression for synced users.

Useful? React with 👍 / 👎.

Comment thread api/link-preview.js Outdated
Comment on lines +89 to +93
if (!/^https?:$/i.test(parsed.protocol)) {
return jsonResponse({ ok: false, error: 'Only HTTP(S) URLs are supported.' }, { status: 400 });
}

const html = await fetchHtml(parsed.toString());
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Block internal-network SSRF targets in preview API

The new /api/link-preview route performs server-side fetches for any user-provided HTTP(S) URL, but only validates the scheme. In environments where the function can reach internal addresses, this enables SSRF against loopback/link-local/private hosts (for example metadata endpoints), so host/IP range checks are needed before issuing the fetch.

Useful? React with 👍 / 👎.

Comment thread api/link-preview.js
Comment on lines +46 to +47
const text = await response.text();
return text.slice(0, MAX_HTML_LENGTH);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce HTML size limit before reading full response

fetchHtml calls response.text() and only then truncates to MAX_HTML_LENGTH, so the configured cap does not prevent large upstream payloads from being fully downloaded and buffered. A large response can still cause avoidable memory/CPU pressure before truncation is applied.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47b3d40209

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread app.js
if (markdownMatch && markdownMatch[1]) {
return markdownMatch[1];
}
const directMatch = normalized.match(/https?:\/\/[^\s<>"')]+/i);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow URLs that contain closing parentheses

The URL extraction regex excludes ) from the matched URL, so valid links like https://en.wikipedia.org/wiki/Taylor_(Swift_album) are truncated before saving. In Links mode this stores a broken URL (missing the closing parenthesis), which then causes incorrect previews and wrong outbound links for any URL with parenthetical path segments.

Useful? React with 👍 / 👎.

Comment thread app.js
Comment on lines +1136 to +1138
.catch(() => readPersistedPreview(url));

linkPreviewCache.set(url, request);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retry preview fetches after transient failures

Failed preview requests are cached as a resolved null promise via .catch(() => readPersistedPreview(url)) and then linkPreviewCache.set(url, request), so a temporary network/API failure permanently suppresses retries for that URL until a full page reload. This makes previews stay unavailable for the rest of the session even after connectivity recovers.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant