feat: add platform shells, CLI, formatting, plugins, tests, and benchmarks
Phase 4 — Platform shells: - calcpad-macos/: SwiftUI two-column editor with Rust FFI bridge (16 files) - calcpad-windows/: iced GUI with Windows 11 Fluent theme (7 files, 13 tests) - calcpad-web/: React 18 + CodeMirror 6 + WASM Worker + PWA (20 files) - calcpad-cli/: clap-based CLI with expression eval, pipe/stdin, JSON/CSV output, and interactive REPL with rustyline history Phase 5 — Engine modules: - formatting/: answer formatting (decimal/scientific/SI notation, thousands separators, currency), line type classification, clipboard values (93 tests) - plugins/: CalcPadPlugin trait, PluginRegistry, Rhai scripting stub (43 tests) - benches/: criterion benchmarks (single-line, 100/500-line sheets, DAG, incremental) - tests/sheet_scenarios.rs: 20 real-world integration tests - tests/proptest_fuzz.rs: 12 property-based fuzz tests 771 tests passing across workspace, 0 failures.
This commit is contained in:
89
calcpad-web/src/App.tsx
Normal file
89
calcpad-web/src/App.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
/**
|
||||
* CalcPad main application component.
|
||||
*
|
||||
* Two-column layout:
|
||||
* Left: CodeMirror 6 editor with CalcPad syntax highlighting
|
||||
* Right: Answer gutter (integrated into CodeMirror) + optional standalone AnswerColumn
|
||||
*
|
||||
* The WASM engine runs in a Web Worker. On each document change (debounced),
|
||||
* the editor sends lines to the worker, which evaluates them and posts back
|
||||
* results. Results are fed into the CodeMirror answer gutter extension.
|
||||
*/
|
||||
|
||||
import { useCallback } from 'react'
|
||||
import { CalcEditor } from './editor/CalcEditor.tsx'
|
||||
import { useEngine } from './engine/useEngine.ts'
|
||||
import { useOnlineStatus } from './hooks/useOnlineStatus.ts'
|
||||
import { useInstallPrompt } from './hooks/useInstallPrompt.ts'
|
||||
import { OfflineBanner } from './components/OfflineBanner.tsx'
|
||||
import { InstallPrompt } from './components/InstallPrompt.tsx'
|
||||
import './styles/app.css'
|
||||
|
||||
const INITIAL_DOC = `# CalcPad
|
||||
|
||||
// Basic arithmetic
|
||||
2 + 3
|
||||
10 * 4.5
|
||||
100 / 7
|
||||
|
||||
// Variables
|
||||
price = 49.99
|
||||
quantity = 3
|
||||
subtotal = price * quantity
|
||||
|
||||
// Percentages
|
||||
tax = subtotal * 8%
|
||||
total = subtotal + tax
|
||||
|
||||
// Functions
|
||||
sqrt(144)
|
||||
2 ^ 10
|
||||
`
|
||||
|
||||
function App() {
|
||||
const engine = useEngine()
|
||||
const isOnline = useOnlineStatus()
|
||||
const installPrompt = useInstallPrompt()
|
||||
|
||||
const handleDocChange = useCallback(
|
||||
(lines: string[]) => {
|
||||
engine.evalSheet(lines)
|
||||
},
|
||||
[engine.evalSheet],
|
||||
)
|
||||
|
||||
return (
|
||||
<div className="calcpad-app">
|
||||
<OfflineBanner isOnline={isOnline} />
|
||||
|
||||
<header className="calcpad-header">
|
||||
<h1>CalcPad</h1>
|
||||
<p className="subtitle">Notepad Calculator</p>
|
||||
<div className="header-status">
|
||||
<span className={`status-dot ${engine.ready ? '' : 'loading'}`} />
|
||||
<span>{engine.ready ? 'Engine ready' : 'Loading engine...'}</span>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main className="calcpad-editor">
|
||||
<div className="editor-pane">
|
||||
<CalcEditor
|
||||
initialDoc={INITIAL_DOC}
|
||||
onDocChange={handleDocChange}
|
||||
results={engine.results}
|
||||
debounceMs={50}
|
||||
/>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<InstallPrompt
|
||||
promptEvent={installPrompt.promptEvent}
|
||||
isInstalled={installPrompt.isInstalled}
|
||||
onInstall={installPrompt.handleInstall}
|
||||
onDismiss={installPrompt.handleDismiss}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
Reference in New Issue
Block a user