0.3.3
Files a module linked can be checked and shown in their folder. The new calls need Tavora 0.23.6 (later), so MIN_TAVORA stays (Tavora 0.19.32).
files.pickReadOnly({ extensions, multiple: true })lets the user pick several files in one go and answers with a list (empty when the dialog was closed). Withoutmultipleit works as before.files.check(id)says whether a linked file is still there, with its size and last change ({ found: true, size, modifiedAt }), or why not ({ found: false, reason: "gone" | "unlinked" }), without reading it.unlinkedmeans this PC has no link for the id, such as one made on another PC.files.showInFolder(id)opens the folder of a linked file with the file selected. It needs a click or key press.files.readreads files up to 50 MiB (was 32 MiB).- The memory host (
@tavora/sdk/testing) answers the new calls and defaults to Tavora 0.23.6.
0.3.2
Two rare item-session cases. Nothing changes on the wire, so MIN_TAVORA stays (Tavora 0.19.32).
session.remove(id)of the open item while itsrestoreDeleted()still runs waits for the restore, then deletes the item and opens the next one, where the restore could put it back after the remove.pick(id)andrename(id, …)of a missing item reject withItemNotFoundErroralso whenparsethrows fornull, where they gave the parse(null) error.
0.3.1
The last gaps of the item session, and the kit's last deprecated helpers go. Nothing changes on the wire, so MIN_TAVORA stays (Tavora 0.19.32).
session.restoreDeleted(copy?)puts backcopyinstead ofdeletedCopy, so edits made after the "deleted elsewhere" notice are not lost. Without an argument it works as before (the kept copy;onDeletedstays "keep" by default). The kit'suseItemSessionpasses it through.restoreDeletedno longer cancels apickthat is still opening: the item is put back in storage, and the pick wins.reload()was checked for the same race and the "deleted, re-created by this panel's owndocuments.put, thenreload()" case: it does not cancel a pick, and the unsaved edits are applied on top of the newer copy.- A
parsethat throws fornullnow fails with a message that says so (parse(null) must return a valid empty value …), where the raw error hid that the document was missing or deleted. - The kit no longer exports
serial(the document store saves one after another), andconnectModuleno longer accepts no argument or a callback: useconnectModule({ texts })(or{}) and readhandle.sdk,handle.languageandhandle.onLanguage.
0.3.0
The helper entries are complete: testing, store (document store, item session, result queue, run store), bootstrap and the generated protocol table. Nothing changes on the wire, so MIN_TAVORA stays (Tavora 0.19.32).
- Item session:
reload()reads the open item again after something other than its handle wrote it (the panel's owndocuments.putis not reported back to it), keeping unsaved edits on top;deletedCopyis the last copy of an item deleted by another panel (unsaved edits included, null otherwise), so a panel can keep showing it.parse(null)must return a valid placeholder: the docs say so now. - Document store:
reload()on the handle is the same for one document. - The kit's
useItemSessionreturnsdeletedCopyandreload. - The kit no longer exports
updateDocument,readDocument,applyTheme,VersionedandUpdateOptions; useopenDocument/useDocument, andconnectModule({ texts }).serialand the callback form ofconnectModulestayed one more version (removed in 0.3.1).
0.2.21
The protocol table: every call declared once. Nothing changes on the wire or for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
METHOD_INFOin@tavora/sdk/protocolholds, for each call, the first Tavora that answers it (since), the permission it needs (capability) and whether it needs a click or key press (gesture). The memory host (@tavora/sdk/testing) reads it instead of its own copy, and so doesapps/docs/reference/protocol.md, whose method index is generated from it.needsGesture(call, params),supportsCall(version, call)andversionAtLeast(version, wanted)are exported with it, andHttpCallstypes the file, picture and blob calls that go over HTTP.sdk.supports(call)says whether the Tavora the module runs in has a call:sdk.supports("storage.compareAndSet"),sdk.supports("panel.openDialog"). Use it to pick a fallback before calling.MIN_TAVORAis worked out from the table (the newest call not markedlater, andpermissions.onRequest), not written by hand.- The memory host now agrees with Tavora about capabilities:
clipboard.writeImage,files.*andoverlay.publish/overlay.removeneed no grant there, as in the real host (they answeredPERMISSION_DENIEDin tests before). - Doc comments and the changelog name the real error code
UNKNOWN_METHOD, where they saidMETHOD_NOT_FOUND.
0.2.20
The result queue and the run store: openResults and openRuns in @tavora/sdk/store. Nothing changes for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
openResults(sdk, { boards?, key?, pollMs? })keeps finished games for a leaderboard.record(board, result)stores the result instorage.appat once (compare-and-set, so two panels never lose one) and sends in the background, in batches of up to 50 per board throughleaderboards.submit. Results the board refused leave the queue like taken ones; results wait while the user is signed out or has not agreed (status"waiting", checked again everypollMs), while Tavora's service is away ("offline", retried after 1, 2, 4 … at most 30 minutes) and when the leaderboard connector is not allowed ("denied"). A panel resuming sends too.onStanding(fn)getsleaderboards.mine()after results were taken (not on a Tavora older than 0.21.97).pending,has(id),flush(),onStatus(fn)anddispose()complete it. A Tavora without anonlinefield in the status counts as online.rowsOf(answer),isOffline(status)andretryDelay(failures)are exported for the leaderboard tab:topanswers with a bare list on Tavora 0.21.92 and{ rows }later.openRuns(sdk, { validate, awaitsUpload, keys?, groups?, groupOf?, keep?, now? })keeps the played games of a daily-puzzle module (start,change,finish,expire,makeCasual,fold,statistics,load,onChange). Every change is a compare-and-set on the run's own key and is applied again on the newest copy; a ranked run whose time is up ends first, so a move after the deadline never counts.fold()folds old finished unlimited runs into a summary but keeps runs thatawaitsUpload, and yields to another panel that folded first. Also exported:localDate,previousDate,msUntilMidnight,onItsDay,runStatistics,finishRun,expireRun,makeRunCasual,defaultRunKeys.
0.2.19
The anchor form: pass the element a menu or tooltip is for. Nothing changes on the wire or for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
ui.showMenu({ anchor, items, side?, align? }):anchoris the element (its box is read when you call) or a box{ x, y, width?, height? }. The SDK sends that box asat; Tavora adds the gap and opens the menu next to it, never at the pointer.atstill works for the rare case without an element; with neither, the call rejects withINVALID_PARAMS. The newMenuOptionstype describes the argument (MenuRequeststays the wire shape).ui.tooltip.showandui.tooltip.movetakeanchorinstead ofat: with an element, the tooltip opens next to it, at the middle of its right edge.TooltipShowOptionsandTooltipMoveOptionsare the new argument types.- The memory host (
@tavora/sdk/testing) does the same, sohost.calls["ui.showMenu"]holds the box that was sent.
0.2.18
The item session: openItems in @tavora/sdk/store. Nothing changes for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
openItems(sdk, { prefix, parse, title, rename?, newItem?, content?, onAdd?, onDeleted?, followNavigate?, restore?, whenEmpty?, merge?, debounceMs? })keeps the items of one kind (documents whose id starts withprefix) and the one a panel has open, on top ofopenDocument.itemsfollowsdocuments.onChangeand the panel's own saves;currentis the open item's document handle.pick(id)saves the open item and opens another; a slow pick that finishes after a newer one resolvesfalse, an unknown id rejects withItemNotFoundError.create,renameandremovekeep the list in step;removedeletes only the copy that was listed (elseCONFLICT), opens the next item when it was the open one and returns anundo. A panel that was open before (or opened bypanels.open) shows its item again; a new one followsonAdd(last,first,none).panel.setContentandpanel.onNavigateare kept in step. When another panel deletes the open item, the session stays on it (onDeleted: "keep", default;deletedturns true andrestoreDeleted()puts its last copy back) or opens the next one ("fallback");flush()andpicknever create a deleted item again. Apickthat cannot save the open item rejects withItemSaveErrorand keeps it open.job(fn)runs one async job at a time and shows what other panels changed meanwhile once afterwards;panels.openand the fallback wait for it (busy).restore(launch, state)lets a module choose the item to show frompanel.getState, andwhenEmpty()creates a starter item once for an empty list.
0.2.17
The module bootstrap: @tavora/sdk/bootstrap. Nothing changes for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
bootstrapModule()connects, applies the host theme now and whenever it changes, and tracks the language. It returns{ sdk, theme, language, status, error?, onLanguage(fn), onTheme(fn), dispose() };dispose()removes every listener it added. A failed connection does not throw:statusis"failed"anderrorsays why, so the page can show "Could not connect".applyHostTheme(theme, root?)writes the theme to the page: every token as--<token>, the summary colours as--host-background,--host-surface,--host-text,--host-text-mutedand--host-accent,--radius,color-scheme, thedarkclass anddata-mode("dark"or"light").isGerman(language)andlanguageOf(language): anything but German (de,de-AT,de_DE, …) is English, as in the host.
0.2.16
The document store: @tavora/sdk/store. Nothing changes for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
openDocument(sdk, id, { parse, title, merge?, debounceMs? })keeps one document for a panel:update(change)shows an edit at once and saves it after a short pause, one save at a time. What the panel shows is what gets saved; make ids and timestamps outside the change function, because after aCONFLICTthe store reads the newest copy and applies your edits on it again (up to 5 attempts). An edit that throws on the newest copy is dropped (onError(error, "change")), the others stay. When a save gives up,onError(error, "save")fires anddatagoes back to the saved copy plus the edits made meanwhile, which are saved next; theonChangeevent hassource: "rollback".statusisidle,dirty,savingorerror, and an emptyflush()leaves it alone. When another panel changes the document, the store reloads it (also after a delete and re-create), or applies your unsaved edits on the new copy; a delete elsewhere pauses saving and keeps your edits until your nextupdateorflush().merge: "mine"is for an editor that owns the whole document: its copy wins a conflict, a failed save keeps the copy on screen (statuserror, retried on the nextflush()orupdate), and another panel's copy arrives asevent.remoteforadoptRemote().flush(),dispose()and the panel being hidden save what is pending.
0.2.15
A test host: @tavora/sdk/testing. Nothing changes for a running module, so MIN_TAVORA stays (Tavora 0.19.32).
createMemoryHost()gives{ sdk, host }:sdkis a wholeTavoraSDKthat keeps its state in memory and follows the real host's rules (limits, document revisions andCONFLICT,compareAndSet, panel state, events), andhostis what a test uses to set things up, raise events and look at the calls the module made. It can simulate an older Tavora, refuse calls that need a click, and share its state with a second panel. See Testing your module.
0.2.14
Panel dialogs, with Tavora 0.21.95 or newer. MIN_TAVORA stays (Tavora 0.19.32): on an older Tavora openDialog throws UNKNOWN_METHOD, so a module can show its settings in the panel instead.
panel.openDialog({ width, height })shows the panel big in a dialog centred on the app, over everything else, with the module's name and a close button: for settings or details that don't fit a small panel. It is the same page, so it keeps all it had. Opening needs a real click or key press.panel.closeDialog()puts it back, andpanel.onDialogChangehears when it opens or closes, also when the user closes it. From a panel over the game, Tavora closes the in-game layer and comes to the front with the dialog open.- Toasts follow the user:
theme.toasts(ToastPosition, one ofTOAST_POSITIONS:"bottom-center","bottom-right"or"top-center") says where they want toasts in a panel; an older Tavora leaves it out, which means bottom center. The new theme tokenstoastandtoast-foregroundcolour them; a theme may leave them out (OPTIONAL_THEME_TOKENS) and Tavora fills them in as flipped pills, so a module always gets every token.
0.2.13
Leaderboards, with Tavora 0.21.92 or newer; board names, the total, average and rate kinds, periods and mine() with Tavora 0.21.97 or newer. Everything else still works on every Tavora the SDK supports (Tavora 0.19.32), so MIN_TAVORA stays; a module that uses leaderboards sets minTavora to 0.21.92.
leaderboards: the boards a manifest'sleaderboardsdeclares, kept on Tavora's service with the user's Tavora account, through the newleaderboardsconnector.status()says whether Tavora's service answered (online, so it never fails offline), whether the user is signed in, whether the boards run for the module, and whether the user agreed to be shown, with their public name once they did.show()(after a click) has Tavora ask the user in the panel;hide()(after a click) takes them off and deletes their results.submit(board, results)sends up to 50 games, each taken or refused on its own, andtop(board, { period, day, limit })reads a board as{ rows }. A module never gets an account id, only aselfmark on the user's own line.- Five kinds of board, so most games find one that fits:
streak(won and lost games, optionally one a day),score(a user's best number, highest or lowest),total(the sum of a user's numbers),average(the mean, afterminGamesgames) andrate(the share of games won, afterminGames). Every board has anameusers see, translatable throughtranslations.boards, andscore,totalandaverageboards may name aunit. - Every board can be read for a
period: a day, a week, a month, a year or all time. A result counts for the periods itsdayis in; the SDK fillsdayin fromatwhen a result has none. mine()gives the user's own standing on every board of the module in every period: rank, how many users the board has, and their numbers. Tavora shows the same in the user's profile.storage.compareAndSet(key, expected, value)andstorage.app.compareAndSet: set a value only while the key still holds what you read, so two panels can't overwrite each other's change.
0.2.12
From Tavora 0.21.43 every item on its rail is a workspace: a game workspace, linked to a game, or a personal workspace.
workspace.getCurrent()answers theTavoraWorkspacethe panel is in:{ id, displayName, kind }, withkind"game"(its id is the game's, the one your manifest'sgameslists) or"personal". It works on every Tavora the SDK supports (Tavora 0.19.32): an older one is askedgame.getCurrentinstead and answered in the new words, soMIN_TAVORAstays.game.getCurrent()andTavoraGameare deprecated: they still answer, withkind"workspace"for a personal workspace, until Tavora's 1.0 preparation.
0.2.11
Stream overlays and hotkeys (Tavora 0.19.26). MIN_TAVORA is now 0.19.32, for permissions.onRequest.
- Stream overlays: a manifest
overlaypage that OBS or other streaming software shows as a Browser Source, with the newstream-overlayconnector.overlay.publish(key, state)sets what an overlay shows (Tavora keeps it while no panel runs),overlay.copyLink(key)copies its link after a click,overlay.removeandoverlay.resetLink. The overlay page reads its state withwatchOverlay(fn). permissions.onRequest: list"stream-overlay"there too, and players are asked for it on the firstoverlay.copyLinkinstead of when they add the module; the call then gives"denied"if they say no (Tavora 0.19.32).- Hotkeys: manifest
actions, which players bind to keys in Tavora's own dialog (hotkeys.set(action, target, label)after a click). Panels hearhotkeys.onPresswhile their game is shown;hotkeys.listandhotkeys.removemanage them. A module never learns the key. - In-game overlays:
overlay.showOverGame,hideOverGame,overGameandpreviewshow, remove, list and preview a module's overlay.panel.setLivesupplies the read-only live view for a panel shown in game. See each method's host-version requirement. - Panels:
isMaximized,setMaximizedandonMaximizedChangeshare the host's Maximize state; maximizing requires a real click or key press. - Files: remember a file chosen with
pickReadOnly, reread it withread, and revoke its reference withforget.saveJsonandopenJsonuse Tavora's dialogs without exposing paths.exportJsonis deprecated in favour ofsaveJson; its limit is now 8 MiB.pictures.savealso uses a save dialog. - System info: the consented
system-infoconnector adds PC readings, running apps, connection checks and history, and Windows traffic totals. Sampling and host-version requirements are documented on the methods and protocol types. - Media: the consented
mediaconnector reads Windows media sessions, watches changes, and sends play/pause, previous or next only after a real click or key press. No player sign-in or token reaches a module.
0.2.10
Requires the JSON export host (Tavora 0.19.17). Adds files.exportJson(value, name?): gesture-gated, bounded JSON export to a new file in Downloads/Tavora.
Changelog
What changed in @tavora/sdk and the bridge protocol it speaks, newest first. Before Tavora 1.0 the SDK may still change in ways that break modules; each such change raises the minor version (0.2 → 0.3) and is marked Breaking here. Anything else raises the patch version. To make sure players have a Tavora that knows what your module uses, set minTavora in its manifest.
0.2.9
network.readPage(url)(Tavora 0.19.3): bounded public HTML capture in a fresh hidden sandbox, using existing exact-domain grants. No module script execution API, interactions or player cookies. Owner disconnect cancels it.MIN_TAVORAis now0.19.3.
0.2.8
- Shared rich tooltips (Tavora 0.18.18). Adds
ui.tooltip.show/move/hide/close/onClosefor checked rich details over neighbouring panels. Hover is pointer-transparent; a pin requires real input. One shared pin remains until closed or its source goes away.
0.2.7
- Manifest
changelog: a JSON file in the package with the module's patch notes,[{ "version", "date", "notes": [...] }], newest first. Players read them under the module in Settings > Modules, and an update marks the new ones (Tavora 0.18.2).MIN_TAVORAis now0.18.2.
0.2.6
game.getCurrent()answerskind:"game", or"workspace"for a personal workspace the player made for modules that belong to no game, such as notes (Tavora 0.18.1). An older Tavora leaves it out, which means a game.MIN_TAVORAis now0.18.1.- Manifest
workspaces: modules for any game are offered in personal workspaces too. Set"workspaces": falsefor one that only makes sense in a game; a module for one game is never offered there (Tavora 0.18.1).
0.2.5
pictures.save(png, name): saves a PNG as a new file in the player'sPictures\Tavorafolder, right after a click or key press in the panel; resolves with the file name it got (Tavora 0.17.19).MIN_TAVORAis now0.17.19.
0.2.4
- Opening another panel:
panels.open(type, { content })opens one of the module's own panel types in the current game, right after a click or key press. Tavora brings forward a panel of that type already showing the content, else asks the one used last to switch throughpanel.onNavigate(fn)(save pending edits, then resolve; throw to refuse; 5 seconds), else opens a new one, whosepanel.getLaunch()is{ reason: "open", content }. It resolves with"shown","switched","opened"or"failed"; when a switch fails, the player is offered the content in a new panel (Tavora 0.17.18). SetminTavorato0.17.18or newer when you use them.
0.2.3
- Content per panel:
panel.setContent({ id, title } | null)tells Tavora what the panel shows now, such as the build picked in your toolbar. Tavora keeps it with the panel across game switches and restarts and shows the title when the player hovers the tab.panel.getLaunch()says why the page is loading:{ reason: "add" }for a panel the player just added (pick the content yourself, such as the last one used), or{ reason: "restore", content }for one that was open before (Tavora 0.17.17). SetminTavorato0.17.17or newer when you use them.
0.2.2
MIN_TAVORA: the oldest Tavora that has everything this SDK version offers, forminTavorain your manifest.- Panel types: a manifest may list
panels, named tools such as Inventory and Build planner that players add, dock and copy on their own, all sharing the module's saved content and permissions.panel.typesays which one a page runs as; the handshake carries it aspanel.type(Tavora 0.17.12). SetminTavorato0.17.12or newer when you use them: an older Tavora refuses thepanelsfield.
0.2.1
ui.showMenu(request): a menu Tavora draws above every panel, so it is never cut off at the panel's edge. Items with an optional colour dot, check or radio mark, hint and danger style; separators; headings; one level of submenus. Only right after a click, right-click or key press in the panel; resolves with the chosen id or null (Tavora 0.17.9).
0.2.0
Needs Tavora 0.17.8 or newer for minTavora; everything else works from the Tavora version named next to it.
minTavorain the manifest: the oldest Tavora a module works with. An older Tavora refuses to install or load it and says why (Tavora 0.17.8).connectors.discord: the Discord app on the player's PC through Tavora's Discord sign-in:state(),connect(),allowControl(),command()for a fixed list of RPC commands,subscribe()andonEvent()for voice events, andimage()for avatars and server icons. Needs"discord"inpermissions.connectorsand the player's consent (Tavora 0.16.11).connectors.aiUsage: the usage of the player's Claude and Codex accounts, without ever seeing a login. Needs"ai-usage"inpermissions.connectors(Tavora 0.16.10).storage.app: small settings shared by every game, next tostoragefor the current game (Tavora 0.16.10).network.fetch: web requests to the hosts inpermissions.network, after the player allowed them; HTTPS only, no cookies (Tavora 0.16.8).clipboard.writeImage(png): puts a PNG on the clipboard right after a click or key press in the panel (Tavora 0.14.1).blobs: pictures a module keeps for a game, served from the module's own address (Tavora 0.12.0).links.open(url): opens a web address in the game's Browser panel, right after a click or key press (Tavora 0.11.0).ui.getLanguage()andui.onLanguageChange(): the language Tavora shows its texts in (Tavora 0.5.9).
0.1.0
The first version: connect(), the current game, storage, documents with revisions and change events, panel state, the panel's size, the theme and its changes, and the suspend, resume and dispose lifecycle. Bridge protocol v1.