alt-plugin-sdk 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/llms.txt +157 -3
- package/package.json +1 -1
package/llms.txt
CHANGED
|
@@ -326,6 +326,23 @@ const recent = await alt.notes.list({ limit: 20 });
|
|
|
326
326
|
const filtered = await alt.notes.list({ folderId: 7, query: 'midterm', limit: 50 });
|
|
327
327
|
const content = await alt.notes.getContent(recent[0].id);
|
|
328
328
|
// { id, title, transcript, memo, summary }
|
|
329
|
+
//
|
|
330
|
+
// `transcript`, `memo`, `summary` are all PLAIN STRINGS, already converted
|
|
331
|
+
// from the host's internal storage format for easy LLM consumption:
|
|
332
|
+
//
|
|
333
|
+
// transcript: LLM-formatted plaintext, one line per entry. Examples:
|
|
334
|
+
// [0:00] Hello everyone, welcome to today's meeting.
|
|
335
|
+
// [0:06 | Speaker 1] I have the numbers ready.
|
|
336
|
+
// [1:23:45 | SPEAKER_0] And in chapter four...
|
|
337
|
+
// (`[m:ss]` or `[h:mm:ss]` for >= 1h. Speaker is appended after
|
|
338
|
+
// ` | ` when present. This is NOT structured JSON — if you need
|
|
339
|
+
// raw timing/speaker data, use getComponent on the transcript
|
|
340
|
+
// component instead, see below.)
|
|
341
|
+
//
|
|
342
|
+
// memo: Markdown, already converted from Alt's internal Plate-JSON
|
|
343
|
+
// rich-text format. Pass back to setMemo as-is.
|
|
344
|
+
//
|
|
345
|
+
// summary: Markdown, same treatment as memo.
|
|
329
346
|
|
|
330
347
|
// Write
|
|
331
348
|
const created = await alt.notes.create({
|
|
@@ -348,12 +365,61 @@ await alt.notes.appendTranscriptLine({
|
|
|
348
365
|
// Focus a note in Alt's main window (requires notes:select)
|
|
349
366
|
await alt.notes.select({ noteId });
|
|
350
367
|
|
|
351
|
-
// Components — fine-grained content blocks attached to a note
|
|
368
|
+
// Components — fine-grained content blocks attached to a note.
|
|
369
|
+
// `getComponent().contentText` is the RAW value as stored by the host — it
|
|
370
|
+
// is NOT pre-converted like getContent() is. The exact shape depends on
|
|
371
|
+
// componentType:
|
|
372
|
+
//
|
|
373
|
+
// transcript JSON-stringified `TranscriptEntry[]`. Parse with
|
|
374
|
+
// JSON.parse to get structured speaker/timing data.
|
|
375
|
+
// This is the right source for anything that needs to
|
|
376
|
+
// line up with the recording — getContent().transcript
|
|
377
|
+
// has already been collapsed to plaintext.
|
|
378
|
+
// memo, summary Plate-JSON (Alt's internal rich-text format). Treat
|
|
379
|
+
// as opaque — there are no SDK helpers to manipulate
|
|
380
|
+
// Plate nodes. To read these as markdown, use
|
|
381
|
+
// getContent() / getMemo() instead. To write, use
|
|
382
|
+
// setMemo() / setSummary() with markdown.
|
|
383
|
+
// slide_summary, Plain text/markdown — exactly what was last upserted
|
|
384
|
+
// meeting_notes via upsertComponent({ contentText, ... }).
|
|
385
|
+
// slides, File-backed components. `contentText` is `null`. Use
|
|
386
|
+
// recording alt.files.read({ fileId }) for the bytes; the fileId
|
|
387
|
+
// comes from alt.files.list({ noteId }).
|
|
388
|
+
|
|
352
389
|
const components = await alt.notes.listComponents({ noteId });
|
|
390
|
+
|
|
353
391
|
const memoComponent = await alt.notes.getComponent({
|
|
354
392
|
componentId: components.find(c => c.componentType === 'memo')!.id,
|
|
355
393
|
});
|
|
356
|
-
// memoComponent.contentText
|
|
394
|
+
// memoComponent.contentText is Plate JSON; use alt.notes.getMemo() or
|
|
395
|
+
// alt.notes.getContent() if you want markdown instead.
|
|
396
|
+
|
|
397
|
+
// Structured transcript (speaker + ms timing) — see recipe in §8 below.
|
|
398
|
+
// The raw transcript contentText is JSON-stringified entries with this
|
|
399
|
+
// internal shape (NOT the SDK's `PluginTranscriptionSegment`, which uses
|
|
400
|
+
// `startMs`/`endMs` — the stored shape uses `start`/`end`):
|
|
401
|
+
interface RawTranscriptSegment {
|
|
402
|
+
start: number; // ms from recording start
|
|
403
|
+
end: number; // ms from recording start
|
|
404
|
+
text: string;
|
|
405
|
+
speaker?: string; // e.g. 'SPEAKER_0', 'Speaker 1'
|
|
406
|
+
translatedText?: string;
|
|
407
|
+
}
|
|
408
|
+
interface RawTranscriptEntry {
|
|
409
|
+
relativeStart?: number; // ms from recording start (entry-level)
|
|
410
|
+
speaker?: string;
|
|
411
|
+
segments?: RawTranscriptSegment[];
|
|
412
|
+
originalText?: string;
|
|
413
|
+
translatedText?: string;
|
|
414
|
+
createdAt: number; // unix ms when the entry was first appended
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
const transcriptComp = components.find(c => c.componentType === 'transcript');
|
|
418
|
+
if (transcriptComp) {
|
|
419
|
+
const raw = await alt.notes.getComponent({ componentId: transcriptComp.id });
|
|
420
|
+
const entries: RawTranscriptEntry[] = JSON.parse(raw.contentText ?? '[]');
|
|
421
|
+
// entries[i].relativeStart, entries[i].speaker, entries[i].segments[j].text
|
|
422
|
+
}
|
|
357
423
|
|
|
358
424
|
// Singleton component types (memo / summary / transcript) auto-upsert —
|
|
359
425
|
// calling upsertComponent for type=memo on a note that already has a memo
|
|
@@ -790,6 +856,76 @@ document.querySelector<HTMLInputElement>('#picker')!.addEventListener(
|
|
|
790
856
|
);
|
|
791
857
|
```
|
|
792
858
|
|
|
859
|
+
### 8.5 Structured transcript with speaker + ms timing
|
|
860
|
+
|
|
861
|
+
`alt.notes.getContent(noteId).transcript` returns LLM-formatted plaintext
|
|
862
|
+
(`[0:06 | Speaker 1] ...`). That's the wrong source when you need to line
|
|
863
|
+
up text with the recording, or render a speaker-labeled UI similar to
|
|
864
|
+
Alt's own transcript view. For that, go through the raw transcript
|
|
865
|
+
component:
|
|
866
|
+
|
|
867
|
+
```ts
|
|
868
|
+
// permissions: ['notes:read']
|
|
869
|
+
import { alt } from 'alt-plugin-sdk';
|
|
870
|
+
|
|
871
|
+
interface RawTranscriptSegment {
|
|
872
|
+
start: number; // ms from recording start
|
|
873
|
+
end: number; // ms from recording start
|
|
874
|
+
text: string;
|
|
875
|
+
speaker?: string; // 'SPEAKER_0', 'Speaker 1', etc.
|
|
876
|
+
translatedText?: string;
|
|
877
|
+
}
|
|
878
|
+
interface RawTranscriptEntry {
|
|
879
|
+
relativeStart?: number; // ms; entry-level start
|
|
880
|
+
speaker?: string;
|
|
881
|
+
segments?: RawTranscriptSegment[];
|
|
882
|
+
originalText?: string;
|
|
883
|
+
translatedText?: string;
|
|
884
|
+
createdAt: number; // unix ms
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
async function getStructuredTranscript(
|
|
888
|
+
noteId: number,
|
|
889
|
+
): Promise<RawTranscriptEntry[]> {
|
|
890
|
+
const components = await alt.notes.listComponents({ noteId });
|
|
891
|
+
const transcript = components.find(c => c.componentType === 'transcript');
|
|
892
|
+
if (!transcript) return [];
|
|
893
|
+
|
|
894
|
+
const raw = await alt.notes.getComponent({ componentId: transcript.id });
|
|
895
|
+
if (!raw.contentText) return [];
|
|
896
|
+
|
|
897
|
+
try {
|
|
898
|
+
const parsed = JSON.parse(raw.contentText);
|
|
899
|
+
return Array.isArray(parsed) ? (parsed as RawTranscriptEntry[]) : [];
|
|
900
|
+
} catch {
|
|
901
|
+
return [];
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Use it:
|
|
906
|
+
const entries = await getStructuredTranscript(noteId);
|
|
907
|
+
for (const entry of entries) {
|
|
908
|
+
for (const seg of entry.segments ?? []) {
|
|
909
|
+
console.log(
|
|
910
|
+
`${seg.start}ms - ${seg.end}ms [${seg.speaker ?? '?'}]:`,
|
|
911
|
+
seg.text,
|
|
912
|
+
);
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
```
|
|
916
|
+
|
|
917
|
+
Two important caveats:
|
|
918
|
+
|
|
919
|
+
- Diarization is opt-in. If the user recorded without diarization
|
|
920
|
+
enabled, `speaker` will be absent on most entries — fall back to a
|
|
921
|
+
single-speaker render.
|
|
922
|
+
- The internal segment shape is `{ start, end }` (milliseconds), NOT the
|
|
923
|
+
SDK's `PluginTranscriptionSegment` (which uses `startMs` / `endMs`).
|
|
924
|
+
The plugin SDK type is for the streaming transcription API; the stored
|
|
925
|
+
format is older and predates that naming choice. Use the inline
|
|
926
|
+
`RawTranscriptSegment` type above (or copy it into your code) rather
|
|
927
|
+
than reusing the SDK type — the field names don't match.
|
|
928
|
+
|
|
793
929
|
---
|
|
794
930
|
|
|
795
931
|
## 9. Errors
|
|
@@ -872,7 +1008,25 @@ The npm package follows semver:
|
|
|
872
1008
|
through Alt's internal Plate-rich-text codec; the SDK gives you the
|
|
873
1009
|
markdown view via `getContent` / `getMemo` and accepts markdown back via
|
|
874
1010
|
`setMemo` / `setSummary`. There is no API to manipulate Plate nodes
|
|
875
|
-
directly.
|
|
1011
|
+
directly. If you call `getComponent` on a memo or summary you get the
|
|
1012
|
+
raw Plate JSON in `contentText` — don't try to parse it, use the
|
|
1013
|
+
markdown surfaces instead.
|
|
1014
|
+
- **`getContent().transcript` is plaintext, not JSON.** It's the same
|
|
1015
|
+
LLM-friendly format Alt uses internally to feed transcripts to LLMs
|
|
1016
|
+
(lines like `[0:06 | Speaker 1] text`). If you need structured
|
|
1017
|
+
speaker/timing data (to render a transcript UI, jump to a timestamp,
|
|
1018
|
+
group by speaker, etc.), don't try to parse `getContent().transcript`
|
|
1019
|
+
— go through `listComponents` → find the `transcript` component →
|
|
1020
|
+
`getComponent` → `JSON.parse(contentText)`. See §8.5 for a worked
|
|
1021
|
+
example. The inline JSON shape is internal and differs from
|
|
1022
|
+
`PluginTranscriptionSegment` (it uses `start`/`end` ms, not `startMs`/
|
|
1023
|
+
`endMs`).
|
|
1024
|
+
- **Components have different `contentText` shapes per type.** The same
|
|
1025
|
+
`getComponent` API gives you Plate JSON for `memo`/`summary`,
|
|
1026
|
+
JSON-stringified entries for `transcript`, plain text for
|
|
1027
|
+
`slide_summary`/`meeting_notes`, and `null` for file-backed types
|
|
1028
|
+
(`slides`/`recording`). Always branch on `componentType` before
|
|
1029
|
+
touching `contentText`.
|
|
876
1030
|
|
|
877
1031
|
---
|
|
878
1032
|
|