콘텐츠로 이동

네이티브 미디어 + 시스템 유틸리티

이 문서는 docs/natives-architecture.md에 설명된 system/media/conversion primitives 계층에 대한 서브시스템 심층 분석입니다: image, html, clipboard, 그리고 work 프로파일링을 다룹니다.

  • crates/pi-natives/src/image.rs
  • crates/pi-natives/src/html.rs
  • crates/pi-natives/src/clipboard.rs
  • crates/pi-natives/src/prof.rs
  • crates/pi-natives/src/task.rs
  • packages/natives/src/image/index.ts
  • packages/natives/src/image/types.ts
  • packages/natives/src/html/index.ts
  • packages/natives/src/html/types.ts
  • packages/natives/src/clipboard/index.ts
  • packages/natives/src/clipboard/types.ts
  • packages/natives/src/work/index.ts
  • packages/natives/src/work/types.ts

참고: crates/pi-natives/src/work.rs는 존재하지 않습니다. 작업 프로파일링은 prof.rs에서 구현되며 task.rs의 계측에 의해 데이터가 공급됩니다.

TS export (packages/natives)Rust N-API exportRust 모듈
PhotonImage.parse(bytes)PhotonImage::parseimage.rs
PhotonImage#resize(width, height, filter)PhotonImage::resizeimage.rs
PhotonImage#encode(format, quality)PhotonImage::encodeimage.rs
htmlToMarkdown(html, options)html_to_markdownhtml.rs
copyToClipboard(text)copy_to_clipboard + TS 폴백 로직clipboard.rs + clipboard/index.ts
readImageFromClipboard()read_image_from_clipboardclipboard.rs
getWorkProfile(lastSeconds)get_work_profileprof.rs
  • JS 입력 경계: Uint8Array 인코딩된 이미지 바이트.
  • Rust 디코드 경계: 바이트가 Vec<u8>로 복사되고, ImageReader::with_guessed_format()으로 형식이 추측된 후 DynamicImage로 디코딩됩니다.
  • 메모리 내 상태: PhotonImageArc<DynamicImage>를 저장합니다.
  • 출력 경계: encode(format, quality)Promise<Uint8Array> (Rust Vec<u8>)를 반환합니다.

형식 ID는 숫자입니다:

  • 0: PNG
  • 1: JPEG
  • 2: WebP (무손실 인코더)
  • 3: GIF

제약 조건:

  • quality는 JPEG에서만 사용됩니다.
  • PNG/WebP/GIF는 quality를 무시합니다.
  • 지원되지 않는 형식 ID는 실패합니다 (Invalid image format: <id>).
  • JS 입력 경계: HTML string + 선택적 객체 { cleanContent?: boolean; skipImages?: boolean }.
  • Rust 변환 경계: String 입력이 html_to_markdown_rs::convert에 의해 변환됩니다.
  • 출력 경계: Markdown string.

변환 동작:

  • cleanContent의 기본값은 false입니다.
  • cleanContent=true일 때, PreprocessingPreset::Aggressive와 네비게이션/폼에 대한 하드 제거 플래그가 포함된 전처리가 활성화됩니다.
  • skipImages의 기본값은 false입니다.
  • 텍스트 경로:
    • TS는 stdout이 TTY일 때 먼저 OSC 52 (\x1b]52;c;<base64>\x07)를 출력합니다.
    • 동일한 텍스트가 최선 노력 방식으로 네이티브 클립보드 API (native.copyToClipboard)를 통해 시도됩니다.
    • Termux에서는 TS가 먼저 termux-clipboard-set을 시도합니다.
  • 이미지 읽기 경로:
    • Rust가 arboard에서 원시 이미지를 읽습니다.
    • Rust가 이를 PNG 바이트로 재인코딩하고 (image 크레이트), { data: Uint8Array, mimeType: "image/png" }를 반환합니다.
    • TS는 Termux 또는 디스플레이 서버가 없는 Linux 세션(DISPLAY/WAYLAND_DISPLAY 누락)에서 null을 조기 반환합니다.
  • 수집 경계: 프로파일링 샘플은 task::blockingtask::futureprofile_region(tag) 가드에 의해 생성됩니다.
  • 저장 형식: 스택 경로 + 지속 시간 (μs) + 타임스탬프 (프로세스 시작 이후 μs)를 저장하는 고정 크기 순환 버퍼 (MAX_SAMPLES = 10_000).
  • 출력 경계: getWorkProfile(lastSeconds)는 다음 객체를 반환합니다:
    • folded: 접힌 스택 텍스트 (플레임그래프 입력)
    • summary: 마크다운 테이블 요약
    • svg: 선택적 플레임그래프 SVG
    • totalMs, sampleCount
  1. PhotonImage.parse(bytes)가 블로킹 디코드 작업을 스케줄링합니다 (image.decode).
  2. 성공 시, 네이티브 PhotonImage 핸들이 JS에 존재합니다.
  3. resize(...)는 새로운 네이티브 핸들을 생성하며 (image.resize), 이전 핸들과 새 핸들이 공존할 수 있습니다.
  4. encode(...)는 이미지 크기를 변경하지 않고 바이트를 구체화합니다 (image.encode).

실패 전이:

  • 형식 감지/디코드 실패는 parse 프로미스를 거부합니다.
  • 인코드 실패는 encode 프로미스를 거부합니다.
  • 유효하지 않은 형식 ID는 encode 프로미스를 거부합니다.
  1. htmlToMarkdown(html, options)가 블로킹 변환 작업을 스케줄링합니다.
  2. 변환은 지정되지 않은 경우 기본 옵션 (cleanContent=false, skipImages=false)으로 실행됩니다.
  3. 마크다운 문자열을 반환하거나 거부합니다.

실패 전이:

  • 변환기 실패는 거부된 프로미스를 반환합니다 (Conversion error: ...).

copyToClipboard(text)는 의도적으로 최선 노력 방식이며 다중 경로입니다:

  1. TTY인 경우: OSC 52 쓰기를 시도합니다 (base64 페이로드).
  2. TERMUX_VERSION이 설정된 경우 Termux 명령을 시도합니다.
  3. 네이티브 arboard 텍스트 복사를 시도합니다.
  4. TS 계층에서 오류를 무시합니다.

readImageFromClipboard()의 엄격성은 단계별로 다릅니다:

  1. TS가 지원되지 않는 런타임 컨텍스트(Termux/헤드리스 Linux)를 null로 하드 게이트합니다.
  2. Rust arboard 읽기는 TS가 허용한 경우에만 실행됩니다.
  3. ContentNotAvailablenull로 매핑됩니다.
  4. 기타 Rust 오류는 거부합니다.
  1. 명시적 시작이 없습니다: 작업 헬퍼가 실행될 때 프로파일링이 항상 켜져 있습니다.
  2. 모든 계측된 작업 스코프는 가드 드롭 시 하나의 샘플을 기록합니다.
  3. 버퍼 용량에 도달하면 샘플이 가장 오래된 항목을 덮어씁니다.
  4. getWorkProfile(lastSeconds)가 시간 윈도우를 읽고 접힌 스택/요약/SVG 아티팩트를 도출합니다.

실패 전이:

  • SVG 생성 실패는 소프트 실패입니다 (svg: null), 접힌 스택과 요약은 여전히 반환됩니다.
  • 빈 샘플 윈도우는 빈 접힌 데이터와 svg: null을 반환하며, 오류가 아닙니다.

지원되지 않는 작업 및 오류 전파

섹션 제목: “지원되지 않는 작업 및 오류 전파”
  • 지원되지 않는 디코드 입력 또는 손상된 바이트: 엄격한 실패 (프로미스 거부).
  • 지원되지 않는 인코드 형식 ID: 엄격한 실패.
  • TS 래퍼에 최선 노력 폴백 경로가 없습니다.
  • 변환 오류는 엄격한 실패입니다 (거부).
  • 옵션 생략은 최선 노력 기본값 적용이며, 실패가 아닙니다.
  • 텍스트 복사는 TS 계층에서 최선 노력 방식입니다: 작동 실패가 억제됩니다.
  • 이미지 읽기는 “이미지 없음” (null)과 작동 실패 (거부)를 구분합니다.
  • Termux/헤드리스 Linux는 이미지 읽기에 대해 지원되지 않는 컨텍스트로 처리됩니다 (null).
  • 함수 호출 자체에 대한 검색은 엄격하지만, 아티팩트 생성은 부분적으로 최선 노력 방식입니다 (svg는 null 가능).
  • 버퍼 잘림은 예상되는 동작(링 버퍼)이며, 데이터 손실 버그가 아닙니다.
  • 클립보드 텍스트: OSC 52는 터미널 지원에 의존하며, 네이티브 클립보드 접근은 데스크톱 환경/세션에 의존합니다.
  • 클립보드 이미지 읽기: Termux 및 디스플레이 서버가 없는 Linux에서는 TS에서 차단됩니다.