Buckets:
| import { type SelectedLineRange } from "@pierre/diffs" | |
| type SelectionKey = "ui.sessionReview.selection.line" | "ui.sessionReview.selection.lines" | |
| type SelectionVars = Record<string, string | number> | |
| type PointerMode = "none" | "text" | "numbers" | |
| type Side = SelectedLineRange["side"] | |
| type LineSpan = Pick<SelectedLineRange, "start" | "end"> | |
| export function formatSelectedLineLabel(range: LineSpan, t: (key: SelectionKey, params: SelectionVars) => string) { | |
| const start = Math.min(range.start, range.end) | |
| const end = Math.max(range.start, range.end) | |
| if (start === end) return t("ui.sessionReview.selection.line", { line: start }) | |
| return t("ui.sessionReview.selection.lines", { start, end }) | |
| } | |
| export function previewSelectedLines(source: string, range: LineSpan) { | |
| const start = Math.max(1, Math.min(range.start, range.end)) | |
| const end = Math.max(range.start, range.end) | |
| const lines = source.split("\n").slice(start - 1, end) | |
| if (lines.length === 0) return | |
| return lines.slice(0, 2).join("\n") | |
| } | |
| export function cloneSelectedLineRange(range: SelectedLineRange): SelectedLineRange { | |
| const next: SelectedLineRange = { | |
| start: range.start, | |
| end: range.end, | |
| } | |
| if (range.side) next.side = range.side | |
| if (range.endSide) next.endSide = range.endSide | |
| return next | |
| } | |
| export function lineInSelectedRange(range: SelectedLineRange | null | undefined, line: number, side?: Side) { | |
| if (!range) return false | |
| const start = Math.min(range.start, range.end) | |
| const end = Math.max(range.start, range.end) | |
| if (line < start || line > end) return false | |
| if (!side) return true | |
| const first = range.side | |
| const last = range.endSide ?? first | |
| if (!first && !last) return true | |
| if (!first || !last) return (first ?? last) === side | |
| if (first === last) return first === side | |
| if (line === start) return first === side | |
| if (line === end) return last === side | |
| return true | |
| } | |
| export function isSingleLineSelection(range: SelectedLineRange | null) { | |
| if (!range) return false | |
| return range.start === range.end && (range.endSide == null || range.endSide === range.side) | |
| } | |
| export function toRange(source: Range | StaticRange): Range { | |
| if (source instanceof Range) return source | |
| const range = new Range() | |
| range.setStart(source.startContainer, source.startOffset) | |
| range.setEnd(source.endContainer, source.endOffset) | |
| return range | |
| } | |
| export function restoreShadowTextSelection(root: ShadowRoot | undefined, range: Range | undefined) { | |
| if (!root || !range) return | |
| requestAnimationFrame(() => { | |
| const selection = | |
| (root as unknown as { getSelection?: () => Selection | null }).getSelection?.() ?? window.getSelection() | |
| if (!selection) return | |
| try { | |
| selection.removeAllRanges() | |
| selection.addRange(range) | |
| } catch {} | |
| }) | |
| } | |
| export function createLineNumberSelectionBridge() { | |
| let mode: PointerMode = "none" | |
| let line: number | undefined | |
| let moved = false | |
| let pending = false | |
| const clear = () => { | |
| mode = "none" | |
| line = undefined | |
| moved = false | |
| } | |
| return { | |
| begin(numberColumn: boolean, next: number | undefined) { | |
| if (!numberColumn) { | |
| mode = "text" | |
| return | |
| } | |
| mode = "numbers" | |
| line = next | |
| moved = false | |
| }, | |
| track(buttons: number, next: number | undefined) { | |
| if (mode !== "numbers") return false | |
| if ((buttons & 1) === 0) { | |
| clear() | |
| return true | |
| } | |
| if (next !== undefined && line !== undefined && next !== line) moved = true | |
| return true | |
| }, | |
| finish() { | |
| const current = mode | |
| pending = current === "numbers" && moved | |
| clear() | |
| return current | |
| }, | |
| consume(range: SelectedLineRange | null) { | |
| const result = pending && !isSingleLineSelection(range) | |
| pending = false | |
| return result | |
| }, | |
| reset() { | |
| pending = false | |
| clear() | |
| }, | |
| } | |
| } | |
Xet Storage Details
- Size:
- 3.89 kB
- Xet hash:
- ea3821f8d78cf7a8b558aca4624cf6d069966cdc00ab09c7f4675b88a87a64bd
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.