- TypeScript 92.5%
- JavaScript 5.8%
- HTML 1.7%
| .changeset | ||
| .forgejo/workflows | ||
| .storybook | ||
| docs | ||
| src | ||
| stories | ||
| .eleventyignore | ||
| .gitignore | ||
| CHANGELOG.md | ||
| cli.ts | ||
| DEVELOPMENT.md | ||
| eleventy.config.js | ||
| index.html | ||
| package-lock.json | ||
| package.json | ||
| PLAN.md | ||
| README.md | ||
| tsconfig.build.json | ||
| tsconfig.json | ||
| typedoc.json | ||
| vite.config.ts | ||
| vitest.config.ts | ||
| vitest.unit.config.ts | ||
pan-saga
<pan-saga> reads screenplay-style saga scripts and streams them as JSON activity objects — characters speaking, narrator descriptions, cinematic effects. Drop it in any HTML page, point it at a .saga file or write one inline, and iterate the results.
Usage
Inline source (slot)
<pan-saga>
<script slot="src" type="text/saga">
# Int. Bengo Apt.
@ Bengo
> if you omit the hyphen it'll redirect you to
<hello-world
! this is a comment — it is ignored
^ fade to black
</script>
</pan-saga>
Remote source (attribute)
<pan-saga src="https://example.com/story.saga"></pan-saga>
If src is provided but the fetch fails, <pan-saga> falls back to the inline slot="src" script tag if one is present.
Saga format sigils
| Sigil | Meaning | Emitted activity |
|---|---|---|
# … |
Scene heading / location | (sets location context) |
@ … |
Character name | (sets speaker context) |
& … |
Character mood/modifier | (sets mood context, silent) |
> … |
Dialogue | CreateActivity |
^ … |
Cinematic effect | EffectActivity |
< tagname |
Embedded HTML element (attrs follow) | NarrateActivity |
! … |
Comment | (ignored) |
| plain text | Narrator description | NarrateActivity |
Attribute lines for <tagname elements look like key: value or key: (boolean attribute). A blank line ends the attribute block.
Activity shapes
// dialogue
{
"type": "Create",
"actor": { "name": "Bengo" },
"object": { "type": "Note", "content": "if you omit the hyphen it'll redirect you to" },
"location": "Int. Bengo Apt."
}
// narrator text or embedded HTML element
{
"type": "Narrate",
"actor": { "type": "Narrator" },
"content": "<hello-world>"
}
// cinematic effect
{
"type": "Effect",
"content": "fade to black"
}
Element API
Implemented in src/pan-saga.ts as PanSaga extends LitElement. The package has no side effects — you register the element yourself when your app is ready.
HTML attributes
| Attribute | Type | Description |
|---|---|---|
src |
string |
URL to fetch saga text from. Falls back to inline slot content on fetch failure. |
Slots
| Slot | Description |
|---|---|
<script slot="src" type="text/saga"> |
Inline saga source text. Used as primary source if no src attribute, or as fallback if the remote fetch fails. |
JavaScript / TypeScript
import { PanSaga } from "pan-saga";
// Registration is the caller's responsibility — the library has no side effects:
customElements.define("pan-saga", PanSaga);
PanSaga#activities: AsyncIterable<Activity>
Lazy async iterable of parsed Activity objects. Begins resolving once the element is connected to the DOM and the saga source is ready. Defined in src/types.ts.
const el = document.querySelector("pan-saga") as PanSaga;
for await (const activity of el.activities) {
console.log(activity); // CreateActivity | NarrateActivity | EffectActivity
}
The parser itself is a standalone export — usable without the element:
import { parseSaga } from "pan-saga/parser";
for await (const activity of parseSaga(sagaText)) {
console.log(activity);
}
The parser is a streaming async generator — it emits one activity at a time and never buffers the full file. For streaming directly from a fetch response body, see SagaTransformStream in DEVELOPMENT.md.
CLI
Pipe saga text in, get NDJSON activity objects out:
printf '@ Alice\n> Hello world\n^ fade to black' | pan-saga | jq .
npx pan-saga --help # usage + sigil reference
Requires Node 22+.
Quick start
npm run dev # dev server → http://localhost:5173
npm run build # production bundle
npm test # browser tests via vitest + Storybook
See DEVELOPMENT.md for full setup instructions, module layout, tool notes, Storybook details, and how to add tests.
Releases
Versioning is managed with Changesets. See DEVELOPMENT.md for the release workflow.