editorial-system

Frontend Architecture

The UI is a single main.js file executed in the browser. There are no build steps or external dependencies; everything runs in vanilla ES2018+.

Core Modules

Routing and Layout

Top Bar Behavior

Page Summaries

| Page | Highlights | | — | — | | Login (renderLoginPage) | Captures Basic Auth credentials, validates by calling /api/users.php, stores credentials in localStorage, and redirects to #/users. | | New Source (renderNewSourcePage) | URL input + “URL取得” fetch button. Uses Firecrawl via /api/crawl.php, detects keywords via /api/detect-keywords.php, shows Markdown + editable fields, and opens a duplicate-check dialog before submission. The dialog enforces: duplicate URL override allowed only when the URL contains a query; keyword duplicates require explicit confirmation; and WORKING_SOURCES_LIMIT (30) is enforced by querying /api/user-counts.php. Successful commits hit /api/sources.php with author id, sanitized URL, Markdown, and keywords. | | Sources list (renderSourcesPage) | Fetches /api/sources.php?author_id=X&state=working and renders cards with title, URL, author, timestamps, comment, and keyword chips. Buttons direct to detail pages. | | Source detail (renderSourceDetailPage) | Loads a single source, displays metadata, and offers inline editing for title/comment/content. Contains actions to update state to done/aborted via api.updateSourceState. | | Users (renderUsersPage) | Shows all users, allows creating new ones, and encourages selecting an author before creating sources. | | User detail (renderUserDetailPage) | Pulls /api/user-counts.php, shows counts per state, and lets admins rename users. | | Keyword list/detail (renderKeywordListPage, renderKeywordDetailPage) | Lists every keyword/count. Detail view calls /api/search-sources.php twice (working/done) to show matching sources. | | Search (renderSearchPage) | For an arbitrary string, fetches keyword suggestions via /api/search-keywords.php, displays them as chips, and queries /api/search-sources.php per keyword cluster. | | WordPress search (renderWordPressSearchPage, renderWordPressKeywordPage) | Either use a free-text query to derive keywords or accept a comma-separated list. Both call /api/wp-post.php with keywords and render WordPress cards. | | WordPress author list (renderWordPressAuthorListPage) | Calls /api/wp-authors.php (list mode) to show local WordPress authors with links into their detail pages. | | WordPress post detail (renderWordPressPostPage) | Fetches /api/wp-post.php?id=…, renders a single card, and shows tags/categories/keywords plus external link. | | WordPress author (renderWordPressAuthorPage) | Calls /api/wp-authors.php + /api/wp-posts.php to display author metadata (name, slug, job title, bio) alongside their latest mirrored posts. | | Logs (renderLogsPage) | Fetches /api/logs.php with limit/page and renders JSON log entries. |

Duplicate Checking Workflow

  1. User enters URL and clicks “URL取得”.
  2. The UI sanitizes the URL (removing query/hash for matching), stores whether a query string existed (required to override URL duplicates), and triggers keyword detection if not already run.
  3. /api/sources.php is queried with state=working and the candidate keywords to detect duplicates by URL and by overlapping keywords.
  4. The modal lists matches grouped by URL/keyword collisions. Editors may override each category by ticking checkboxes (URL override only when the original URL kept query parameters).
  5. Before final submission, the UI calls /api/user-counts.php to ensure the selected author stays under WORKING_SOURCES_LIMIT.

Visual Components

WordPress Rendering Notes

Keeping the UI Fresh