diff --git a/windows/src/island/island.ts b/windows/src/island/island.ts index 7fdb618027..ecc0b49fea 100644 --- a/windows/src/island/island.ts +++ b/windows/src/island/island.ts @@ -239,7 +239,13 @@ export class Island { break; case "home": this.expand(State.defaultView()); - if (!this.wasInIsland) this.fsm.mouseLeft(); + if (!this.wasInIsland) { + this.fsm.mouseLeft(); + if (!State.isPinned) { + this.homeCollapseAt = performance.now() + State.settings.autoCloseInterval * 1000; + this.ensureRunning(); + } + } break; case "coucou": this.expand("greeting"); @@ -294,7 +300,11 @@ export class Island { if (State.mode !== "expanded") this.setMode("expanded"); else this.animateGeometry(false); State.lastActivity = performance.now(); - this.homeCollapseAt = null; + this.homeCollapseAt = + !this.wasInIsland && !State.isPinned + ? performance.now() + State.settings.autoCloseInterval * 1000 + : null; + this.ensureRunning(); State.notify(); } @@ -328,6 +338,11 @@ export class Island { this.fsm.pinned = State.isPinned; this.fsm.forceHome(); this.expand(view); + if (!this.wasInIsland && !State.isPinned) { + this.homeCollapseAt = performance.now() + State.settings.autoCloseInterval * 1000; + this.fsm.mouseLeft(); + this.ensureRunning(); + } } reveal() { @@ -564,6 +579,9 @@ export class Island { */ followPageCursor() { window.addEventListener("mousemove", (e) => this.onCursor(e.clientX, e.clientY)); + document.addEventListener("mouseleave", () => this.onCursor(-10_000, -10_000)); + window.addEventListener("blur", () => this.onCursor(-10_000, -10_000)); + this.islandEl.addEventListener("mouseleave", () => this.onCursor(-10_000, -10_000)); window.addEventListener("mouseout", (e) => { if (e.relatedTarget == null) this.onCursor(-10_000, -10_000); }); @@ -594,6 +612,7 @@ export class Island { this.fsm.mouseLeft(); if (this.fsm.state === "home" && !State.isPinned) { this.homeCollapseAt = performance.now() + State.settings.autoCloseInterval * 1000; + this.ensureRunning(); } } this.wasInIsland = inIsland; @@ -729,11 +748,14 @@ export class Island { // spends most of its life in. Geometry still has to finish retracting. const settling = this.width.animating || this.height.animating || this.radius.animating; + const countdownActive = + State.mode === "expanded" && !State.isPinned && this.homeCollapseAt != null; const busy = State.mode === "hidden" ? settling : settling || !this.botCx.settled || !this.botCy.settled || !this.botSize.settled || - greetingActive || this.engine.busy || UploadSeq.isActive; + greetingActive || this.engine.busy || UploadSeq.isActive || + countdownActive; if (busy) { requestAnimationFrame(this.frame); @@ -826,6 +848,12 @@ export class Island { const autoClose = State.settings.autoCloseInterval; const windowS = Math.min(10, autoClose * 0.6); const remaining = (this.homeCollapseAt - nowMs) / 1000; + if (remaining <= 0) { + this.homeCollapseAt = null; + this.countdown.style.width = "0px"; + this.collapse(); + return; + } this.countdown.style.width = remaining < windowS ? `${Math.max(0, clamp(remaining / windowS, 0, 1) * 160)}px` : "0px"; } diff --git a/windows/src/style.css b/windows/src/style.css index b2cf6756c8..b0dd08019f 100644 --- a/windows/src/style.css +++ b/windows/src/style.css @@ -245,6 +245,10 @@ body { cursor: default; } +.header-actions button:hover { + color: var(--dim-1); +} + .header-actions button.on { color: var(--ink); } diff --git a/windows/src/views/views.ts b/windows/src/views/views.ts index ac0ac7b6f7..76452b01ee 100644 --- a/windows/src/views/views.ts +++ b/windows/src/views/views.ts @@ -84,6 +84,7 @@ export function buildHeader(actions: ViewActions): ViewHost { const gearBtn = h("button", { title: "Settings", onclick: () => go("settings") }, svg(ICONS.gear, 14)); const soundBtn = h("button", { title: "Mute", onclick: () => actions.toggleSound() }, svg(ICONS.speakerOn, 14)); + const closeBtn = h("button", { title: "Close (Esc)", onclick: () => actions.collapse() }, svg(ICONS.xmark, 11)); function go(v: IslandViewName) { actions.blip(); @@ -94,7 +95,7 @@ export function buildHeader(actions: ViewActions): ViewHost { "div", { id: "header" }, h("div", { class: "tabs" }, tabHome, tabChat, tabDrop), - h("div", { class: "header-actions" }, gearBtn, soundBtn), + h("div", { class: "header-actions" }, gearBtn, soundBtn, closeBtn), ); return {