{"version":3,"file":"progress-bar.d.ts","sourceRoot":"","sources":["../../../../src/modes/interactive/components/progress-bar.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH;;;GAGG;AACH,eAAO,MAAM,kBAAkB,KAAK,CAAC;AAErC;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAErD;AAoBD,MAAM,WAAW,kBAAkB;IAClC,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;CACf;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,kBAAuB,GAAG,MAAM,CAKzG;AAED;;;;;;;GAOG;AACH,wBAAgB,sBAAsB,CACrC,aAAa,EAAE,MAAM,EACrB,UAAU,EAAE,MAAM,GAAG,IAAI,EACzB,OAAO,GAAE,kBAAuB,GAC9B,MAAM,CAMR","sourcesContent":["/**\n * The one progress bar. Every surface that makes the user wait on measurable\n * work renders it through here.\n *\n * There used to be two, written months apart, and they had drifted in three\n * separate ways: the footer drew `▰▱` while the voice panel drew `·` over `·`,\n * the footer said `2.0 MB` while the voice panel said `2 MB`, and each carried\n * its own copy of the percent-and-detail layout. None of that is a decision\n * anyone made — it is just what happens when the same widget is written twice.\n *\n * Bar *width* is still the caller's, because that is a genuine property of the\n * surface: a footer line shares its row with other status, a panel has the room\n * to be finer-grained. Everything else — glyphs, colours, percent, separator,\n * byte formatting — is fixed here, so two bars on screen at once always agree.\n */\n\nimport { theme } from \"../theme/theme.js\";\n\n/**\n * Default cells in a bar. Compact enough that several stacked footer lines\n * still fit a narrow terminal.\n */\nexport const PROGRESS_BAR_CELLS = 12;\n\n/**\n * Megabytes, to one decimal.\n *\n * The decimal is the point: whole megabytes make a slow download look frozen\n * for tens of seconds at a time, which is precisely when the user is deciding\n * whether it has hung.\n */\nexport function formatMegabytes(bytes: number): string {\n\treturn `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\n/**\n * The filled/empty track.\n *\n * The glyphs differ in shape, not only in colour, matching the context gauge in\n * the footer. An earlier version drew both halves as `·` and left the whole\n * reading to the colour, so at a glance — or on a low-contrast theme, or piped\n * somewhere that drops styling — a bar at 10% and one at 90% were identical.\n *\n * A started-but-tiny ratio keeps one filled cell rather than rounding to an\n * empty bar: \"nothing has happened yet\" and \"the first of forty is done\" are\n * different things to be told.\n */\nfunction track(ratio: number, cells: number): string {\n\tconst clamped = Math.max(0, Math.min(1, ratio));\n\tconst filled = Math.max(clamped > 0 ? 1 : 0, Math.min(cells, Math.round(clamped * cells)));\n\treturn theme.fg(\"accent\", \"▰\".repeat(filled)) + theme.fg(\"halftone\", \"▱\".repeat(cells - filled));\n}\n\nexport interface ProgressBarOptions {\n\t/** Track width. Defaults to {@link PROGRESS_BAR_CELLS}. */\n\tcells?: number;\n}\n\n/**\n * A determinate bar: track, percent, and a trailing detail such as\n * `3/12 sessions` or `2.0 MB / 8.0 MB`.\n *\n * Returns a styled string with no leading or trailing space, so callers can\n * place it after their own label. Callers width-clamp.\n */\nexport function renderProgressBar(ratio: number, detail: string, options: ProgressBarOptions = {}): string {\n\tconst cells = options.cells ?? PROGRESS_BAR_CELLS;\n\tconst clamped = Math.max(0, Math.min(1, ratio));\n\tconst percent = `${Math.round(clamped * 100)}%`;\n\treturn `${track(clamped, cells)} ${theme.fg(\"muted\", percent)} ${theme.fg(\"dim\", `· ${detail}`)}`;\n}\n\n/**\n * A download's progress, determinate when the server sent a length and a\n * running byte count when it did not.\n *\n * The indeterminate case deliberately drops the bar rather than animating a\n * fake one: a moving byte count already says \"still going\", and a bar that\n * cannot reach the end is worse than no bar.\n */\nexport function renderDownloadProgress(\n\treceivedBytes: number,\n\ttotalBytes: number | null,\n\toptions: ProgressBarOptions = {},\n): string {\n\tif (totalBytes === null || totalBytes <= 0) {\n\t\treturn theme.fg(\"dim\", `${formatMegabytes(receivedBytes)}…`);\n\t}\n\tconst detail = `${formatMegabytes(receivedBytes)} / ${formatMegabytes(totalBytes)}`;\n\treturn renderProgressBar(receivedBytes / totalBytes, detail, options);\n}\n"]}