Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions .github/workflows/publish-self-hosted.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Publish package (self-hosted fallback)

on:
workflow_dispatch:
inputs:
release_tag:
description: Existing v-prefixed tag matching package.json
required: true
type: string
source_sha:
description: Exact 40-character source commit SHA to publish
required: true
type: string

permissions:
contents: read
packages: write

jobs:
npm:
runs-on: [self-hosted, macOS, ARM64, velaros-package-publish]
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
ref: ${{ inputs.source_sha }}
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
registry-url: https://npm.pkg.github.com
scope: '@velaros-ai'
- name: Verify immutable release source
env:
RELEASE_TAG: ${{ inputs.release_tag }}
SOURCE_SHA: ${{ inputs.source_sha }}
run: |
test "$(git rev-parse HEAD)" = "$SOURCE_SHA"
test "$(git rev-parse "${RELEASE_TAG}^{commit}")" = "$SOURCE_SHA"
- run: npm ci
- run: npm run check
- id: release
name: Build the immutable release bundle
env:
RELEASE_TAG: ${{ inputs.release_tag }}
SOURCE_SHA: ${{ inputs.source_sha }}
run: |
node scripts/prepare-release.mjs \
--tag "$RELEASE_TAG" \
--source-sha "$SOURCE_SHA" \
--out-dir release \
--require-tag
echo "tarball=$(node -p 'require("./release/release-manifest.json").tarball.fileName')" >> "$GITHUB_OUTPUT"
- uses: actions/upload-artifact@v4
with:
name: html-artifacts-${{ inputs.release_tag }}-${{ inputs.source_sha }}
path: release/
if-no-files-found: error
retention-days: 90
- run: npm publish "./release/${{ steps.release.outputs.tarball }}" --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,6 @@ jobs:
path: release/
if-no-files-found: error
retention-days: 90
- run: npm publish "./release/${{ steps.release.outputs.tarball }}" --access restricted
- run: npm publish "./release/${{ steps.release.outputs.tarball }}" --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
10 changes: 6 additions & 4 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ VelarOS HTML Artifacts 是一个零运行时依赖的浏览器库。它统一负

[English](./README.md)

完整的中文公共接口、生命周期、依赖注入与扩展约定见
[中文 API 文档](./docs/api.zh-CN.md)。

## 安装

为 GitHub Packages 的私有 `@velaros-ai` scope 配置 npm 认证后安装:
Expand Down Expand Up @@ -118,14 +121,13 @@ v1 协议保持有意的小规模:

## 高级 API

绝大多数应用只需要从包根入口使用 `mountHtmlArtifact()`。需要自定义宿主时,再使用下面两个底层入口:
绝大多数应用只需要从包根入口使用 `mountHtmlArtifact()` 或
`HtmlArtifactRuntime`。需要自定义宿主时,再使用下面的稳定子路径:

```ts
// 与渲染器无关的增量协议解析器。
import {
applyHtmlArtifactProtocolChunk,
createHtmlArtifactProtocolStreamState,
finalizeHtmlArtifactProtocol,
HtmlArtifactProtocolParser,
} from '@velaros-ai/html-artifacts/protocol'

// iframe 文档、高度适配与 URL 安全原语。
Expand Down
73 changes: 70 additions & 3 deletions dist/browser.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ export interface HtmlArtifactHostError {
patchId?: string;
patchType?: string;
}
/**
* Browser operations used by the artifact runtime.
*
* Applications can provide an implementation for alternate DOM hosts, tests, or embedded
* webviews without patching browser globals.
*/
export interface HtmlArtifactBrowserEnvironment {
createBridgeId(): string;
createIframe(): HTMLIFrameElement;
addMessageListener(listener: (event: MessageEvent<unknown>) => void): void;
removeMessageListener(listener: (event: MessageEvent<unknown>) => void): void;
scrollBy(deltaX: number, deltaY: number): void;
}
export interface MountHtmlArtifactOptions {
/** Accessible title applied to the generated iframe. */
title?: string;
Expand All @@ -28,6 +41,8 @@ export interface MountHtmlArtifactOptions {
protocolLimits?: HtmlArtifactProtocolLimits;
/** URL protocols handed to `onLink`. Defaults to HTTP and HTTPS. */
allowedLinkProtocols?: readonly string[];
/** Browser boundary used to create the iframe and subscribe to messages. */
environment?: HtmlArtifactBrowserEnvironment;
onMarkdown?: (text: string) => void;
onPrompt?: (prompt: string) => void;
onLink?: (url: string) => void;
Expand All @@ -48,10 +63,62 @@ export interface HtmlArtifactController {
dispose(): void;
}
/**
* Mount one streaming HTML artifact surface into a DOM element.
* Default DOM implementation used by {@link HtmlArtifactRuntime}.
*
* The controller owns protocol parsing, iframe creation, sandbox transport, height negotiation,
* action validation, and cleanup. Callers only feed model text and handle explicit capabilities.
* Keeping browser access behind this object makes runtime ownership explicit and lets other
* applications adapt the library to webviews or test environments.
*/
export declare class DomHtmlArtifactEnvironment implements HtmlArtifactBrowserEnvironment {
private readonly browserWindow;
private readonly browserDocument;
private fallbackId;
constructor(browserWindow?: Window, browserDocument?: Document);
createBridgeId(): string;
createIframe(): HTMLIFrameElement;
addMessageListener(listener: (event: MessageEvent<unknown>) => void): void;
removeMessageListener(listener: (event: MessageEvent<unknown>) => void): void;
scrollBy(deltaX: number, deltaY: number): void;
}
/**
* Owns one artifact parser, iframe transport, host callbacks, and their complete lifecycle.
*/
export declare class HtmlArtifactRuntime implements HtmlArtifactController {
private readonly options;
readonly iframe: HTMLIFrameElement;
readonly ready: Promise<HTMLIFrameElement>;
private readonly environment;
private readonly minHeight;
private readonly maxHeight;
private readonly initialHeight;
private readonly parser;
private bridgeMessages;
private latestArtifactId;
private disposed;
private consuming;
private frameReady;
private settleReady;
private readonly pendingMessages;
constructor(target: HTMLElement, options?: MountHtmlArtifactOptions);
readonly write: (chunk: string) => HtmlArtifactProtocolEvent[];
readonly finish: () => HtmlArtifactProtocolEvent[];
readonly consume: (chunks: AsyncIterable<string> | Iterable<string>) => Promise<HtmlArtifactSnapshot | null>;
readonly getSnapshot: (artifactId?: string) => HtmlArtifactSnapshot | null;
readonly reset: () => void;
readonly dispose: () => void;
private configureIframe;
private createBridgeMessages;
private createShellDocument;
private reportError;
private invoke;
private assertActive;
private post;
private dispatch;
private applyReportedHeight;
private readonly handleMessage;
private readonly handleLoad;
}
/**
* Compatibility factory for applications that prefer a function entry point.
*/
export declare function mountHtmlArtifact(target: HTMLElement, options?: MountHtmlArtifactOptions): HtmlArtifactController;
//# sourceMappingURL=browser.d.ts.map
2 changes: 1 addition & 1 deletion dist/browser.d.ts.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading