birdclaw 0.6.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/README.md +32 -2
- package/package.json +29 -30
- package/scripts/build-docs-site.mjs +39 -12
- package/src/cli.ts +457 -26
- package/src/components/AppNav.tsx +10 -0
- package/src/components/MarkdownViewer.tsx +438 -72
- package/src/components/ProfileAnalysisStream.tsx +428 -0
- package/src/components/ProfilePreview.tsx +120 -9
- package/src/components/SavedTimelineView.tsx +30 -8
- package/src/components/SyncNowButton.tsx +5 -2
- package/src/components/TimelineCard.tsx +20 -4
- package/src/components/TimelineRouteFrame.tsx +16 -0
- package/src/components/TweetRichText.tsx +36 -12
- package/src/components/useTimelineRouteData.ts +74 -6
- package/src/lib/account-sync-job.ts +15 -3
- package/src/lib/archive-finder.ts +1 -1
- package/src/lib/archive-import.ts +245 -7
- package/src/lib/authored-live.ts +1 -0
- package/src/lib/avatar-cache.ts +50 -0
- package/src/lib/backup.ts +4 -3
- package/src/lib/bird.ts +33 -0
- package/src/lib/config.ts +35 -2
- package/src/lib/data-sources.ts +219 -0
- package/src/lib/db.ts +62 -1
- package/src/lib/geocoding.ts +296 -0
- package/src/lib/link-insights.ts +2 -0
- package/src/lib/location.ts +137 -0
- package/src/lib/mention-threads-live.ts +94 -1
- package/src/lib/mentions-live.ts +187 -40
- package/src/lib/network-map.ts +382 -0
- package/src/lib/period-digest.ts +468 -29
- package/src/lib/profile-analysis.ts +1272 -0
- package/src/lib/profile-bio-entities.ts +1 -1
- package/src/lib/queries.ts +14 -4
- package/src/lib/search-discussion.ts +1016 -0
- package/src/lib/timeline-live.ts +272 -19
- package/src/lib/tweet-account-edges.ts +2 -0
- package/src/lib/tweet-render.ts +141 -1
- package/src/lib/tweet-search-live.ts +565 -0
- package/src/lib/types.ts +37 -2
- package/src/lib/ui.ts +1 -1
- package/src/lib/web-sync.ts +7 -2
- package/src/lib/xurl-rate-limits.ts +267 -0
- package/src/lib/xurl.ts +551 -41
- package/src/routeTree.gen.ts +231 -0
- package/src/routes/__root.tsx +5 -6
- package/src/routes/api/data-sources.tsx +24 -0
- package/src/routes/api/network-map.tsx +55 -0
- package/src/routes/api/period-digest.tsx +29 -3
- package/src/routes/api/profile-analysis.tsx +152 -0
- package/src/routes/api/query.tsx +1 -0
- package/src/routes/api/search-discussion.tsx +169 -0
- package/src/routes/api/xurl-rate-limits.tsx +24 -0
- package/src/routes/data-sources.tsx +257 -0
- package/src/routes/discuss.tsx +419 -0
- package/src/routes/network-map.tsx +1035 -0
- package/src/routes/profile-analyze.tsx +112 -0
- package/src/routes/profiles.$handle.tsx +228 -0
- package/src/routes/rate-limits.tsx +309 -0
- package/src/routes/today.tsx +22 -8
- package/src/styles.css +22 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,69 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
## 0.8.0 - 2026-06-10
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Add localized Today/digest reports through `--language <locale-id>`, `BIRDCLAW_DIGEST_LANGUAGE`, and the period-digest API, with canonical locale validation and separate caches. (#47 - thanks @yujiawei)
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Update runtime and development dependencies, align TanStack Start packages, and move pnpm's native build allowlist into workspace configuration.
|
|
12
|
+
|
|
13
|
+
### Fixed
|
|
14
|
+
|
|
15
|
+
- Add accurate archive-first Sign in and archive-request onboarding, including account-binding requirements, current xurl/bird setup, scoped transport selection, and clean autolink rendering. (#46 - thanks @peetzweg)
|
|
16
|
+
- Respect `OPENAI_BASE_URL` when sending Today/digest requests to OpenAI-compatible API endpoints.
|
|
17
|
+
- Anchor Link Insights' Today range to UTC midnight so it matches stored `created_at` timestamps across local time zones.
|
|
18
|
+
|
|
19
|
+
## 0.7.0 - 2026-06-01
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
|
|
23
|
+
- Stream live `birdclaw import archive` progress to stderr: per-slice parsing ticks (tweets, DMs, likes, bookmarks, follows, media) and chunked write-phase progress every 1,000 rows for profiles, tweets, likes+bookmarks, and DM messages. `--json` still keeps stdout clean for scripting.
|
|
24
|
+
- Add `birdclaw discuss <query>` and a Discuss web view for live keyword search via `bird`/`xurl`, persisted search-result tweets, and streaming OpenAI summaries with optional private DM context.
|
|
25
|
+
- Add `birdclaw profile-analyze <handle>` plus a Profile Analyse web view that backfills profile timelines and conversation context through `xurl`, caches the fetched context and AI result in SQLite, and exposes Analyse actions on tweet cards.
|
|
26
|
+
- Add canonical `/profiles/:handle` pages with profile headers and cached Profile Analyse output.
|
|
27
|
+
- Add a Rate Limits web view for observed `xurl` profile-analysis calls, 429s, local throttle settings, and documented X API recent-search windows.
|
|
28
|
+
- Add a Network Map web view for current followers/following, with SQLite geocode caching, OpenCage refreshes, Mapbox rendering, and a local fallback map.
|
|
29
|
+
- Render Network Map clusters as avatar stacks with relationship-weighted rings and avatar-rich profile/cluster overlays.
|
|
30
|
+
- Make the Network Map people list follow the current viewport with an in-view search panel.
|
|
31
|
+
- Add a Data Sources web view showing Birdclaw, bird, and xurl health, authenticated accounts, and automatic fallback order.
|
|
32
|
+
- Prefetch cached avatars for Discuss hover citations so source previews avoid fallback initials once profile metadata includes an avatar URL.
|
|
33
|
+
- Refresh Today digests from live `xurl` home timelines, mentions, and mention conversations before AI analysis so reports see more current context and reply parents.
|
|
34
|
+
|
|
35
|
+
### Changed
|
|
36
|
+
|
|
37
|
+
- Let Today and Discuss fetch much deeper live `xurl` data for the selected time window while keeping the AI prompt constrained to a large model-context budget.
|
|
38
|
+
|
|
39
|
+
### Fixed
|
|
40
|
+
|
|
41
|
+
- Implement `birdclaw auth use <auto|bird|xurl>` so the documented command persists the preferred moderation action transport. (#45 - thanks @peetzweg)
|
|
42
|
+
- Keep `birdclaw init` alive when the macOS Downloads scan is blocked, falling back to the other archive discovery paths. (#44 - thanks @peetzweg)
|
|
43
|
+
- Show live Today fetch progress while Birdclaw pulls X home timeline, mentions, and reply context before the first AI tokens arrive.
|
|
44
|
+
- Include live fetch counts and page/thread progress in Today status messages before AI summary streaming begins.
|
|
45
|
+
- Recover live `xurl` sync when the valid OAuth token is stored under a different local xurl username label than the Birdclaw account handle.
|
|
46
|
+
- Keep Profile Analyse citation hover cards linked to real tweet/avatar sources, throttle `xurl` conversation searches, and retry 429s before continuing AI summaries with partial context.
|
|
47
|
+
- Open `/profiles/:handle` analysis streams immediately, use same-origin profile fetches, and let `BIRDCLAW_PROFILE_ANALYSIS_ACCOUNT` select the xurl account used for profile backfills.
|
|
48
|
+
- Keep Profile Analyse headers from slicing through loaded avatars/names and turn unresolved numeric tweet citations into safe X source links without leaking raw IDs.
|
|
49
|
+
- Group adjacent Profile Analyse tweet citations so cached AI reports show numbered source links instead of repeated generic `source` labels.
|
|
50
|
+
- Highlight hydrated Profile Analyse `@handle` mentions with profile previews and link multi-source citations to readable clauses when possible.
|
|
51
|
+
- Hydrate Profile Analyse header bio `@handle` mentions as soon as the profile context loads, so affiliation-style bios show profile hover previews.
|
|
52
|
+
- Flip tweet and profile hover previews above their trigger when there is not enough room below.
|
|
53
|
+
- Show expanded URLs instead of `t.co` shortlinks in tweet citation hover previews whenever tweet URL entities are available.
|
|
54
|
+
- Show expanded URLs instead of `t.co` shortlinks in Profile Analyse account bios when X description URL entities are available.
|
|
55
|
+
- Keep emoji-bearing profile bios and media tweets aligned with X entity ranges, and route `@handle` profile-preview links to internal `/profiles/:handle` analysis pages.
|
|
56
|
+
- Make Discuss search source/mode controls look like dropdowns in one row, raise live tweet search depth to 20,000 results / 200 pages, combine bird plus xurl in auto mode, and include matching local timeline/saved tweets in Live search discussions.
|
|
57
|
+
- Default Discuss live mode to xurl now that OAuth2 search is authorized.
|
|
58
|
+
- Use the default authorized xurl OAuth2 user for Discuss/Profile Analyse recent-search reads instead of the selected Birdclaw account handle.
|
|
59
|
+
- Keep Discuss/Profile Analyse recent-search reads from inheriting `BIRDCLAW_XURL_OAUTH2_*` overrides, so account-scoped xurl settings do not force stale app/user auth into global search calls.
|
|
60
|
+
- Let normal Discuss web searches reuse cached AI discussions while keeping the Refresh button as the explicit forced-refresh path.
|
|
61
|
+
- Keep Discuss Live search scoped to live/search-result tweets instead of sweeping every local timeline bucket before AI streaming starts.
|
|
62
|
+
- Link unresolved model-emitted `tweet_<id>` citations in AI reports to X source URLs instead of showing raw citation tokens.
|
|
63
|
+
- Tighten AI report line height and first-block spacing in Today and Discuss.
|
|
64
|
+
- Keep Network Map profile positions anchored to exact geocoded locations and render dense areas through smarter avatar clusters instead of random scatter.
|
|
65
|
+
- Ignore stale configured OAuth2 xurl account overrides for Profile Analyse user lookup and profile timeline reads.
|
|
66
|
+
|
|
3
67
|
## 0.6.0 - 2026-05-22
|
|
4
68
|
|
|
5
69
|
### Added
|
package/README.md
CHANGED
|
@@ -219,6 +219,8 @@ birdclaw auth status --json
|
|
|
219
219
|
birdclaw db stats --json
|
|
220
220
|
```
|
|
221
221
|
|
|
222
|
+
`auth status` reports Birdclaw's coarse xurl status. Verify xurl with `xurl whoami` and bird with `bird whoami`. For setup and transport selection, see [Sign in](docs/auth.md).
|
|
223
|
+
|
|
222
224
|
Find and import an archive:
|
|
223
225
|
|
|
224
226
|
```bash
|
|
@@ -227,6 +229,8 @@ birdclaw import archive --json
|
|
|
227
229
|
birdclaw import archive ~/Downloads/twitter-archive-2025.zip --json
|
|
228
230
|
```
|
|
229
231
|
|
|
232
|
+
Don't have an archive yet? Request it from <https://x.com/settings/download_your_data>; X emails a download link when it is ready, which may take a few days. A fresh Birdclaw database needs the archive import to establish account identity before live sync. See [Archive Import → Get an archive](docs/archive.md#get-an-archive).
|
|
233
|
+
|
|
230
234
|
Optional profile hydration can improve bios, follower counts, and avatars, but it performs live X profile reads and can spend API credits on large archives:
|
|
231
235
|
|
|
232
236
|
```bash
|
|
@@ -387,19 +391,45 @@ birdclaw research "codex" --limit 20 --thread-depth 10 --json
|
|
|
387
391
|
birdclaw research --account acct_primary --out ~/research/codex.md
|
|
388
392
|
```
|
|
389
393
|
|
|
394
|
+
### Discuss keyword searches
|
|
395
|
+
|
|
396
|
+
`birdclaw discuss` fetches live keyword matches through `bird` or `xurl`, stores them as local `search` tweets, then streams an OpenAI Markdown summary and discussion. DMs are excluded unless explicitly included.
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
birdclaw discuss "local-first" --mode bird
|
|
400
|
+
birdclaw discuss "sync engine" --question "what changed over time?"
|
|
401
|
+
birdclaw discuss "prototype" --include-dms --limit 500 --max-pages 5 --json
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
### Profile analysis
|
|
405
|
+
|
|
406
|
+
`birdclaw profile-analyze` resolves a profile through `xurl`, walks as much of the retrievable timeline as the API allows, backfills high-signal conversations, caches both the fetched context and AI result in SQLite, and writes a Markdown profile brief.
|
|
407
|
+
|
|
408
|
+
Conversation backfill uses X recent search, so Birdclaw paces those calls by default (`BIRDCLAW_PROFILE_ANALYSIS_CONVERSATION_DELAY_MS`, default `3100`) and retries a 429 once after `BIRDCLAW_PROFILE_ANALYSIS_RATE_LIMIT_RETRY_MS` (default `60000`) before continuing with partial context. Set `BIRDCLAW_PROFILE_ANALYSIS_RATE_LIMIT_MAX_RETRIES` or the matching CLI flags when you want different behavior.
|
|
409
|
+
|
|
410
|
+
When `xurl` has multiple OAuth2 labels, set `BIRDCLAW_XURL_OAUTH2_APP` and `BIRDCLAW_XURL_OAUTH2_USERNAME` to force the known-good token. Set `BIRDCLAW_PROFILE_ANALYSIS_ACCOUNT` to an account id or handle when profile backfills should use a non-default Birdclaw account.
|
|
411
|
+
|
|
412
|
+
The web UI uses `/profiles/<handle>` for the canonical profile page, `/profile-analyze` for the analysis/search utility page, and `/rate-limits` for observed `xurl` pressure, recent 429s, and the active Profile Analyse throttle settings.
|
|
413
|
+
|
|
414
|
+
```bash
|
|
415
|
+
birdclaw profile-analyze steipete
|
|
416
|
+
birdclaw profile-analyse openai --max-pages 20 --max-conversations 40 --conversation-delay-ms 3100 --rate-limit-retries 2 --json
|
|
417
|
+
```
|
|
418
|
+
|
|
390
419
|
### What happened today
|
|
391
420
|
|
|
392
|
-
`birdclaw today` streams a local "what happened" digest from the SQLite store. It uses the OpenAI Responses API with `gpt-5.5`, medium reasoning, and priority service tier by default. Set `OPENAI_API_KEY`; override with `BIRDCLAW_AI_MODEL`, `BIRDCLAW_OPENAI_REASONING_EFFORT`, or `BIRDCLAW_OPENAI_SERVICE_TIER` when needed.
|
|
421
|
+
`birdclaw today` streams a local "what happened" digest from the SQLite store. It uses the OpenAI Responses API with `gpt-5.5`, medium reasoning, and priority service tier by default. Set `OPENAI_API_KEY`; override with `BIRDCLAW_AI_MODEL`, `BIRDCLAW_OPENAI_REASONING_EFFORT`, or `BIRDCLAW_OPENAI_SERVICE_TIER` when needed. Use `--language <locale-id>` or `BIRDCLAW_DIGEST_LANGUAGE` for localized reports.
|
|
393
422
|
|
|
394
423
|
```bash
|
|
395
424
|
birdclaw today
|
|
425
|
+
birdclaw today --language zh-CN
|
|
396
426
|
birdclaw digest 24h --refresh
|
|
397
427
|
birdclaw digest week --json
|
|
398
428
|
birdclaw digest --since 2026-05-16T00:00:00Z --until 2026-05-17T00:00:00Z
|
|
399
429
|
birdclaw digest today --include-dms
|
|
400
430
|
```
|
|
401
431
|
|
|
402
|
-
The web UI exposes the same stream under `What happened`. DMs are excluded unless explicitly enabled. Final structured results are cached by the exact local context hash, model, reasoning effort, and
|
|
432
|
+
The web UI exposes the same stream under `What happened`. DMs are excluded unless explicitly enabled. Final structured results are cached by the exact local context hash, model, reasoning effort, service tier, and report language.
|
|
403
433
|
|
|
404
434
|
### Search and triage DMs
|
|
405
435
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "birdclaw",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Local Twitter memory in SQLite for archives, DMs, likes, bookmarks, and moderation",
|
|
5
5
|
"homepage": "https://github.com/steipete/birdclaw#readme",
|
|
6
6
|
"license": "MIT",
|
|
@@ -56,48 +56,47 @@
|
|
|
56
56
|
"@tailwindcss/vite": "^4.3.0",
|
|
57
57
|
"@tanstack/devtools-vite": "0.7.0",
|
|
58
58
|
"@tanstack/react-devtools": "0.10.5",
|
|
59
|
-
"@tanstack/react-router": "1.
|
|
60
|
-
"@tanstack/react-router-devtools": "1.
|
|
61
|
-
"@tanstack/react-router-ssr-query": "1.
|
|
62
|
-
"@tanstack/react-start": "1.
|
|
63
|
-
"@tanstack/router-plugin": "^1.
|
|
64
|
-
"@vitejs/plugin-react": "^6.0.
|
|
65
|
-
"commander": "^
|
|
66
|
-
"effect": "^3.21.
|
|
67
|
-
"kysely": "^0.29.
|
|
68
|
-
"lucide-react": "^1.
|
|
69
|
-
"
|
|
70
|
-
"react
|
|
59
|
+
"@tanstack/react-router": "1.170.15",
|
|
60
|
+
"@tanstack/react-router-devtools": "1.167.0",
|
|
61
|
+
"@tanstack/react-router-ssr-query": "1.167.1",
|
|
62
|
+
"@tanstack/react-start": "1.168.25",
|
|
63
|
+
"@tanstack/router-plugin": "^1.168.18",
|
|
64
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
65
|
+
"commander": "^15.0.0",
|
|
66
|
+
"effect": "^3.21.3",
|
|
67
|
+
"kysely": "^0.29.2",
|
|
68
|
+
"lucide-react": "^1.17.0",
|
|
69
|
+
"mapbox-gl": "^3.24.0",
|
|
70
|
+
"react": "^19.2.7",
|
|
71
|
+
"react-dom": "^19.2.7",
|
|
72
|
+
"react-map-gl": "^8.1.1",
|
|
73
|
+
"supercluster": "^8.0.1",
|
|
71
74
|
"tailwindcss": "^4.3.0",
|
|
72
|
-
"tsx": "^4.22.
|
|
73
|
-
"vite": "^8.0.
|
|
75
|
+
"tsx": "^4.22.4",
|
|
76
|
+
"vite": "^8.0.16",
|
|
74
77
|
"zod": "^4.4.3"
|
|
75
78
|
},
|
|
76
79
|
"devDependencies": {
|
|
77
80
|
"@playwright/test": "^1.60.0",
|
|
78
|
-
"@tailwindcss/typography": "^0.5.
|
|
81
|
+
"@tailwindcss/typography": "^0.5.20",
|
|
79
82
|
"@testing-library/dom": "^10.4.1",
|
|
80
83
|
"@testing-library/jest-dom": "^6.9.1",
|
|
81
84
|
"@testing-library/react": "^16.3.2",
|
|
82
|
-
"@types/
|
|
83
|
-
"@types/
|
|
85
|
+
"@types/geojson": "^7946.0.16",
|
|
86
|
+
"@types/node": "^25.9.2",
|
|
87
|
+
"@types/react": "^19.2.17",
|
|
84
88
|
"@types/react-dom": "^19.2.3",
|
|
85
|
-
"@
|
|
86
|
-
"@
|
|
89
|
+
"@types/supercluster": "^7.1.3",
|
|
90
|
+
"@typescript/native-preview": "7.0.0-dev.20260609.1",
|
|
91
|
+
"@vitest/coverage-v8": "^4.1.8",
|
|
87
92
|
"jsdom": "^29.1.1",
|
|
88
|
-
"oxfmt": "^0.
|
|
89
|
-
"oxlint": "^1.
|
|
93
|
+
"oxfmt": "^0.54.0",
|
|
94
|
+
"oxlint": "^1.69.0",
|
|
90
95
|
"typescript": "^6.0.3",
|
|
91
|
-
"vitest": "^4.1.
|
|
96
|
+
"vitest": "^4.1.8"
|
|
92
97
|
},
|
|
93
98
|
"engines": {
|
|
94
99
|
"node": ">=25.8.1 <27"
|
|
95
100
|
},
|
|
96
|
-
"packageManager": "pnpm@10.31.0"
|
|
97
|
-
"pnpm": {
|
|
98
|
-
"onlyBuiltDependencies": [
|
|
99
|
-
"esbuild",
|
|
100
|
-
"lightningcss"
|
|
101
|
-
]
|
|
102
|
-
}
|
|
101
|
+
"packageManager": "pnpm@10.31.0"
|
|
103
102
|
}
|
|
@@ -24,7 +24,10 @@ const productDescription =
|
|
|
24
24
|
const brewInstall = "brew install steipete/tap/birdclaw";
|
|
25
25
|
|
|
26
26
|
const sections = [
|
|
27
|
-
[
|
|
27
|
+
[
|
|
28
|
+
"Start",
|
|
29
|
+
["index.md", "install.md", "auth.md", "quickstart.md", "configuration.md"],
|
|
30
|
+
],
|
|
28
31
|
[
|
|
29
32
|
"Archive & Sync",
|
|
30
33
|
["archive.md", "sync.md", "media.md", "backup.md", "jobs.md"],
|
|
@@ -449,19 +452,41 @@ function inline(text, currentRel) {
|
|
|
449
452
|
stash.push(`<code>${escapeHtml(code)}</code>`);
|
|
450
453
|
return `\u0000${stash.length - 1}\u0000`;
|
|
451
454
|
});
|
|
452
|
-
|
|
453
|
-
.
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
.
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
455
|
+
const stashLink = (label, href) => {
|
|
456
|
+
stash.push(
|
|
457
|
+
`<a href="${escapeAttr(rewriteHref(href, currentRel))}">${renderInlineText(label, stash)}</a>`,
|
|
458
|
+
);
|
|
459
|
+
return `\u0000${stash.length - 1}\u0000`;
|
|
460
|
+
};
|
|
461
|
+
out = out.replace(/\[([^\]]+)\]\(<([^<>]+)>\)/g, (_, label, href) =>
|
|
462
|
+
stashLink(label, href),
|
|
463
|
+
);
|
|
464
|
+
out = out.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (_, label, href) =>
|
|
465
|
+
stashLink(label, href),
|
|
466
|
+
);
|
|
467
|
+
out = out.replace(/<(https?:\/\/[^\s<>]+)>/g, (_, url) => {
|
|
468
|
+
stash.push(`<a href="${escapeAttr(url)}">${escapeHtml(url)}</a>`);
|
|
469
|
+
return `\u0000${stash.length - 1}\u0000`;
|
|
470
|
+
});
|
|
471
|
+
return renderInlineText(out, stash);
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
function renderInlineText(text, stash) {
|
|
475
|
+
let out = formatInlineText(text);
|
|
462
476
|
out = out.replace(/\\\|/g, "|");
|
|
463
477
|
out = out.replace(/<br>/g, "<br>");
|
|
464
|
-
return out
|
|
478
|
+
return restoreInlineStash(out, stash);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
function formatInlineText(text) {
|
|
482
|
+
return escapeHtml(text)
|
|
483
|
+
.replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
|
|
484
|
+
.replace(/(^|[^*])\*([^*\s][^*]*?)\*(?!\*)/g, "$1<em>$2</em>")
|
|
485
|
+
.replace(/(^|[^_])_([^_\s][^_]*?)_(?!_)/g, "$1<em>$2</em>");
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function restoreInlineStash(text, stash) {
|
|
489
|
+
return text.replace(/\u0000(\d+)\u0000/g, (_, i) => stash[Number(i)]);
|
|
465
490
|
}
|
|
466
491
|
|
|
467
492
|
function rewriteHref(href, currentRel) {
|
|
@@ -886,6 +911,8 @@ function highlightYamlValue(rest) {
|
|
|
886
911
|
return escapeHtml(rest);
|
|
887
912
|
}
|
|
888
913
|
|
|
914
|
+
export const __test__ = { inline };
|
|
915
|
+
|
|
889
916
|
function validateLinks(outputDir) {
|
|
890
917
|
const failures = [];
|
|
891
918
|
const placeholderHrefs =
|