Zettly is a modern, full-stack todo list application built with Laravel 12, React, Inertia.js, TailwindCSS, and shadcn/ui. Create, manage, and track your todos with a beautiful and responsive interface.
IMPORTANT: Zettly uses a strict two-color design system - ONLY black, white, and gray colors are allowed throughout the entire application.
- Primary Colors: Black (
#000000) and White (#FFFFFF) - Accent Colors: Gray scale (
gray-50throughgray-900) - NO colored elements: No red, blue, green, yellow, orange, purple, or any other colors
- Consistent theme: All UI components must adhere to this monochromatic palette
- Buttons: Use gray scale only (gray-500, gray-600, gray-700, gray-800, gray-900)
- Backgrounds: White or gray variations only
- Text: Black, white, or gray tones only
- Borders: Gray colors only
- Icons: Black, white, or gray
- Status indicators: Use gray intensity instead of colored badges
- Error states: Use gray backgrounds with darker gray text (no red)
- Success states: Use darker gray backgrounds (no green)
- Primary buttons:
bg-gray-800 hover:bg-gray-900 text-white - Secondary buttons:
bg-gray-600 hover:bg-gray-700 text-white - Outline buttons:
border-gray-300 text-gray-600 hover:bg-gray-100 - Error messages:
bg-gray-200 text-gray-900 border-gray-300 - Success messages:
bg-gray-700 text-white
This two-color system ensures visual consistency, reduces cognitive load, and creates a clean, professional interface.
Detailed historical changes live in docs/reference/RELEASE_NOTES.md and on the GitHub releases page.
-
Clone the repository
git clone <repository-url> cd todoapp
-
Install dependencies
composer install && npm install -
Environment setup
cp .env.example .env php artisan key:generate
-
Database setup
# Configure your database in .env file php artisan migrate php artisan db:seedRunning the seeders now creates todos that already have sample tags (Work, Personal, Important, Learning) attached, so the dashboard filters and tag-based features are immediately usable.
Super administrator access: The
UserSeederpromotesjohn@example.comto a super administrator every time it runs (existing records are updated). Sign in with that account (passwordpassword123) to use the monitoring tools. To elevate another account, runphp artisan tinkerand execute\App\Models\User::where('email', 'you@example.com')->first()?->assignRole(\App\Enums\UserRole::SUPER_ADMIN);. -
Administration & Monitoring
- Visit
/admin/system-monitor(requires super administrator role) to inspect WebSocket status, Pusher credentials, authentication state, and server health from the browser. - The System Monitor page embeds the same realtime diagnostics widget used on the dashboard, but it is permanently enabled for administrators.
- Visit
-
Start development server
composer run dev
- PHPUnit
php artisan test - Frontend regression suites (Node test runner)
The Node test suite now includes targeted regression coverage for the drawing workspace to ensure we never reintroduce the infinite render loop or passive event listener warnings on the TLDraw canvas.
npm run test:js
- ✅ Create & Manage Todos - Add, edit, delete, and complete todos
- ✅ Priority System - Set priority levels (Low, Medium, High, Urgent) with color-coded badges; completed or archived todos automatically drop their priority so the "Completed" lane stays clean, and API updates mirror the same behavior for parity across clients
- ✅ Smart Sorting - Todos automatically sorted by priority and completion status
- ✅ Tag Management - Organize todos with customizable colored tags
- ✅ Todo Linking - Create relationships between related todos
- ✅ Attachments - Upload images/documents, preview images, download files, and delete with a confirmation modal
- ✅ Rich Text Descriptions - Compose todos with the Zettly Editor (TipTap). The app sanitizes all saved HTML, highlights code blocks with Highlight.js, and shows a concise plain-text preview on the overview cards to keep layouts tidy.
- ✅ Dashboard Overview - Focused workspace for prioritizing todos. The home view merges your actionable todos with the Eisenhower Matrix workspace by default, while the Kanban board remains available when the saved preference is set to
kanban.- Todos panel on the left for daily/recent tasks with search and tag filtering
- Eisenhower Matrix in the center (drag tasks between quadrants; urgency/importance auto-adjust)
- Context panel on the right that surfaces linked todos and metadata for the selected task
- ✅ Notes Mode - Capture lightweight notes without due dates or priorities alongside your actionable todos
- ✅ Draw Workspace - Open the TLDraw-powered canvas alongside Todos and Notes, create multiple sketches, and enjoy automatic autosave with persistent storage
The /draw experience introduces a multi-document sketching surface powered by TLDraw. Key capabilities:
- Multiple drawings per user with lazy loading and caching for fast context switches.
- Autosave every keystroke – snapshots queue up and debounce writes to the server, with persisted metadata stored in the new
drawingstable. - Cross-session continuity – drawings are restored on demand, including title edits, undo history, and last-saved status.
- Live updates – changes are synchronized in real-time across multiple tabs/devices using WebSockets.
- Robust migration & seeding – run
php artisan migrateafter updating to create the supporting schema; no seed data is required.
ℹ️ Optional TLDraw License: Set
VITE_TLDRAW_LICENSE_KEYin.envif you have a commercial key. Leaving it blank falls back to the open core features.
To enable real-time collaboration across tabs/devices:
-
Configure Pusher (or compatible WebSocket service):
# Add to your .env file PUSHER_APP_ID=your_pusher_app_id PUSHER_APP_KEY=your_pusher_app_key PUSHER_APP_SECRET=your_pusher_app_secret PUSHER_HOST=your_pusher_host # Optional: for self-hosted PUSHER_PORT=443 PUSHER_SCHEME=https PUSHER_APP_CLUSTER=mt1
-
Install dependencies (already included):
composer require pusher/pusher-php-server npm install --save-dev laravel-echo pusher-js
-
Update broadcasting configuration:
php artisan config:clear
The live update system:
- Broadcasts changes on private channels (
drawings.{id}) for security - Deduplicates updates from the same client to prevent infinite loops
- Automatically reconnects if the connection is lost
- Shows real-time changes without requiring page refresh
Behind the scenes, the app guards against runaway renders and browser warnings by:
- Normalising snapshots before persisting them so corrupted TLDraw payloads are rejected gracefully.
- Debouncing save operations and flushing pending writes when switching drawings.
- Overriding passive event listeners up-front so TLDraw can call
preventDefault()without triggering console spam. - Shipping regression tests (
DrawInfiniteLoopTestandDrawPassiveEventFixTest) that enforce these guardrails during every CI run.
We strive to foster an inclusive, learner-friendly community.
- 📜 Code of Conduct: CODE_OF_CONDUCT.md
- 🛠️ Contributing Guide: CONTRIBUTING.md
- 🔐 Security Policy: SECURITY.md
- 📮 Contact: Mahatma Mahardhika — mahatma.mahardhika@programinglive.com
- 🧾 License: MIT
Pull requests are welcome! Please review the contributing guide and use the pull request template when opening a PR. For sensitive security matters, email the address above instead of posting a public issue.
- Built with the @programinglive/zettly-editor package — explore the editor source and examples in that repository.
The Zettly project proudly uses Algolia to deliver fast, typo-tolerant search across todos, notes, and tags.
- Branding assets live in
public/images/. The Algolia attribution and sponsor card now use the official full-blue SVG (algolia.svg), while the tldraw sponsor tile displays the latest community avatar (tldraw.png) sourced from GitHub. Keep these files intact to preserve required acknowledgements.
Dear Algolia Open Source Team,
My name is Mahatma Mahardhika. I lead a developer community in Indonesia focused on helping students and early-career developers learn programming through real, open-source projects that support the “Golden Indonesia 2045” vision.
I’m developing Zettly — an open-source, privacy-first note-taking app that helps learners capture ideas, link notes using the Zettelkasten method, and organize tasks with the Eisenhower Matrix. It’s fully public, non-commercial, and designed as a teaching tool.
- Main site: https://programinglive.com
- Project: https://github.com/programinglive/zettly
- Demo: https://zettly.programinglive.com
Search is at the core of Zettly. We chose Algolia for its speed, typo tolerance, and developer-friendly API. Algolia would power global note search, tag and language filters, and “Did you mean” features—making it easier for students to explore their notes and learn how modern search works. We also plan to include clear integration examples in our open documentation for educational use.
Zettly also includes a reusable open-source editor component,
@programinglive/zettly-editor, built with TipTap and Shadcn UI for writing and formatting notes.
- Editor: https://github.com/programinglive/zettly-editor
- Demo: https://zettly-editor.programinglive.com
Zettly is open, community-driven, and educational. We will display the “Search by Algolia” attribution and document the integration so contributors can learn from it. This project directly supports developer education and aligns with Algolia’s mission to empower open-source communities.
Thank you for considering our request. Algolia’s support would greatly enhance how learners discover and connect knowledge through Zettly.
Warm regards,
Mahatma Mahardhika
mahatma.mahardhika@programinglive.com
- 🧭 Public Marketing Layout – The landing experience uses
PublicLayout.jsx, a centered design with a marketing-focused navbar, internal anchors, and a theme toggle without exposing account information. - 📊 Authenticated App Shell – Signed-in pages share
DashboardLayout.jsx, which wraps the fluid-widthAppLayout.jsxso dashboard, todos, and other tools stretch edge-to-edge while keeping account menus handy.
The application includes a comprehensive 4-level priority system:
- 🟢 Low - Nice to have tasks
- 🟡 Medium - Standard tasks (default)
- 🔴 High - Important tasks
- 🚨 Urgent - Critical tasks requiring immediate attention
Todos are automatically sorted by priority on all pages, ensuring urgent and high-priority items are always visible first.
Visit the application in your browser and start managing your todos! The interface is intuitive:
- Create new todos with priority levels and tags
- Switch to note mode when you just need to jot something down—notes never require a priority and stay separate from your actionable task list
- Link related todos together
- Upload attachments on the todo show page (image preview supported)
- All destructive actions (e.g., deleting todos, tags, attachments) use a reusable confirmation modal for consistency
Zettly ships with an installable PWA experience so you can keep the dashboard on your home screen and continue working while offline. A service worker and manifest are bundled during npm run build and activated automatically in production.
- Browse to your deployed Zettly instance using Chrome, Edge, or another Chromium browser over HTTPS.
- When the in-app banner appears, click Install app or use the browser's menu (usually
⋮→ Install app). - The app opens in its own window and will update automatically on the next publish.
Apple Safari does not fire the beforeinstallprompt event, so the banner inside Zettly provides manual guidance instead. To complete the install:
- Open the site in Safari (Chrome/Firefox on iOS cannot add PWAs to the home screen).
- Tap the Share button (square with the ↑ arrow).
- Choose Add to Home Screen and optionally rename the shortcut.
- Tap Add. The Zettly icon should now appear on your home screen.
When installed on tablets (iPad, Android tablets), Zettly automatically detects the device and uses the full screen width for an optimized experience. The app:
- Detects tablet devices via user agent (iPad/Android) or screen width ≥ 768px
- Detects PWA standalone mode via the
(display-mode: standalone)media query - Applies full-width layout on tablets in PWA mode
- Respects safe-area insets for notched devices (iPhone X+, modern Android devices)
See .windsurf/docs/PWA_TABLET_FIX.md for implementation details.
ℹ️ Troubleshooting
- Ensure you are using HTTPS (or a LAN IP while testing) – iOS will not install PWAs from
http://localhost.- Disable Private Browsing; the Add to Home Screen option is hidden there.
- Confirm the icon loads by visiting
/apple-touch-icon.png. If it 404s, Safari will skip the install icon.- If the option is missing, remove any previous shortcut created for the same domain and refresh.
- On tablets, if the app still appears small, try uninstalling and reinstalling the PWA after clearing browser cache.
To inspect TipTap lifecycle and toolbar state during development, the app integrates @programinglive/zettly-editor debug logging with a local toggle (no API key, no remote forwarding):
<ZettlyEditor
value={data.description || ''}
onChange={(value) => setData('description', value)}
debug={debugEnabled}
onDebugEvent={handleDebugEvent}
onDebugToggle={setDebugEnabled}
className="zettly-editor-wrapper"
editorClassName="min-h-[240px]"
/>debugEnabledis a local React state controlled by the 🐞 toggle in the toolbar.handleDebugEventlogs structured events to the browser console only when debug is enabled.- No
.envkeys are required for this flow. - Styling overrides in
resources/css/app.cssremove the editor surface/footer borders so only the outer card border remains. Adjust those selectors if you change the wrapper layout. - The same stylesheet now also ensures toolbar buttons keep their active colors in both light and dark themes via the
:root:not(.dark) [data-zettly-editor-toolbar] button[data-state=on]override.
Zettly uses Algolia for full-text search across todos, notes, and tags. The integration uses the native Algolia PHP v4 client (no Scout Extended dependency).
-
Create Algolia indexes in your Algolia Dashboard:
zettly_search
-
Set environment variables in
.env:# Admin key (for server-side indexing) ALGOLIA_APP_ID=your_algolia_app_id ALGOLIA_ADMIN_KEY=your_algolia_admin_key # Search key (for client-side search) VITE_ALGOLIA_APP_ID=your_algolia_app_id VITE_ALGOLIA_SEARCH_KEY=your_algolia_search_key # Index names VITE_ALGOLIA_INDEX_TODOS=zettly_todos VITE_ALGOLIA_INDEX_NOTES=zettly_notes VITE_ALGOLIA_INDEX_TAGS=zettly_tags
-
Import existing data (one-time):
php artisan algolia:import
Use
--clearto clear indexes before importing:php artisan algolia:import --clear
- Server-side indexing: Model observers (
TodoObserver,TagObserver) automatically sync records to Algolia when created, updated, or deleted. - Client-side search: The
NavbarSearchcomponent queries Algolia using the search-only API key, filtering byuser_idfor privacy. - Mobile & Desktop: Search is available on both desktop (navbar center) and mobile (search icon in navbar, expands below).
- Attribution: The search dropdown displays a "Search by Algolia" badge linking to Algolia's site as required for open-source sponsorship.
- Application ID & Admin Key: Found in Algolia Dashboard → Settings > API Keys. Use the Admin API Key for server-side indexing.
- Search-Only API Key: Use this for client-side queries (already restricted to search operations).
- If search shows "Search unavailable", verify
VITE_ALGOLIA_*variables are set and indexes exist. - Run
php artisan algolia:importto re-sync all data. - Check Laravel logs for indexing errors.
- Ensure
GEMINI_API_KEY(and optionalGEMINI_REQUEST_TIMEOUT) are set before hitting the chat endpoint inroutes/web.php. GeminiTestController::chat()now returns a504JSON response when the upstream request times out and logs the failure for later inspection.
- Prerequisites
- Follow Conventional Commits (e.g.,
feat:,fix:,refactor:). AddBREAKING CHANGE:to the body for major bumps. - Ensure the test-suite passes before releasing.
- Follow Conventional Commits (e.g.,
- Create a release
Powered by
npm run release
@programinglive/commiter, this analyzes commits, updatesCHANGELOG.md, bumps the version inpackage.json/package-lock.json, and creates a Git tag with emoji-friendly sections. - Override the version bump
Use
npm run release:minor
release:patchorrelease:majorfor other SemVer increments. - Publish
Create the GitHub/GitLab release after the push if your CI is not configured to do so automatically.
git push --follow-tags
- Install hooks
Run this once after cloning to enable the Husky hooks configured by
npx husky install
@programinglive/commiter. - Enforced format
- Commits must follow the Conventional Commits specification.
- Examples:
feat(todo): add priority filter,fix(api): validate payload,chore: update dependencies.
- Hook behavior
- Husky runs
commitlintvia.husky/commit-msg. Non-compliant messages are rejected, preventing the commit.
- Husky runs
For detailed API documentation, developer guides, and more information, visit the /developer page in the application.
This project is open-sourced software licensed under the MIT license.