From db78ee3f8cc06fc473a9b40759d340a679c552ac Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Sun, 1 Feb 2026 13:11:37 +0700 Subject: [PATCH 1/2] feat: Add layout toggle for all other pages --- AGENTS.md | 58 +++++++++++++++++---- README.md | 9 ++-- TOOL_STATUS.md | 53 -------------------- src/pages/BarcodeGenerator.jsx | 20 +++++++- src/pages/CodeFormatter/index.jsx | 20 +++++++- src/pages/CsvJsonConverter.jsx | 83 ------------------------------- src/pages/DataGenerator/index.jsx | 19 ++++++- src/pages/YamlToJson.jsx | 71 -------------------------- 8 files changed, 105 insertions(+), 228 deletions(-) delete mode 100644 src/pages/CsvJsonConverter.jsx delete mode 100644 src/pages/YamlToJson.jsx diff --git a/AGENTS.md b/AGENTS.md index 57e286c..ff5bc58 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,41 @@ Every tool must follow this hierarchy: 2. **Controls**: A distinct row/area for options (selects, inputs) and primary actions (buttons). 3. **Workspace**: The main area, usually split 50/50 between Input and Output panes. +### Layout Toggle (Horizontal/Vertical) +All tools with split-pane layouts **MUST** include a layout toggle button to allow users to switch between horizontal and vertical arrangements. + +**Implementation Pattern:** +```jsx +import useLayoutToggle from '../hooks/useLayoutToggle'; +import { ToolLayoutToggle } from '../components/ToolUI'; + +// In component: +const layout = useLayoutToggle({ + toolKey: 'unique-tool-key-layout', + defaultDirection: 'horizontal', + showToggle: true, + persist: true +}); + +// In ToolControls: +
+ +
+ +// On ToolSplitPane: + +``` + +**Requirements:** +- Place the toggle button at the end of the `ToolControls` area using `marginLeft: 'auto'` +- Use a unique `toolKey` for each tool to ensure independent persistence +- Always set `persist: true` to remember user's preference +- Update `ToolSplitPane` to dynamically adjust `columnCount` based on layout direction + ### Component Specifics #### Buttons @@ -239,7 +274,7 @@ func TestDecode(t *testing.T) { ## 7. Development Setup ### Prerequisites -- **Node.js** (>= 18) +- **Bun** (>= 1.0) - Required for frontend dependencies - **Go** (>= 1.22) - **Wails CLI** (`go install github.com/wailsapp/wails/v2/cmd/wails@latest`) @@ -249,7 +284,7 @@ func TestDecode(t *testing.T) { git clone https://github.com/vuon9/dev-toolbox.git cd dev-toolbox -# Install frontend dependencies (using Bun) +# Install dependencies (using Bun) bun install ``` @@ -304,7 +339,7 @@ bun run format Follow this step‑by‑step guide to add a new tool component: 1. **Create Component File** - - Create a new `.jsx` file in `frontend/src/tools/` (e.g., `MyNewTool.jsx`). + - Create a new `.jsx` file in `src/pages/` (e.g., `MyNewTool.jsx`). - Use the existing tool components as reference (e.g., `JwtDebugger.jsx`). 2. **Implement the Tool** @@ -316,15 +351,19 @@ Follow this step‑by‑step guide to add a new tool component: - Ensure input/output panes are symmetrical, have monospace fonts, and include copy buttons. 3. **Add Route** - - Open `frontend/src/App.jsx`. + - Open `src/App.jsx`. - Import your new component. - - Add a route entry in the `routes` array: + - Add the component to the `renderTool()` switch statement: ```jsx - { path: '/my-new-tool', element: } + case 'my-new-tool': return ; ``` -4. **Update Sidebar** (Optional) - - If the tool should appear in the sidebar, add an entry in `frontend/src/App.jsx` within the `sidebarItems` array. +4. **Update Sidebar** + - Open `src/components/Sidebar.jsx`. + - Add an entry to the `tools` array: + ```jsx + { id: 'my-new-tool', name: 'My New Tool', icon: '🛠️' } + ``` 5. **Test the Tool** - Run `wails dev` to verify the tool works correctly. @@ -443,11 +482,12 @@ These guidelines are intended for AI assistants (like opencode) working on this 1. **Run linting & formatting** – Execute any available lint/format commands (see section 8). 2. **Test the tool** – Verify functionality with `wails dev`. 3. **Update `TOOL_STATUS.md`** – Update the tool's status and add completion notes (mark as 🟢 Done when complete). -4. **Update `README.md`** – **Whenever TOOL_STATUS.md is updated, review README.md to ensure consistency**: +4. **Update `README.md`** – **REQUIRED: Whenever TOOL_STATUS.md is updated, README.md MUST also be updated to ensure consistency**: - Add new tools to the feature table - Remove deprecated tools - Update descriptions if features changed - Keep the tool count/feature list in sync + - **This is mandatory** - never update TOOL_STATUS.md without checking/updating README.md 5. **Commit changes** – Use descriptive commit messages that reference the tool name and changes made. ### Important Notes diff --git a/README.md b/README.md index adce7ec..5c372df 100644 --- a/README.md +++ b/README.md @@ -41,13 +41,10 @@ The central hub with 45+ algorithms across 5 categories: | **Code Formatter** | Format and minify JSON, XML, HTML, SQL, CSS, and JavaScript with advanced filtering support (jq for JSON, XPath for XML, CSS selectors for HTML) | | **RegExp Tester** | Test regular expressions with real-time matching | | **Unix Time Converter** | Convert between Unix timestamps and human-readable dates | -| **String Case Converter** | Convert between camelCase, snake_case, PascalCase, etc. | +| **String Utilities** | Sort/Dedupe lines, Case conversion (camelCase, snake_case, etc.), String Inspector | | **Cron Job Parser** | Parse and explain cron expressions | | **Text Diff Checker** | Compare two text blocks and highlight differences | -| **Line Sort / Dedupe** | Sort lines, remove duplicates, trim whitespace | -| **String Inspector** | Count characters, words, lines, bytes, and sentences | -| **PHP Serializer** | PHP serialization/unserialization | -| **URL Tools** | Parse URLs, extract components | +| **Number Converter** | Convert between Decimal, Hex, Octal, and Binary | ## Installation @@ -62,7 +59,7 @@ Download the latest release for your platform from the [Releases](https://github ### Build from Source **Prerequisites:** -- Node.js (>= 18) +- Bun (>= 1.0) - Required for frontend dependencies - Go (>= 1.22) - Wails CLI: `go install github.com/wailsapp/wails/v2/cmd/wails@latest` diff --git a/TOOL_STATUS.md b/TOOL_STATUS.md index a3ae59e..32df695 100644 --- a/TOOL_STATUS.md +++ b/TOOL_STATUS.md @@ -26,59 +26,6 @@ This document tracks the refactoring and development status of each tool compone | **TextDiffChecker** | 🟡 In Progress | Refactored with enhanced features. Features: Diff mode switcher (Lines/Words/Chars), auto-compare on input change, Clear button, improved diff view with color coding, layout toggle. | Updated 2026-01-31 | | **UnixTimeConverter** | 🟡 In Progress | Refactored with new features. Features: Relative time display (e.g., "2 hours ago"), split-pane layout (ISO 8601 / Local), "Now" button with icon, layout toggle, auto-initialization with current time. | Updated 2026-01-31 | -### Removed Tools (Consolidated) - -| Tool | Replacement | Reason | -|------|-------------|--------| -| JsonFormatter | CodeFormatter | Unified formatting tool | -| SqlFormatter | CodeFormatter | Unified formatting tool | -| UrlTools | TextConverter | URL encode/decode functionality | -| UrlParser | TextConverter | URL parsing functionality | -| UrlEncoder | TextConverter | URL encoding functionality | -| PhpSerializer | - | Removed - low usage | -| PhpJsonConverter | - | Removed - low usage | -| LineSortDedupe | StringUtilities | Consolidated into StringUtilities | -| StringCaseConverter | StringUtilities | Consolidated into StringUtilities | -| StringInspector | StringUtilities | Consolidated into StringUtilities | -| NumberBaseConverter | NumberConverter | Renamed and refactored | -| TextBasedConverter | TextConverter | Renamed for clarity | - ---- - -## Final Tool Count: 11 Tools - -1. **Text Converter** - Encoding, encryption, hashing, escaping -2. **String Utilities** - Sort/Dedupe, Case conversion, Inspector -3. **Number Converter** - Decimal, Hex, Octal, Binary conversions -4. **Unix Time Converter** - Timestamp conversions with relative time -5. **JWT Debugger** - JWT encode/decode/verify -6. **RegExp Tester** - Regular expression testing -7. **Cron Job Parser** - Cron expression parsing -8. **Text Diff Checker** - Text comparison -9. **Code Formatter** - JSON, XML, HTML, SQL, CSS, JS formatting -10. **Barcode Generator** - QR codes and barcodes -11. **Data Generator** - Mock data generation - ---- - -## Refactoring Checklist - -When refactoring a tool, ensure: - -- [x] Uses **Carbon Design System** components (`@carbon/react`) -- [x] All colors use `var(--cds-*)` tokens, no hardcoded hex values -- [x] Implements **useReducer** for state management (not multiple useState hooks) -- [x] Uses **useCallback** for memoized functions -- [x] Follows **DRY principle** - no duplicated components/logic -- [x] Has proper **ToolHeader** with title and description -- [x] Input/Output panes are symmetrical and use **Carbon TextArea** -- [x] All buttons properly spaced (gap: 1rem) -- [x] Copy buttons present on all output/data panes -- [x] Monospace font for data (`'IBM Plex Mono', monospace`) -- [x] Proper flex layout for responsive sizing -- [x] No unused imports or variables -- [x] Code compiles without errors or warnings - --- ## How to Update This File diff --git a/src/pages/BarcodeGenerator.jsx b/src/pages/BarcodeGenerator.jsx index 583f1ae..e929c86 100644 --- a/src/pages/BarcodeGenerator.jsx +++ b/src/pages/BarcodeGenerator.jsx @@ -1,7 +1,8 @@ import React, { useState, useCallback, useRef } from 'react'; import { Button, Dropdown, InlineLoading } from '@carbon/react'; import { Renew, Download } from '@carbon/icons-react'; -import { ToolHeader, ToolPane, ToolSplitPane } from '../components/ToolUI'; +import { ToolHeader, ToolPane, ToolSplitPane, ToolLayoutToggle } from '../components/ToolUI'; +import useLayoutToggle from '../hooks/useLayoutToggle'; import { Backend } from '../utils/backendBridge'; const BARCODE_STANDARDS = [ @@ -119,6 +120,13 @@ export default function BarcodeGenerator() { const [loading, setLoading] = useState(false); const [error, setError] = useState(''); + const layout = useLayoutToggle({ + toolKey: 'barcode-generator-layout', + defaultDirection: 'horizontal', + showToggle: true, + persist: true + }); + // Track what was last generated to prevent duplicate generation const lastGeneratedParams = useRef({ content: '', standard: 'QR', size: 256, level: 'M' }); @@ -288,9 +296,17 @@ export default function BarcodeGenerator() { > Generate + +
+ +
- + {/* Input Pane */} { localStorage.setItem(STORAGE_KEY, JSON.stringify({ @@ -206,6 +214,14 @@ export default function CodeFormatter() { + +
+ +
@@ -221,7 +237,7 @@ export default function CodeFormatter() { )} - + { - if (!input.trim()) { - setOutput(''); - setError(''); - return; - } - setError(''); - - if (mode === 'csv2json') { - Papa.parse(input, { - header: true, - skipEmptyLines: true, - complete: (results) => { - if (results.errors.length > 0) { - setError('CSV Parse Error: ' + results.errors[0].message); - setOutput(''); - } else { - setOutput(JSON.stringify(results.data, null, 2)); - } - }, - error: (err) => { - setError('CSV Parse Error: ' + err.message); - setOutput(''); - } - }); - } else { - try { - const json = JSON.parse(input); - const csv = Papa.unparse(json); - setOutput(csv); - } catch (e) { - setError('Invalid JSON: ' + e.message); - setOutput(''); - } - } - }; - - return ( -
- - - - - - - - - - - {error &&
{error}
} - - - setInput(e.target.value)} - /> - - -
- ); -} diff --git a/src/pages/DataGenerator/index.jsx b/src/pages/DataGenerator/index.jsx index 17ac4f5..8c4c82f 100644 --- a/src/pages/DataGenerator/index.jsx +++ b/src/pages/DataGenerator/index.jsx @@ -1,6 +1,7 @@ import React, { useEffect, useCallback } from 'react'; import { InlineNotification } from '@carbon/react'; -import { ToolHeader, ToolControls, ToolPane, ToolSplitPane } from '../../components/ToolUI'; +import { ToolHeader, ToolControls, ToolPane, ToolSplitPane, ToolLayoutToggle } from '../../components/ToolUI'; +import useLayoutToggle from '../../hooks/useLayoutToggle'; import { Backend } from '../../utils/backendBridge'; import { initialState, reducer } from './constants'; import GeneratorControls from './components/GeneratorControls'; @@ -10,6 +11,13 @@ import HelpModal from './components/HelpModal'; export default function DataGenerator() { const [state, dispatch] = React.useReducer(reducer, initialState); + const layout = useLayoutToggle({ + toolKey: 'data-generator-layout', + defaultDirection: 'horizontal', + showToggle: true, + persist: true + }); + // Load presets on mount useEffect(() => { const loadPresets = async () => { @@ -131,6 +139,13 @@ export default function DataGenerator() { onPresetChange={handlePresetChange} onGenerate={handleGenerate} /> +
+ +
- + { - const convert = () => { - if (!input.trim()) { - setOutput(''); - setError(''); - return; - } - - try { - if (mode === 'yaml2json') { - const obj = yaml.load(input); - setOutput(JSON.stringify(obj, null, 2)); - } else { - const obj = JSON.parse(input); - setOutput(yaml.dump(obj)); - } - setError(''); - } catch (e) { - setError(e.message); - setOutput(''); - } - }; - convert(); - }, [input, mode]); - - return ( -
- - - - - - - - - - {error &&
{error}
} - - - setInput(e.target.value)} - placeholder={mode === 'yaml2json' ? 'Enter YAML...' : 'Enter JSON...'} - /> - - -
- ); -} From cf86664fcd2228978c6cb832d7e84699159a8af5 Mon Sep 17 00:00:00 2001 From: Vuong <3168632+vuon9@users.noreply.github.com> Date: Sun, 1 Feb 2026 13:27:58 +0700 Subject: [PATCH 2/2] try: gh opencode --- .github/workflows/opencode.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.github/workflows/opencode.yml b/.github/workflows/opencode.yml index a82b792..50c5e60 100644 --- a/.github/workflows/opencode.yml +++ b/.github/workflows/opencode.yml @@ -21,13 +21,14 @@ jobs: issues: read steps: - name: Checkout repository - uses: actions/checkout@v6 - with: - persist-credentials: false + uses: actions/checkout@v4 + + - uses: ./.github/actions/setup-bun - name: Run opencode uses: anomalyco/opencode/github@latest env: OPENCODE_API_KEY: ${{ secrets.OPENCODE_API_KEY }} + OPENCODE_PERMISSION: '{"bash": "deny"}' with: model: opencode/kimi-k2.5-free