Skip to content
This repository was archived by the owner on Feb 14, 2025. It is now read-only.

Commit 63903b2

Browse files
committed
feat: add a 'publicodes dev' command
1 parent 2ad8c4f commit 63903b2

18 files changed

+1356
-51
lines changed

package.json

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
},
1010
"files": [
1111
"dist",
12-
"bin"
12+
"bin",
13+
"quick-doc"
1314
],
1415
"exports": {
1516
".": {
@@ -62,18 +63,28 @@
6263
"dependencies": {
6364
"@clack/prompts": "^0.7.0",
6465
"@oclif/core": "^4.0.23",
66+
"@publicodes/react-ui": "^1.5.4",
67+
"@tailwindcss/typography": "^0.5.16",
68+
"@tailwindcss/vite": "^4.0.0",
6569
"@types/node": "^18.11.18",
6670
"chalk": "^5.3.0",
71+
"chokidar": "^4.0.3",
6772
"glob": "^10.4.1",
6873
"path": "^0.12.7",
6974
"publicodes": "^1.6.1",
70-
"yaml": "^2.4.5"
75+
"react": "^19.0.0",
76+
"react-dom": "^19.0.0",
77+
"react-router-dom": "^7.1.3",
78+
"tailwindcss": "^4.0.0",
79+
"vite": "^6.0.11",
80+
"yaml": "^2.7.0"
7181
},
7282
"devDependencies": {
7383
"@oclif/test": "^4.0.9",
7484
"@types/jest": "^29.5.13",
85+
"@types/react": "^19.0.8",
7586
"docdash": "^2.0.1",
76-
"prettier": "^3.0.0",
87+
"prettier": "^3.4.2",
7788
"ts-node": "^10.9.2",
7889
"tsup": "^8.0.2",
7990
"typedoc": "^0.24.8",
@@ -106,5 +117,6 @@
106117
},
107118
"publishConfig": {
108119
"access": "public"
109-
}
120+
},
121+
"packageManager": "[email protected]+sha1.4ba7fc5c6e704fce2066ecbfb0b0d8976fe62447"
110122
}

quick-doc/README.md

Whitespace-only changes.

quick-doc/index.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<title>⚡️ QuickDoc - Publicodes</title>
7+
<!-- <link rel="stylesheet" href="./src/index.css" /> -->
8+
</head>
9+
<body>
10+
<div id="root"></div>
11+
12+
<script type="module" src="/src/index.tsx"></script>
13+
<link href="/src/app.css" rel="stylesheet" />
14+
</body>
15+
</html>

quick-doc/src/app.css

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
@import 'tailwindcss';
2+
3+
@layer base {
4+
h1 {
5+
@apply text-4xl font-bold mt-8 mb-6 text-[#10162F];
6+
}
7+
8+
h2 {
9+
@apply text-3xl font-semibold mt-6 mb-5 text-[#10162F];
10+
}
11+
12+
h3 {
13+
@apply text-2xl font-semibold mt-5 mb-4 text-[#10162F];
14+
}
15+
16+
p {
17+
@apply text-base leading-relaxed mb-4 text-[#4B5563];
18+
}
19+
20+
ul {
21+
@apply list-disc list-inside mb-4 space-y-2;
22+
}
23+
24+
ol {
25+
@apply list-decimal list-inside mb-4 space-y-2;
26+
}
27+
28+
li {
29+
@apply text-[#4B5563] leading-relaxed;
30+
}
31+
32+
a {
33+
@apply text-[#2975d1] hover:text-[#1a365d] transition-colors underline-offset-2 hover:underline;
34+
}
35+
36+
blockquote {
37+
@apply pl-4 border-l-4 border-[#E5E7EB] italic my-4;
38+
}
39+
40+
code {
41+
@apply bg-[#F3F4F6] px-2 py-1 rounded text-sm text-[#10162F];
42+
}
43+
44+
pre {
45+
@apply bg-[#F3F4F6] p-4 rounded-lg mb-4 overflow-x-auto;
46+
}
47+
button {
48+
@apply bg-slate-100 px-2 py-1 rounded-md hover:bg-slate-50 transition-colors cursor-pointer disabled:opacity-50 text-sm disabled:cursor-not-allowed border border-slate-600 text-slate-800 ml-2;
49+
}
50+
}

quick-doc/src/components/App.tsx

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {
2+
BrowserRouter,
3+
Route,
4+
Routes,
5+
Link,
6+
useParams,
7+
Navigate,
8+
} from 'react-router-dom'
9+
import { RulePage } from '@publicodes/react-ui'
10+
import { engine, onEngineUpdate } from '../engine'
11+
import { RulesIndex } from './RulesIndex'
12+
import { useEffect, useState } from 'react'
13+
import Header from './Header'
14+
import { onSituationUpdate, situations } from '../situations'
15+
import { sitemap } from '../sitemap'
16+
17+
function RulePageWrapper() {
18+
let { '*': splat } = useParams()
19+
return (
20+
<RulePage
21+
engine={engine}
22+
documentationPath=""
23+
rulePath={splat}
24+
searchBar={true}
25+
showDevSection={true}
26+
language="fr"
27+
renderers={{ Link: Link }}
28+
/>
29+
)
30+
}
31+
32+
export default function App() {
33+
const [, forceUpdate] = useState({})
34+
useEffect(() => {
35+
// Subscribe to engine updates
36+
return onEngineUpdate(() => forceUpdate({}))
37+
}, [])
38+
const [activeSituation, setActiveSituation] = useState('')
39+
40+
useEffect(() => {
41+
return onSituationUpdate(() => {
42+
// forceUpdate({})
43+
console.log('situation updated', activeSituation)
44+
engine.setSituation(situations[activeSituation] ?? {})
45+
setActiveSituation(activeSituation in situations ? activeSituation : '')
46+
forceUpdate({})
47+
})
48+
}, [activeSituation])
49+
50+
function handleSituationChange(situation: string) {
51+
setActiveSituation(situation)
52+
engine.setSituation(situations[situation] ?? {})
53+
}
54+
55+
return (
56+
<>
57+
<BrowserRouter>
58+
<Header
59+
setSituation={handleSituationChange}
60+
activeSituation={activeSituation}
61+
/>
62+
<div className="container mx-auto">
63+
<Routes>
64+
<Route
65+
path="/"
66+
element={<Navigate to={Object.keys(sitemap)[0]} replace />}
67+
/>
68+
<Route path="/*" element={<RulePageWrapper />} />
69+
</Routes>
70+
</div>
71+
</BrowserRouter>
72+
</>
73+
)
74+
}

quick-doc/src/components/Header.tsx

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { Link } from 'react-router-dom'
2+
import { situations } from '../situations'
3+
4+
export default function Header({
5+
setSituation,
6+
activeSituation,
7+
}: {
8+
setSituation: (situation: string) => void
9+
activeSituation: string
10+
}) {
11+
return (
12+
<header className=" container mx-auto">
13+
<div className="flex items-center justify-between">
14+
<h1>
15+
<Link to="/" className="text-xl font-bold">
16+
⚡ Quick-doc
17+
</Link>
18+
</h1>
19+
20+
<nav className="w-full md:w-auto">
21+
<div className="flex flex-col gap-2">
22+
<label
23+
htmlFor="situation-select"
24+
className="text-sm font-medium text-gray-700"
25+
>
26+
Selectionner une situation
27+
</label>
28+
<select
29+
id="situation-select"
30+
value={activeSituation}
31+
onChange={(e) => setSituation(e.target.value)}
32+
className="block w-full md:w-64 px-3 py-2 bg-white border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
33+
>
34+
<option value=""></option>
35+
{Object.keys(situations).map((situationName) => (
36+
<option key={situationName} value={situationName}>
37+
{situationName}
38+
</option>
39+
))}
40+
</select>
41+
</div>
42+
</nav>
43+
</div>
44+
</header>
45+
)
46+
}

quick-doc/src/components/QuickSearch.tsx

Whitespace-only changes.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { Link } from 'react-router-dom'
2+
import { sitemap } from '../sitemap'
3+
4+
export function RulesIndex() {
5+
return (
6+
<div>
7+
<h1>Index des règles</h1>
8+
<ul>
9+
{Object.entries(sitemap).map(([path, name]) => (
10+
<li key={path}>
11+
<Link to={path}>{name}</Link>
12+
</li>
13+
))}
14+
</ul>
15+
</div>
16+
)
17+
}

quick-doc/src/engine.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import Engine from 'publicodes'
2+
3+
// Create a custom event for engine updates
4+
const ENGINE_UPDATED = 'engine-updated'
5+
const engineUpdate = new EventTarget()
6+
7+
export let engine = new Engine(__INJECTED_RULES__)
8+
9+
// Helper to subscribe to engine updates
10+
export function onEngineUpdate(callback: () => void) {
11+
engineUpdate.addEventListener(ENGINE_UPDATED, callback)
12+
return () => engineUpdate.removeEventListener(ENGINE_UPDATED, callback)
13+
}
14+
15+
if (import.meta.hot) {
16+
import.meta.hot.on('rules-updated', (newRules) => {
17+
const previousEngine = engine
18+
engine = new Engine(newRules)
19+
engine.setSituation(previousEngine.getSituation())
20+
// Dispatch event to notify subscribers
21+
engineUpdate.dispatchEvent(new Event(ENGINE_UPDATED))
22+
})
23+
}

quick-doc/src/index.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import React from 'react'
2+
import ReactDOM from 'react-dom/client'
3+
import App from './components/App.js'
4+
5+
ReactDOM.createRoot(document.getElementById('root')!).render(
6+
<React.StrictMode>
7+
<App />
8+
</React.StrictMode>,
9+
)

quick-doc/src/sitemap.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { getDocumentationSiteMap } from '@publicodes/react-ui'
2+
import { engine } from './engine'
3+
export const sitemap = getDocumentationSiteMap({
4+
documentationPath: '',
5+
engine,
6+
})

quick-doc/src/situations.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
export let situations = __INJECTED_SITUATIONS__
2+
const situationUpdate = new EventTarget()
3+
const SITUATION_UPDATED = 'situations-updated'
4+
5+
export function onSituationUpdate(callback: () => void) {
6+
situationUpdate.addEventListener(SITUATION_UPDATED, callback)
7+
return () => situationUpdate.removeEventListener(SITUATION_UPDATED, callback)
8+
}
9+
10+
if (import.meta.hot) {
11+
import.meta.hot.on('situations-updated', (newSituations) => {
12+
situations = newSituations
13+
situationUpdate.dispatchEvent(new Event(SITUATION_UPDATED))
14+
})
15+
}

quick-doc/tsconfig.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"compilerOptions": {
3+
"target": "ES2020",
4+
"useDefineForClassFields": true,
5+
"lib": ["ES2020", "DOM", "DOM.Iterable"],
6+
"module": "ESNext",
7+
"skipLibCheck": true,
8+
9+
/* Bundler mode */
10+
"moduleResolution": "bundler",
11+
"allowImportingTsExtensions": true,
12+
"resolveJsonModule": true,
13+
"isolatedModules": true,
14+
"noEmit": true,
15+
"jsx": "react-jsx",
16+
17+
/* Linting */
18+
"strict": true,
19+
"noUnusedLocals": true,
20+
"noUnusedParameters": true,
21+
"noFallthroughCasesInSwitch": true
22+
},
23+
"include": ["src"]
24+
}

0 commit comments

Comments
 (0)