6bf09bedb0
- Migrated language classifiers from imperative methods to declarative semantic rule tables with ClassifierTables and StructuralOverrides. - Extracted node shape analysis into dedicated shape module with field priority constants and helper functions for AST traversal. - Added schema module with language-aware node metadata and thread-local language context management. - Centralized environment variable parsing across codebase using $flag(), $envpos(), and isBunTestRuntime() utilities. - Added PI_CHUNK_AUTOINDENT configuration to control indentation normalization in chunk read/edit operations. - Enhanced system prompt with instruction priority, output contract, tool persistence, and completeness guidelines.
70 lines
2.3 KiB
TypeScript
70 lines
2.3 KiB
TypeScript
import { $env, $flag } from "@oh-my-pi/pi-utils";
|
|
|
|
const SIXEL_START_REGEX = /\x1bP(?:[0-9;]*)q/u;
|
|
const SIXEL_END_SEQUENCE = "\x1b\\";
|
|
const SIXEL_END_BELL = "\x07";
|
|
const SIXEL_SEQUENCE_REGEX = /\x1bP(?:[0-9;]*)q[\s\S]*?(?:\x1b\\|\x07)/gu;
|
|
const SIXEL_PLACEHOLDER_PREFIX = "__OMP_SIXEL_SEQUENCE_";
|
|
|
|
/**
|
|
* Returns whether SIXEL passthrough is explicitly enabled.
|
|
*
|
|
* Both gates must be enabled to preserve SIXEL control sequences:
|
|
* - PI_FORCE_IMAGE_PROTOCOL=sixel
|
|
* - PI_ALLOW_SIXEL_PASSTHROUGH=1
|
|
*/
|
|
export function isSixelPassthroughEnabled(): boolean {
|
|
const forcedProtocol = $env.PI_FORCE_IMAGE_PROTOCOL?.trim().toLowerCase();
|
|
return forcedProtocol === "sixel" && $flag("PI_ALLOW_SIXEL_PASSTHROUGH");
|
|
}
|
|
/** Returns true when the text contains a SIXEL start sequence. */
|
|
export function containsSixelSequence(text: string): boolean {
|
|
return SIXEL_START_REGEX.test(text);
|
|
}
|
|
|
|
/**
|
|
* Returns a boolean mask indicating which lines belong to a SIXEL sequence block.
|
|
* Supports multi-line SIXEL payloads generated by libsixel.
|
|
*/
|
|
export function getSixelLineMask(lines: string[]): boolean[] {
|
|
let inSequence = false;
|
|
return lines.map(line => {
|
|
const hasStart = containsSixelSequence(line);
|
|
if (hasStart) {
|
|
inSequence = true;
|
|
}
|
|
const isSixelLine = inSequence;
|
|
if (inSequence && (line.includes(SIXEL_END_SEQUENCE) || line.includes(SIXEL_END_BELL))) {
|
|
inSequence = false;
|
|
}
|
|
return isSixelLine;
|
|
});
|
|
}
|
|
|
|
/** Returns true when the line contains a SIXEL start sequence. */
|
|
export function isSixelLine(line: string): boolean {
|
|
return containsSixelSequence(line);
|
|
}
|
|
|
|
/**
|
|
* Sanitizes text while preserving embedded SIXEL sequences when passthrough is enabled.
|
|
*/
|
|
export function sanitizeWithOptionalSixelPassthrough(text: string, sanitize: (text: string) => string): string {
|
|
if (!isSixelPassthroughEnabled() || !containsSixelSequence(text)) {
|
|
return sanitize(text);
|
|
}
|
|
|
|
const preservedSequences: string[] = [];
|
|
const tokenized = text.replace(SIXEL_SEQUENCE_REGEX, match => {
|
|
const token = `${SIXEL_PLACEHOLDER_PREFIX}${preservedSequences.length}__`;
|
|
preservedSequences.push(match);
|
|
return token;
|
|
});
|
|
|
|
const sanitized = sanitize(tokenized);
|
|
return sanitized.replace(/__OMP_SIXEL_SEQUENCE_(\d+)__/gu, (_, indexText: string) => {
|
|
const index = Number.parseInt(indexText, 10);
|
|
return preservedSequences[index] ?? "";
|
|
});
|
|
}
|