feat(tui): allow collapsing archived todo panels
This commit is contained in:
parent
c78b528125
commit
b36007b246
@ -26,7 +26,7 @@ module.exports = {
|
|||||||
],
|
],
|
||||||
// We feed already-compiled JS into babel; don't re-parse as TS/JSX.
|
// We feed already-compiled JS into babel; don't re-parse as TS/JSX.
|
||||||
// @babel/preset-env etc. would over-transform — the compiler is our only
|
// @babel/preset-env etc. would over-transform — the compiler is our only
|
||||||
// transform here.
|
// transform here. babelrc:false stops @babel/cli from walking up the
|
||||||
babelrc: false,
|
// filesystem looking for other configs (the parent repo might add one).
|
||||||
configFile: false
|
babelrc: false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
|||||||
import typescriptParser from '@typescript-eslint/parser'
|
import typescriptParser from '@typescript-eslint/parser'
|
||||||
import perfectionist from 'eslint-plugin-perfectionist'
|
import perfectionist from 'eslint-plugin-perfectionist'
|
||||||
import reactPlugin from 'eslint-plugin-react'
|
import reactPlugin from 'eslint-plugin-react'
|
||||||
|
import reactCompiler from 'eslint-plugin-react-compiler'
|
||||||
import hooksPlugin from 'eslint-plugin-react-hooks'
|
import hooksPlugin from 'eslint-plugin-react-hooks'
|
||||||
import unusedImports from 'eslint-plugin-unused-imports'
|
import unusedImports from 'eslint-plugin-unused-imports'
|
||||||
import globals from 'globals'
|
import globals from 'globals'
|
||||||
@ -43,6 +44,7 @@ export default [
|
|||||||
'custom-rules': customRules,
|
'custom-rules': customRules,
|
||||||
perfectionist,
|
perfectionist,
|
||||||
react: reactPlugin,
|
react: reactPlugin,
|
||||||
|
'react-compiler': reactCompiler,
|
||||||
'react-hooks': hooksPlugin,
|
'react-hooks': hooksPlugin,
|
||||||
'unused-imports': unusedImports
|
'unused-imports': unusedImports
|
||||||
},
|
},
|
||||||
@ -53,6 +55,12 @@ export default [
|
|||||||
'@typescript-eslint/no-unused-vars': 'off',
|
'@typescript-eslint/no-unused-vars': 'off',
|
||||||
'no-undef': 'off',
|
'no-undef': 'off',
|
||||||
'no-unused-vars': 'off',
|
'no-unused-vars': 'off',
|
||||||
|
// React Compiler: warn (not error) so the gate doesn't block merges
|
||||||
|
// while we migrate. Flags patterns that would break the compiler at
|
||||||
|
// runtime (mutating refs during render, non-PascalCase components,
|
||||||
|
// etc.). See audit §5 — we run the compiler in `npm run build` as a
|
||||||
|
// post-pass over tsc's `dist/` output.
|
||||||
|
'react-compiler/react-compiler': 'warn',
|
||||||
'padding-line-between-statements': [
|
'padding-line-between-statements': [
|
||||||
1,
|
1,
|
||||||
{ blankLine: 'always', next: ['block-like', 'block', 'return', 'if', 'class', 'continue', 'debugger', 'break', 'multiline-const', 'multiline-let'], prev: '*' },
|
{ blankLine: 'always', next: ['block-like', 'block', 'return', 'if', 'class', 'continue', 'debugger', 'break', 'multiline-const', 'multiline-let'], prev: '*' },
|
||||||
@ -89,6 +97,9 @@ export default [
|
|||||||
'no-constant-condition': 'off',
|
'no-constant-condition': 'off',
|
||||||
'no-empty': 'off',
|
'no-empty': 'off',
|
||||||
'no-redeclare': 'off',
|
'no-redeclare': 'off',
|
||||||
|
// Ink internals: reconciler, style pool, DOM node impl — full of
|
||||||
|
// intentional side effects the compiler rules reject.
|
||||||
|
'react-compiler/react-compiler': 'off',
|
||||||
'react-hooks/exhaustive-deps': 'off'
|
'react-hooks/exhaustive-deps': 'off'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -6,7 +6,8 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "npm run build --prefix packages/hermes-ink && tsx --watch src/entry.tsx",
|
"dev": "npm run build --prefix packages/hermes-ink && tsx --watch src/entry.tsx",
|
||||||
"start": "tsx src/entry.tsx",
|
"start": "tsx src/entry.tsx",
|
||||||
"build": "npm run build --prefix packages/hermes-ink && tsc -p tsconfig.build.json && chmod +x dist/entry.js",
|
"build": "npm run build --prefix packages/hermes-ink && tsc -p tsconfig.build.json && npm run build:compile && chmod +x dist/entry.js",
|
||||||
|
"build:compile": "babel dist --out-dir dist --config-file ./babel.compiler.config.cjs --extensions .js --keep-file-extension",
|
||||||
"type-check": "tsc --noEmit -p tsconfig.json",
|
"type-check": "tsc --noEmit -p tsconfig.json",
|
||||||
"lint": "eslint src/ packages/",
|
"lint": "eslint src/ packages/",
|
||||||
"lint:fix": "eslint src/ packages/ --fix",
|
"lint:fix": "eslint src/ packages/ --fix",
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { Box, Text } from '@hermes/ink'
|
import { Box, Text } from '@hermes/ink'
|
||||||
import { memo } from 'react'
|
import { memo, useState } from 'react'
|
||||||
|
|
||||||
import { countPendingTodos } from '../lib/liveProgress.js'
|
import { countPendingTodos } from '../lib/liveProgress.js'
|
||||||
import { todoGlyph, todoTone } from '../lib/todo.js'
|
import { todoGlyph, todoTone } from '../lib/todo.js'
|
||||||
@ -13,7 +13,7 @@ const rowColor = (t: Theme, status: TodoItem['status']) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const TodoPanel = memo(function TodoPanel({
|
export const TodoPanel = memo(function TodoPanel({
|
||||||
collapsed = false,
|
collapsed,
|
||||||
incomplete = false,
|
incomplete = false,
|
||||||
onToggle,
|
onToggle,
|
||||||
t,
|
t,
|
||||||
@ -25,6 +25,25 @@ export const TodoPanel = memo(function TodoPanel({
|
|||||||
t: Theme
|
t: Theme
|
||||||
todos: TodoItem[]
|
todos: TodoItem[]
|
||||||
}) {
|
}) {
|
||||||
|
// Fallback local state for archived todos in transcript where there's no
|
||||||
|
// external controller. Live TodoPanel passes collapsed+onToggle from the
|
||||||
|
// turn store so clicks still work there.
|
||||||
|
const [localCollapsed, setLocalCollapsed] = useState(false)
|
||||||
|
const isControlled = typeof collapsed === 'boolean'
|
||||||
|
const effectiveCollapsed = isControlled ? collapsed : localCollapsed
|
||||||
|
|
||||||
|
const handleToggle = () => {
|
||||||
|
if (onToggle) {
|
||||||
|
onToggle()
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isControlled) {
|
||||||
|
setLocalCollapsed(v => !v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!todos.length) {
|
if (!todos.length) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
@ -34,9 +53,9 @@ export const TodoPanel = memo(function TodoPanel({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Box flexDirection="column" marginBottom={1}>
|
<Box flexDirection="column" marginBottom={1}>
|
||||||
<Box onClick={onToggle}>
|
<Box onClick={handleToggle}>
|
||||||
<Text color={t.color.dim}>
|
<Text color={t.color.dim}>
|
||||||
<Text color={t.color.amber}>{collapsed ? '▸ ' : '▾ '}</Text>
|
<Text color={t.color.amber}>{effectiveCollapsed ? '▸ ' : '▾ '}</Text>
|
||||||
<Text bold color={t.color.cornsilk}>
|
<Text bold color={t.color.cornsilk}>
|
||||||
Todo
|
Todo
|
||||||
</Text>{' '}
|
</Text>{' '}
|
||||||
@ -52,7 +71,7 @@ export const TodoPanel = memo(function TodoPanel({
|
|||||||
</Text>
|
</Text>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
{!collapsed && (
|
{!effectiveCollapsed && (
|
||||||
<Box flexDirection="column" marginLeft={2}>
|
<Box flexDirection="column" marginLeft={2}>
|
||||||
{todos.map(todo => {
|
{todos.map(todo => {
|
||||||
const tone = todoTone(todo.status)
|
const tone = todoTone(todo.status)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user