-
Notifications
You must be signed in to change notification settings - Fork 0
Template browser widget
Josh Dionne edited this page Aug 25, 2026
·
1 revision
Generated from the thetowersdk repository. Edits made here are overwritten on the next push — change the source file instead.
/**
* TEMPLATE — a browser-safe module.
*
* Everything here runs in a bundle with no Node built-ins. That is the whole
* point of the file: `thetowersdk/node` is the only entry point that needs Node,
* so a widget that never decodes a save file never imports it.
*
* Copy this into a component, a Svelte store, a plain <script type="module"> —
* it has no framework in it.
*
* Browser-safe entry points: data, save, formatting, mechanics, wiki, charts,
* knowledge, inputs. Node-only: node.
*/
import { LAB_CATALOG, type LabCatalogRecord } from 'thetowersdk/data'
import { formatDuration, formatLargeNumber, parseDurationToHours } from 'thetowersdk/formatting'
export interface LabSummary {
readonly name: string
readonly category: string
readonly maxLevel: number
readonly costToMax: string
readonly timeToMax: string
}
/**
* Turn the catalog into rows a UI can render directly.
*
* Formatting happens here rather than in the template, so the numbers a player
* sees match the game's own ladder no matter which framework renders them.
*/
export function summarizeLabs(catalog: readonly LabCatalogRecord[] = LAB_CATALOG): LabSummary[] {
return catalog.map(lab => {
const levels = lab.levels ?? []
const costToMax = levels.reduce((sum, level) => sum + (level.cost ?? 0), 0)
/*
* `duration` is text, and it is NOT all one shape: most rows are "HH:MM:SS"
* (with hours running past 24, e.g. "500:00:00") but some are "0s". Use
* `parseDurationToHours` rather than splitting on ":" — a hand-rolled split
* returns NaN on the "0s" rows and poisons the whole sum.
*/
const secondsToMax = levels.reduce(
(sum, level) => sum + parseDurationToHours(level.duration) * 3600,
0,
)
return {
name: lab.name,
category: lab.category ?? 'Uncategorized',
maxLevel: levels.length,
costToMax: formatLargeNumber(costToMax),
timeToMax: formatDuration(secondsToMax),
}
})
}
/** Narrow to one category — the filter a picker UI needs. */
export function labsInCategory(category: string): LabSummary[] {
return summarizeLabs().filter(lab => lab.category.toLowerCase() === category.toLowerCase())
}
/*
* Rendering, framework-free. Replace with your own view layer; the data above
* does not care which one you use.
*/
export function renderLabTable(rows: readonly LabSummary[]): string {
const header = ['Lab', 'Category', 'Levels', 'Cost to max', 'Time to max']
const body = rows.map(row => [row.name, row.category, String(row.maxLevel), row.costToMax, row.timeToMax])
const widths = header.map((_, index) =>
Math.max(header[index]!.length, ...body.map(cells => cells[index]!.length)))
const line = (cells: readonly string[]): string =>
cells.map((cell, index) => cell.padEnd(widths[index]!)).join(' ')
return [line(header), line(widths.map(width => '-'.repeat(width))), ...body.map(line)].join('\n')
}Generated from TheTowerSDK — do not edit here. Docs and live demos: https://tmrxjd.github.io/TheTowerSDK/
- Quick Start
- Entry Points
- How It Fits Together
- Getting a Save File
- Reading a Save
- Reading The Community Wiki
- Formulas
- Builders
- Charts
- Effective Paths
- Examples
- Templates
- Building a Bot On This
- Google Sheets
- Desktop and Mobile
- Optional Add-ons
- Using It With An AI Agent
- Using Your Own Artwork
- Names And Acronyms
- Patch Notes
- Accuracy
- Versioning
- Where the Docs Live
- Contributing
- Credits
- License
Guides
Examples
- 01-browse-game-data.ts
- 02-read-a-save-file.ts
- 03-plan-upgrades-from-a-save.ts
- 04-generate-a-chart.ts
- 05-generate-a-cost-table.ts
- 06-read-the-community-wiki.ts
- 07-format-like-the-game.ts
- 08-build-a-calculator.ts
- 09-build-a-bot.ts
- 10-read-a-sheet.ts
- 11-build-a-knowledge-base.ts
- 12-show-module-and-card-art.ts
Templates