mirror of
https://github.com/stablyai/orca.git
synced 2026-09-29 16:02:50 +00:00
* fix(mobile): make native-chat file links and path citations tappable (STA-3331)
- Linkify POSIX absolute paths in chat prose (leading-/ regex alternative;
URL guard now keys off the char before the matched slash)
- Parse agent-style path:line(:col) citations in prose, code spans, and the
open flow; line/column ride into the mobile file preview route
- Route non-web markdown hrefs (file: URIs, relative/absolute paths) to the
file opener instead of silently dropping them; unknown schemes stay dead
- Resolve chat paths against the worktree root, not the terminal's live cwd
- Reuse the terminal tap-to-open flow for chat taps (haptic, preview route,
tab activation with retries) via a shared identity-stable hook, and toast
on misses instead of silent no-ops
- Keep snake_case paths whole (intraword underscores are literal text),
scan bold/italic/strike spans for paths, split trailing punctuation off
autolinks, and let taps land while the composer keyboard is up
* fix(mobile): harden chat file tap handling
* refactor(chat): share native chat href routing
* fix(mobile): detect files directly under path roots
* fix(mobile): keep inline tokens and dunder paths intact around emphasis
Review follow-ups on the chat file-link work:
- A rejected intraword `_` token left the scan index past its closing
underscore, so every inline token between two snake_case words was
swallowed and rendered as literal source — including markdown links,
which became untappable. Rescan from just past the opening delimiter.
- Treat a path separator as an intraword flank so `src/__init__.py` and
`a/__tests__/x.ts` stay whole; previously they rendered as bold plus a
remnant that the new absolute-root pattern turned into a tap on `/x.ts`.
- Bound the `:line(:col)` tail so `src/app.ts:1e3` and `:80%` no longer
parse a line number, while a cited range still opens its first line.
- Route chat tap failures through the composer banner (toast fallback):
chat taps happen with the keyboard up, which covers the toast.
- Drop the tap-handler mirror's dep list; the call site rebuilds its
accessors every render, so it could never skip on a route that
rerenders per keystroke.
* Revert "fix(mobile): keep inline tokens and dunder paths intact around emphasis"
This reverts commit 308bfaf22b.
* fix(mobile): preserve chat file-link parsing and feedback
56 lines
1.7 KiB
TypeScript
56 lines
1.7 KiB
TypeScript
// Post-checks for inline markdown tokens that a single-pass tokenizer regex
|
|
// cannot express on its own.
|
|
|
|
const INTRAWORD_FLANK_PATTERN = /[\w\\/]/
|
|
|
|
/**
|
|
* True when a `_…_` / `__…__` token sits inside a word (snake_case, dunder
|
|
* tails). CommonMark treats intraword underscores as literal text; path
|
|
* separators count as flanks so dunder path segments also stay whole.
|
|
*/
|
|
export function isIntrawordUnderscoreToken(text: string, index: number, token: string): boolean {
|
|
if (!token.startsWith('_')) {
|
|
return false
|
|
}
|
|
const prev = index > 0 ? text[index - 1]! : ''
|
|
const next = text[index + token.length] ?? ''
|
|
return INTRAWORD_FLANK_PATTERN.test(prev) || INTRAWORD_FLANK_PATTERN.test(next)
|
|
}
|
|
|
|
/**
|
|
* Split sentence punctuation off an autolinked URL tail ("see https://x.com/a."),
|
|
* keeping a trailing ')' only when the URL itself opened a paren.
|
|
*/
|
|
export function trimAutolinkTrailingPunctuation(url: string): { url: string; trailing: string } {
|
|
let end = url.length
|
|
let parenthesisCountsReady = false
|
|
let openParentheses = 0
|
|
let closeParentheses = 0
|
|
while (end > 0) {
|
|
const char = url[end - 1]!
|
|
if ('.,;:!?'.includes(char)) {
|
|
end--
|
|
continue
|
|
}
|
|
if (char === ')') {
|
|
if (!parenthesisCountsReady) {
|
|
for (let index = 0; index < end; index++) {
|
|
if (url[index] === '(') {
|
|
openParentheses++
|
|
} else if (url[index] === ')') {
|
|
closeParentheses++
|
|
}
|
|
}
|
|
parenthesisCountsReady = true
|
|
}
|
|
if (closeParentheses > openParentheses) {
|
|
end--
|
|
closeParentheses--
|
|
continue
|
|
}
|
|
}
|
|
break
|
|
}
|
|
return { url: url.slice(0, end), trailing: url.slice(end) }
|
|
}
|