feat(panel): improve header update loop and animations
This commit is contained in:
@@ -27,6 +27,9 @@ export class Panel {
|
|||||||
#pageAgent: PageAgent
|
#pageAgent: PageAgent
|
||||||
#userAnswerResolver: ((input: string) => void) | null = null
|
#userAnswerResolver: ((input: string) => void) | null = null
|
||||||
#isWaitingForUserAnswer: boolean = false
|
#isWaitingForUserAnswer: boolean = false
|
||||||
|
#headerUpdateTimer: ReturnType<typeof setInterval> | null = null
|
||||||
|
#pendingHeaderText: string | null = null
|
||||||
|
#isAnimating = false
|
||||||
|
|
||||||
get wrapper(): HTMLElement {
|
get wrapper(): HTMLElement {
|
||||||
return this.#wrapper
|
return this.#wrapper
|
||||||
@@ -46,6 +49,7 @@ export class Panel {
|
|||||||
this.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!
|
this.#taskInput = this.#wrapper.querySelector(`.${styles.taskInput}`)!
|
||||||
|
|
||||||
this.#setupEventListeners()
|
this.#setupEventListeners()
|
||||||
|
this.#startHeaderUpdateLoop()
|
||||||
// this.#expand() // debug
|
// this.#expand() // debug
|
||||||
|
|
||||||
this.#showInputArea()
|
this.#showInputArea()
|
||||||
@@ -85,20 +89,19 @@ export class Panel {
|
|||||||
*/
|
*/
|
||||||
dispose(): void {
|
dispose(): void {
|
||||||
this.#isWaitingForUserAnswer = false
|
this.#isWaitingForUserAnswer = false
|
||||||
|
this.#stopHeaderUpdateLoop()
|
||||||
this.wrapper.remove()
|
this.wrapper.remove()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Update status
|
* Update status
|
||||||
*/
|
*/
|
||||||
async #update(stepData: Omit<Step, 'id' | 'stepNumber' | 'timestamp'>): Promise<void> {
|
#update(stepData: Omit<Step, 'id' | 'stepNumber' | 'timestamp'>): void {
|
||||||
const step = this.#state.addStep(stepData)
|
const step = this.#state.addStep(stepData)
|
||||||
|
|
||||||
// Show animation if text changes
|
// Queue header text update (will be processed by periodic check)
|
||||||
const headerText = truncate(step.displayText, 20)
|
const headerText = truncate(step.displayText, 20)
|
||||||
if (this.#statusText.textContent !== headerText) {
|
this.#pendingHeaderText = headerText
|
||||||
await this.#animateTextChange(headerText)
|
|
||||||
}
|
|
||||||
|
|
||||||
this.#updateStatusIndicator(step.type)
|
this.#updateStatusIndicator(step.type)
|
||||||
this.#updateHistory()
|
this.#updateHistory()
|
||||||
@@ -394,25 +397,69 @@ export class Panel {
|
|||||||
this.#expandButton.textContent = '▼'
|
this.#expandButton.textContent = '▼'
|
||||||
}
|
}
|
||||||
|
|
||||||
async #animateTextChange(newText: string): Promise<void> {
|
/**
|
||||||
return new Promise((resolve) => {
|
* Start periodic header update loop
|
||||||
// Fade out current text
|
*/
|
||||||
this.#statusText.classList.add(styles.fadeOut)
|
#startHeaderUpdateLoop(): void {
|
||||||
|
// Check every 450ms (same as total animation duration)
|
||||||
|
this.#headerUpdateTimer = setInterval(() => {
|
||||||
|
this.#checkAndUpdateHeader()
|
||||||
|
}, 450)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stop periodic header update loop
|
||||||
|
*/
|
||||||
|
#stopHeaderUpdateLoop(): void {
|
||||||
|
if (this.#headerUpdateTimer) {
|
||||||
|
clearInterval(this.#headerUpdateTimer)
|
||||||
|
this.#headerUpdateTimer = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if header needs update and trigger animation if not currently animating
|
||||||
|
*/
|
||||||
|
#checkAndUpdateHeader(): void {
|
||||||
|
// If no pending text or currently animating, skip
|
||||||
|
if (!this.#pendingHeaderText || this.#isAnimating) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If text is already displayed, clear pending and skip
|
||||||
|
if (this.#statusText.textContent === this.#pendingHeaderText) {
|
||||||
|
this.#pendingHeaderText = null
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start animation
|
||||||
|
const textToShow = this.#pendingHeaderText
|
||||||
|
this.#pendingHeaderText = null
|
||||||
|
this.#animateTextChange(textToShow)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Animate text change with fade out/in effect
|
||||||
|
*/
|
||||||
|
#animateTextChange(newText: string): void {
|
||||||
|
this.#isAnimating = true
|
||||||
|
|
||||||
|
// Fade out current text
|
||||||
|
this.#statusText.classList.add(styles.fadeOut)
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// Update text content
|
||||||
|
this.#statusText.textContent = newText
|
||||||
|
|
||||||
|
// Fade in new text
|
||||||
|
this.#statusText.classList.remove(styles.fadeOut)
|
||||||
|
this.#statusText.classList.add(styles.fadeIn)
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// Update text content
|
this.#statusText.classList.remove(styles.fadeIn)
|
||||||
this.#statusText.textContent = newText
|
this.#isAnimating = false
|
||||||
|
}, 300)
|
||||||
// Fade in new text
|
}, 150) // Half the duration of fade out animation
|
||||||
this.#statusText.classList.remove(styles.fadeOut)
|
|
||||||
this.#statusText.classList.add(styles.fadeIn)
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
this.#statusText.classList.remove(styles.fadeIn)
|
|
||||||
resolve()
|
|
||||||
}, 300)
|
|
||||||
}, 150) // Half the duration of fade out animation
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#updateStatusIndicator(type: Step['type']): void {
|
#updateStatusIndicator(type: Step['type']): void {
|
||||||
|
|||||||
Reference in New Issue
Block a user