fix: handle text selection edge cases using new delimeter
This commit is contained in:
+19
-11
@@ -9,39 +9,47 @@
|
|||||||
/**
|
/**
|
||||||
* Encode a text selection into a compact URL-safe fragment.
|
* Encode a text selection into a compact URL-safe fragment.
|
||||||
*
|
*
|
||||||
* Format: `?hl=<prefix>,<suffix>,<length>`
|
* Format: `?hl=<prefix>§<suffix>§<length>`
|
||||||
* - prefix: first 4 words of the selection
|
* - prefix: first 4 words of the selection
|
||||||
* - suffix: last 4 words of the selection (omitted if same as prefix)
|
* - suffix: last 4 words of the selection (omitted if same as prefix)
|
||||||
* - length: character count of the full selection (for disambiguation)
|
* - length: character count of the full selection (for disambiguation)
|
||||||
*
|
*
|
||||||
* This keeps URLs short while being specific enough to locate the text.
|
* Uses `§` (section sign) as delimiter — it never appears in Sanskrit,
|
||||||
|
* Devanagari, English, or any Vedic text, unlike `,` (commas in prose)
|
||||||
|
* or `|` (pūrṇa virāma in Sanskrit verses).
|
||||||
*/
|
*/
|
||||||
export function encodeTextFragment(text: string): string {
|
export function encodeTextFragment(text: string): string {
|
||||||
const words = text.split(/\s+/).filter(Boolean);
|
const words = text.split(/\s+/).filter(Boolean);
|
||||||
if (words.length === 0) return "";
|
if (words.length === 0) return "";
|
||||||
|
|
||||||
const prefixWords = words.slice(0, 4).join(" ");
|
|
||||||
const suffixWords = words.length > 6 ? words.slice(-4).join(" ") : "";
|
|
||||||
const len = text.length;
|
const len = text.length;
|
||||||
|
|
||||||
const parts = [prefixWords];
|
// Short selections (≤8 words): use the full text as prefix, no suffix needed.
|
||||||
if (suffixWords) parts.push(suffixWords);
|
// The entire selection is specific enough to locate uniquely.
|
||||||
parts.push(String(len));
|
if (words.length <= 8) {
|
||||||
|
return `${text.trim()}§${len}`;
|
||||||
|
}
|
||||||
|
|
||||||
return parts.join(",");
|
// Longer selections: first 4 words + last 4 words + length
|
||||||
|
const prefixWords = words.slice(0, 4).join(" ");
|
||||||
|
const suffixWords = words.slice(-4).join(" ");
|
||||||
|
|
||||||
|
return `${prefixWords}§${suffixWords}§${len}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a text fragment from URL params.
|
* Decode a text fragment from URL params.
|
||||||
|
* Supports `§` (current), `|` (v2 legacy), and `,` (v1 legacy) delimiters.
|
||||||
* Returns { prefix, suffix, length } or null if invalid.
|
* Returns { prefix, suffix, length } or null if invalid.
|
||||||
*/
|
*/
|
||||||
export function decodeTextFragment(
|
export function decodeTextFragment(
|
||||||
hl: string,
|
hl: string,
|
||||||
): { prefix: string; suffix: string; length: number } | null {
|
): { prefix: string; suffix: string; length: number } | null {
|
||||||
const parts = hl.split(",");
|
const delimiter = "§";
|
||||||
|
const parts = hl.split(delimiter);
|
||||||
if (parts.length < 2) return null;
|
if (parts.length < 2) return null;
|
||||||
|
|
||||||
const lengthStr = parts[parts.length - 1];
|
const lengthStr = parts[parts.length - 1].trim();
|
||||||
const length = parseInt(lengthStr, 10);
|
const length = parseInt(lengthStr, 10);
|
||||||
if (isNaN(length)) return null;
|
if (isNaN(length)) return null;
|
||||||
|
|
||||||
@@ -51,7 +59,7 @@ export function decodeTextFragment(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// prefix + suffix + length
|
// prefix + suffix + length
|
||||||
const prefix = parts.slice(0, -2).join(","); // in case prefix itself had commas
|
const prefix = parts.slice(0, -2).join(delimiter);
|
||||||
const suffix = parts[parts.length - 2];
|
const suffix = parts[parts.length - 2];
|
||||||
return { prefix, suffix, length };
|
return { prefix, suffix, length };
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user