@stemtrooper/learningcode 0.4.9 → 0.4.10

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.
@@ -152,9 +152,57 @@ export function createBanner(theme: ThemeLike): HeaderComponent {
152
152
  * right moment: extensions are loaded in print and JSON modes too, where there is
153
153
  * no header to replace, and starting work in the factory would run for those.
154
154
  */
155
+
156
+ /** Terminal tab title. Just enough of the event context to set it. */
157
+ const TITLE = "learningcode by the learning curve";
158
+ type TitleContext = {
159
+ hasUI: boolean;
160
+ mode: string;
161
+ ui: { setTitle(title: string): void };
162
+ };
163
+
164
+ /**
165
+ * Pi rewrites its own π title after extensions bind and again after its async
166
+ * startup package check on Windows, so one write is never enough. The title is
167
+ * set on every event where Pi rewrites its own (startup, renames, new turns),
168
+ * plus once deferred past the startup check. Each write is last-writer-wins in
169
+ * our favour on at least one of those paths.
170
+ */
171
+ const TITLE_REASSERT_MS = 2500;
172
+
155
173
  export default function learningcodeBanner(pi: ExtensionAPI) {
156
- pi.on("session_start", async (_event, ctx) => {
157
- if (!ctx.hasUI || ctx.mode !== "tui") return;
158
- ctx.ui.setHeader((_tui, theme) => createBanner(theme as ThemeLike));
159
- });
174
+ let titleTimer: ReturnType<typeof setTimeout> | undefined;
175
+
176
+ const applyTitle = (ctx: TitleContext) => {
177
+ try {
178
+ ctx.ui.setTitle(TITLE);
179
+ } catch {
180
+ // Cosmetic only: a title failure must never break startup.
181
+ }
182
+ };
183
+
184
+ const titleHook =
185
+ (defer: boolean) => async (_event: unknown, ctx: TitleContext) => {
186
+ if (!ctx.hasUI || ctx.mode !== "tui") return;
187
+ applyTitle(ctx);
188
+ if (!defer) return;
189
+ if (titleTimer) clearTimeout(titleTimer);
190
+ titleTimer = setTimeout(() => {
191
+ titleTimer = undefined;
192
+ applyTitle(ctx);
193
+ }, TITLE_REASSERT_MS);
194
+ titleTimer.unref?.();
195
+ };
196
+
197
+ pi.on("session_start", async (_event, ctx) => {
198
+ if (!ctx.hasUI || ctx.mode !== "tui") return;
199
+ ctx.ui.setHeader((_tui, theme) => createBanner(theme as ThemeLike));
200
+ await titleHook(true)(_event, ctx);
201
+ });
202
+ pi.on("session_info_changed", titleHook(false));
203
+ pi.on("agent_start", titleHook(false));
204
+ pi.on("session_shutdown", async () => {
205
+ if (titleTimer) clearTimeout(titleTimer);
206
+ titleTimer = undefined;
207
+ });
160
208
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stemtrooper/learningcode",
3
- "version": "0.4.9",
3
+ "version": "0.4.10",
4
4
  "description": "TLC Spark coding agent for students: Pi wired to the Spark OpenAI-compatible endpoint with per-student tokens, quota and seat-queue awareness.",
5
5
  "type": "module",
6
6
  "license": "MIT",