Rename djarea to mizan and fix React casing conventions
Rename the package from djarea to mizan across the entire codebase — Python package, React library, generators, tests, and examples. Fix JSX/hook casing (MizanProvider, useMizan, etc.) that broke when the original PascalCase names were lowercased during the rename. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,9 @@
|
||||
#!/usr/bin/env python
|
||||
"""
|
||||
Djarea Desktop — PyWebView + Django local RPC.
|
||||
mizan Desktop — PyWebView + Django local RPC.
|
||||
|
||||
Starts a local Django ASGI server and opens a native desktop window.
|
||||
All communication between the UI and backend uses Djarea server functions.
|
||||
All communication between the UI and backend uses mizan server functions.
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -63,7 +63,7 @@ def main():
|
||||
|
||||
base_url = f"http://{host}:{port}"
|
||||
|
||||
if not wait_for_server(f"{base_url}/api/djarea/session/"):
|
||||
if not wait_for_server(f"{base_url}/api/mizan/session/"):
|
||||
print("ERROR: Django server failed to start", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
@@ -83,7 +83,7 @@ def main():
|
||||
import webview
|
||||
|
||||
window = webview.create_window(
|
||||
title="Djarea Desktop",
|
||||
title="mizan Desktop",
|
||||
url=base_url,
|
||||
width=1024,
|
||||
height=768,
|
||||
|
||||
@@ -6,8 +6,8 @@ os.environ.setdefault("DJANGO_SETTINGS_MODULE", "backend.settings")
|
||||
django.setup()
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
from djarea import wrap_asgi
|
||||
from mizan import wrap_asgi
|
||||
|
||||
import backend.djarea_clients # noqa: F401
|
||||
import backend.mizan_clients # noqa: F401
|
||||
|
||||
application = wrap_asgi(get_asgi_application())
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Desktop RPC server functions.
|
||||
|
||||
Tests Djarea's appropriateness for desktop apps:
|
||||
Tests mizan's appropriateness for desktop apps:
|
||||
- Local file system access
|
||||
- SQLite CRUD
|
||||
- System introspection
|
||||
@@ -20,10 +20,10 @@ from pathlib import Path
|
||||
from django.http import HttpRequest
|
||||
from pydantic import BaseModel
|
||||
|
||||
from djarea.client import client
|
||||
from djarea.channels import ReactChannel
|
||||
from djarea.setup.registry import register
|
||||
from djarea.channels import register as register_channel
|
||||
from mizan.client import client
|
||||
from mizan.channels import ReactChannel
|
||||
from mizan.setup.registry import register
|
||||
from mizan.channels import register as register_channel
|
||||
|
||||
|
||||
# =============================================================================
|
||||
@@ -40,12 +40,12 @@ class SystemInfoOutput(BaseModel):
|
||||
home_dir: str
|
||||
cwd: str
|
||||
cpu_count: int
|
||||
djarea_version: str
|
||||
mizan_version: str
|
||||
|
||||
|
||||
@client(websocket=True)
|
||||
def system_info(request: HttpRequest) -> SystemInfoOutput:
|
||||
import djarea
|
||||
import mizan
|
||||
|
||||
return SystemInfoOutput(
|
||||
os_name=platform.system(),
|
||||
@@ -56,7 +56,7 @@ def system_info(request: HttpRequest) -> SystemInfoOutput:
|
||||
home_dir=str(Path.home()),
|
||||
cwd=os.getcwd(),
|
||||
cpu_count=os.cpu_count() or 1,
|
||||
djarea_version=getattr(djarea, "__version__", "dev"),
|
||||
mizan_version=getattr(mizan, "__version__", "dev"),
|
||||
)
|
||||
|
||||
|
||||
@@ -114,16 +114,20 @@ def list_files(request: HttpRequest, directory: str = "~") -> ListFilesOutput:
|
||||
|
||||
entries = []
|
||||
try:
|
||||
for entry in sorted(dir_path.iterdir(), key=lambda e: (not e.is_dir(), e.name.lower())):
|
||||
for entry in sorted(
|
||||
dir_path.iterdir(), key=lambda e: (not e.is_dir(), e.name.lower())
|
||||
):
|
||||
try:
|
||||
stat = entry.stat()
|
||||
entries.append(FileEntry(
|
||||
name=entry.name,
|
||||
path=str(entry),
|
||||
is_dir=entry.is_dir(),
|
||||
size=stat.st_size if not entry.is_dir() else 0,
|
||||
modified=datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
||||
))
|
||||
entries.append(
|
||||
FileEntry(
|
||||
name=entry.name,
|
||||
path=str(entry),
|
||||
is_dir=entry.is_dir(),
|
||||
size=stat.st_size if not entry.is_dir() else 0,
|
||||
modified=datetime.fromtimestamp(stat.st_mtime).isoformat(),
|
||||
)
|
||||
)
|
||||
except (PermissionError, OSError):
|
||||
continue
|
||||
except PermissionError:
|
||||
@@ -268,7 +272,9 @@ register(list_notes, "list_notes")
|
||||
|
||||
|
||||
@client(websocket=True)
|
||||
def create_note(request: HttpRequest, title: str, content: str = "", pinned: bool = False) -> NoteOutput:
|
||||
def create_note(
|
||||
request: HttpRequest, title: str, content: str = "", pinned: bool = False
|
||||
) -> NoteOutput:
|
||||
from backend.models import Note
|
||||
|
||||
note = Note.objects.create(title=title, content=content, pinned=pinned)
|
||||
@@ -403,7 +409,7 @@ def app_info(request: HttpRequest) -> AppInfoOutput:
|
||||
from django.conf import settings
|
||||
|
||||
return AppInfoOutput(
|
||||
app_name="Djarea Desktop",
|
||||
app_name="mizan Desktop",
|
||||
uptime_seconds=round(time.time() - _start_time, 2),
|
||||
db_path=str(settings.DATABASES["default"]["NAME"]),
|
||||
pid=os.getpid(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
Django settings for the Djarea desktop integration test app.
|
||||
Django settings for the mizan desktop integration test app.
|
||||
|
||||
Runs entirely local: SQLite database, in-memory channel layer,
|
||||
no external services required.
|
||||
|
||||
@@ -27,7 +27,7 @@ def serve_dist(request, path="index.html"):
|
||||
|
||||
|
||||
urlpatterns = [
|
||||
path("api/djarea/", include("djarea.urls")),
|
||||
path("api/mizan/", include("mizan.urls")),
|
||||
re_path(r"^(?P<path>assets/.+)$", serve_dist),
|
||||
path("favicon.ico", serve_dist, {"path": "favicon.ico"}),
|
||||
path("", serve_dist),
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Djarea Desktop</title>
|
||||
<title>mizan Desktop</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: system-ui, -apple-system, sans-serif; background: #0f0f0f; color: #e0e0e0; }
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"name": "djarea-desktop-frontend",
|
||||
"name": "mizan-desktop-frontend",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
@@ -7,7 +7,7 @@
|
||||
"build": "vite build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rythazhur/djarea": "file:../../react",
|
||||
"@rythazhur/mizan": "file:../../react",
|
||||
"react": "^19.0.0",
|
||||
"react-dom": "^19.0.0"
|
||||
},
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { useState, useEffect, useCallback } from 'react'
|
||||
import { DjareaProvider, useDjarea, useDjareaStatus } from '@rythazhur/djarea'
|
||||
import { MizanProvider, useMizan, useMizanStatus } from '@rythazhur/mizan'
|
||||
|
||||
// ─── System Info ────────────────────────────────────────────────────────────
|
||||
|
||||
function SystemInfo() {
|
||||
const { call } = useDjarea()
|
||||
const { call } = useMizan()
|
||||
const [info, setInfo] = useState<Record<string, unknown> | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
@@ -33,7 +33,7 @@ function SystemInfo() {
|
||||
// ─── Connection Status ──────────────────────────────────────────────────────
|
||||
|
||||
function StatusBar() {
|
||||
const status = useDjareaStatus()
|
||||
const status = useMizanStatus()
|
||||
return (
|
||||
<div style={{ ...styles.statusBar, color: status === 'connected' ? '#4ade80' : '#f87171' }}>
|
||||
{status}
|
||||
@@ -46,7 +46,7 @@ function StatusBar() {
|
||||
type Note = { id: number; title: string; content: string; pinned: boolean; updated_at: string }
|
||||
|
||||
function Notes() {
|
||||
const { call } = useDjarea()
|
||||
const { call } = useMizan()
|
||||
const [notes, setNotes] = useState<Note[]>([])
|
||||
const [selected, setSelected] = useState<Note | null>(null)
|
||||
const [title, setTitle] = useState('')
|
||||
@@ -140,7 +140,7 @@ function Notes() {
|
||||
type FileEntry = { name: string; path: string; is_dir: boolean; size: number }
|
||||
|
||||
function FileBrowser() {
|
||||
const { call } = useDjarea()
|
||||
const { call } = useMizan()
|
||||
const [dir, setDir] = useState('~')
|
||||
const [entries, setEntries] = useState<FileEntry[]>([])
|
||||
const [parent, setParent] = useState<string | null>(null)
|
||||
@@ -184,17 +184,17 @@ function FileBrowser() {
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<DjareaProvider baseUrl="/api/djarea" autoConnect={false}>
|
||||
<MizanProvider baseUrl="/api/mizan" autoConnect={false}>
|
||||
<div style={{ maxWidth: 960, margin: '0 auto', padding: 24 }}>
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
|
||||
<h1 style={{ fontSize: 24, color: '#fff' }}>Djarea Desktop</h1>
|
||||
<h1 style={{ fontSize: 24, color: '#fff' }}>mizan Desktop</h1>
|
||||
<StatusBar />
|
||||
</div>
|
||||
<SystemInfo />
|
||||
<Notes />
|
||||
<FileBrowser />
|
||||
</div>
|
||||
</DjareaProvider>
|
||||
</MizanProvider>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
[project]
|
||||
name = "djarea-desktop"
|
||||
name = "mizan-desktop"
|
||||
version = "0.1.0"
|
||||
description = "Desktop integration test app for Djarea"
|
||||
description = "Desktop integration test app for mizan"
|
||||
requires-python = ">=3.10"
|
||||
dependencies = [
|
||||
"djarea[channels]",
|
||||
"mizan[channels]",
|
||||
"uvicorn[standard]>=0.30",
|
||||
"pywebview[qt]>=5.0",
|
||||
]
|
||||
|
||||
[tool.uv.sources]
|
||||
djarea = { path = "../django", editable = true }
|
||||
mizan = { path = "../django", editable = true }
|
||||
|
||||
[project.optional-dependencies]
|
||||
dev = [
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import django
|
||||
from django.conf import settings
|
||||
|
||||
|
||||
# Ensure migrations run before tests
|
||||
def pytest_configure():
|
||||
# Import djarea_clients to trigger function registration
|
||||
import backend.djarea_clients # noqa: F401
|
||||
# Import mizan_clients to trigger function registration
|
||||
import backend.mizan_clients # noqa: F401
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
"""
|
||||
REAL integration tests for the Djarea RPC framework layer.
|
||||
REAL integration tests for the mizan RPC framework layer.
|
||||
|
||||
Tests the actual HTTP stack: CSRF, middleware, error codes, validation.
|
||||
Every test makes a real HTTP request — no mocks, no RequestFactory.
|
||||
@@ -14,7 +14,7 @@ from django.test import LiveServerTestCase
|
||||
|
||||
class RealHTTPMixin:
|
||||
def _session_init(self):
|
||||
url = f"{self.live_server_url}/api/djarea/session/"
|
||||
url = f"{self.live_server_url}/api/mizan/session/"
|
||||
resp = urlopen(Request(url))
|
||||
cookies = resp.headers.get_all("Set-Cookie") or []
|
||||
for cookie in cookies:
|
||||
@@ -26,7 +26,7 @@ class RealHTTPMixin:
|
||||
self._cookies = ""
|
||||
|
||||
def _call(self, fn: str, args: dict | None = None):
|
||||
url = f"{self.live_server_url}/api/djarea/call/"
|
||||
url = f"{self.live_server_url}/api/mizan/call/"
|
||||
body = json.dumps({"fn": fn, "args": args or {}}).encode()
|
||||
req = Request(url, data=body, method="POST")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
@@ -37,7 +37,13 @@ class RealHTTPMixin:
|
||||
resp = urlopen(req)
|
||||
return json.loads(resp.read())
|
||||
|
||||
def _raw_post(self, path: str, body: bytes | str, content_type: str = "application/json", include_csrf: bool = False):
|
||||
def _raw_post(
|
||||
self,
|
||||
path: str,
|
||||
body: bytes | str,
|
||||
content_type: str = "application/json",
|
||||
include_csrf: bool = False,
|
||||
):
|
||||
"""Raw POST without the call() envelope — for testing malformed requests."""
|
||||
url = f"{self.live_server_url}{path}"
|
||||
if isinstance(body, str):
|
||||
@@ -55,7 +61,7 @@ class CSRFTests(RealHTTPMixin, LiveServerTestCase):
|
||||
|
||||
def test_session_endpoint_sets_csrf_cookie(self):
|
||||
"""GET /session/ must return a Set-Cookie with csrftoken."""
|
||||
url = f"{self.live_server_url}/api/djarea/session/"
|
||||
url = f"{self.live_server_url}/api/mizan/session/"
|
||||
resp = urlopen(Request(url))
|
||||
cookies = resp.headers.get_all("Set-Cookie") or []
|
||||
|
||||
@@ -64,7 +70,7 @@ class CSRFTests(RealHTTPMixin, LiveServerTestCase):
|
||||
|
||||
def test_call_without_csrf_is_rejected(self):
|
||||
"""POST /call/ without CSRF token must fail."""
|
||||
url = f"{self.live_server_url}/api/djarea/call/"
|
||||
url = f"{self.live_server_url}/api/mizan/call/"
|
||||
body = json.dumps({"fn": "system_info", "args": {}}).encode()
|
||||
req = Request(url, data=body, method="POST")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
@@ -134,7 +140,7 @@ class ErrorCodeTests(RealHTTPMixin, LiveServerTestCase):
|
||||
|
||||
def test_get_method_rejected(self):
|
||||
"""GET to /call/ should be rejected."""
|
||||
url = f"{self.live_server_url}/api/djarea/call/"
|
||||
url = f"{self.live_server_url}/api/mizan/call/"
|
||||
try:
|
||||
resp = urlopen(Request(url))
|
||||
data = json.loads(resp.read())
|
||||
@@ -147,7 +153,7 @@ class ErrorCodeTests(RealHTTPMixin, LiveServerTestCase):
|
||||
self._session_init()
|
||||
try:
|
||||
resp = self._raw_post(
|
||||
"/api/djarea/call/",
|
||||
"/api/mizan/call/",
|
||||
body="not valid json{{{",
|
||||
include_csrf=True,
|
||||
)
|
||||
@@ -162,7 +168,7 @@ class ErrorCodeTests(RealHTTPMixin, LiveServerTestCase):
|
||||
self._session_init()
|
||||
try:
|
||||
resp = self._raw_post(
|
||||
"/api/djarea/call/",
|
||||
"/api/mizan/call/",
|
||||
body=json.dumps({"not_fn": "hello"}),
|
||||
include_csrf=True,
|
||||
)
|
||||
|
||||
@@ -12,7 +12,7 @@ from urllib.request import urlopen, Request
|
||||
|
||||
class RealHTTPMixin:
|
||||
def _session_init(self):
|
||||
url = f"{self.live_server_url}/api/djarea/session/"
|
||||
url = f"{self.live_server_url}/api/mizan/session/"
|
||||
resp = urlopen(Request(url))
|
||||
cookies = resp.headers.get_all("Set-Cookie") or []
|
||||
for cookie in cookies:
|
||||
@@ -24,7 +24,7 @@ class RealHTTPMixin:
|
||||
self._cookies = ""
|
||||
|
||||
def _call(self, fn: str, args: dict | None = None):
|
||||
url = f"{self.live_server_url}/api/djarea/call/"
|
||||
url = f"{self.live_server_url}/api/mizan/call/"
|
||||
body = json.dumps({"fn": fn, "args": args or {}}).encode()
|
||||
req = Request(url, data=body, method="POST")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
@@ -105,6 +105,7 @@ class NotesCRUDTests(RealHTTPMixin, LiveServerTestCase):
|
||||
|
||||
# Verify it's gone
|
||||
from urllib.error import HTTPError
|
||||
|
||||
try:
|
||||
get_data = self._call("get_note", {"id": note_id})
|
||||
self.assertTrue(get_data["error"])
|
||||
|
||||
@@ -18,8 +18,8 @@ class RealHTTPMixin:
|
||||
"""Makes real HTTP requests to the live server."""
|
||||
|
||||
def _session_init(self):
|
||||
"""Hit /session/ to get CSRF cookie, like DjareaProvider does."""
|
||||
url = f"{self.live_server_url}/api/djarea/session/"
|
||||
"""Hit /session/ to get CSRF cookie, like mizanProvider does."""
|
||||
url = f"{self.live_server_url}/api/mizan/session/"
|
||||
req = Request(url)
|
||||
resp = urlopen(req)
|
||||
# Extract csrftoken from Set-Cookie header
|
||||
@@ -33,8 +33,8 @@ class RealHTTPMixin:
|
||||
self._cookies = ""
|
||||
|
||||
def _call(self, fn: str, args: dict | None = None):
|
||||
"""Make a real POST to /api/djarea/call/ with CSRF token."""
|
||||
url = f"{self.live_server_url}/api/djarea/call/"
|
||||
"""Make a real POST to /api/mizan/call/ with CSRF token."""
|
||||
url = f"{self.live_server_url}/api/mizan/call/"
|
||||
body = json.dumps({"fn": fn, "args": args or {}}).encode()
|
||||
req = Request(url, data=body, method="POST")
|
||||
req.add_header("Content-Type", "application/json")
|
||||
@@ -80,7 +80,7 @@ class SystemInfoTests(RealHTTPMixin, LiveServerTestCase):
|
||||
data = self._call("app_info")
|
||||
|
||||
self.assertFalse(data["error"])
|
||||
self.assertEqual(data["data"]["app_name"], "Djarea Desktop")
|
||||
self.assertEqual(data["data"]["app_name"], "mizan Desktop")
|
||||
self.assertGreater(data["data"]["uptime_seconds"], 0)
|
||||
|
||||
|
||||
@@ -89,11 +89,12 @@ class FileSystemTests(RealHTTPMixin, LiveServerTestCase):
|
||||
|
||||
def setUp(self):
|
||||
self._session_init()
|
||||
self.test_dir = Path.home() / ".djarea-test"
|
||||
self.test_dir = Path.home() / ".mizan-test"
|
||||
self.test_dir.mkdir(exist_ok=True)
|
||||
|
||||
def tearDown(self):
|
||||
import shutil
|
||||
|
||||
if self.test_dir.exists():
|
||||
shutil.rmtree(self.test_dir)
|
||||
|
||||
@@ -116,7 +117,9 @@ class FileSystemTests(RealHTTPMixin, LiveServerTestCase):
|
||||
test_content = "Hello from a REAL HTTP integration test!"
|
||||
|
||||
# Write
|
||||
write_data = self._call("write_file", {"path": test_path, "content": test_content})
|
||||
write_data = self._call(
|
||||
"write_file", {"path": test_path, "content": test_content}
|
||||
)
|
||||
self.assertFalse(write_data["error"])
|
||||
self.assertEqual(write_data["data"]["path"], test_path)
|
||||
|
||||
@@ -130,7 +133,9 @@ class FileSystemTests(RealHTTPMixin, LiveServerTestCase):
|
||||
from urllib.error import HTTPError
|
||||
|
||||
try:
|
||||
data = self._call("write_file", {"path": "/tmp/escape.txt", "content": "nope"})
|
||||
data = self._call(
|
||||
"write_file", {"path": "/tmp/escape.txt", "content": "nope"}
|
||||
)
|
||||
# If we get here, check the response has an error
|
||||
self.assertTrue(data["error"])
|
||||
self.assertEqual(data["code"], "FORBIDDEN")
|
||||
|
||||
Reference in New Issue
Block a user