Skip to content

Commit b4f26dd

Browse files
committed
feat: PIML Lab.
1 parent 5bbcb52 commit b4f26dd

File tree

4 files changed

+299
-19
lines changed

4 files changed

+299
-19
lines changed

public/apps/apps.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,14 @@
536536
"icon": "ToolboxIcon",
537537
"order": 5,
538538
"apps": [
539+
{
540+
"slug": "piml-lab",
541+
"to": "/apps/piml-lab",
542+
"title": "PIML Lab",
543+
"description": "Real-time technical markup visualization protocol. Write custom PIML and see it rendered instantly.",
544+
"icon": "CodeIcon",
545+
"created_at": "2025-12-21T19:00:00+03:00"
546+
},
539547
{
540548
"slug": "notepad",
541549
"to": "/apps/notepad",

src/components/AnimatedRoutes.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ const AchievementsPage = lazy(() => import('../pages/AchievementsPage'));
163163
const SitemapPage = lazy(() => import('../pages/SitemapPage'));
164164
const WelcomePage = lazy(() => import('../pages/WelcomePage'));
165165
const KnowledgeGraphPage = lazy(() => import('../pages/KnowledgeGraphPage'));
166+
const PIMLLabPage = lazy(() => import('../pages/apps/PIMLLabPage'));
166167
const VocabRouteHandler = lazy(() => import('../components/VocabRouteHandler'));
167168

168169
const pageVariants = {
@@ -1031,6 +1032,22 @@ const AnimatedRoutes = ({
10311032
</motion.div>
10321033
}
10331034
/>
1035+
<Route
1036+
path="/apps/piml-lab"
1037+
element={
1038+
<motion.div
1039+
initial="initial"
1040+
animate="in"
1041+
exit="out"
1042+
variants={pageVariants}
1043+
transition={pageTransition}
1044+
>
1045+
<Suspense fallback={<Loading />}>
1046+
<PIMLLabPage />
1047+
</Suspense>
1048+
</motion.div>
1049+
}
1050+
/>
10341051
<Route
10351052
path="/apps/connect-four"
10361053
element={

src/components/roadmap/RoadmapCard.js

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@ import React from 'react';
22
import { Link } from 'react-router-dom';
33
import { motion } from 'framer-motion';
44
import {
5-
Lightning,
6-
Circle,
7-
ArrowsClockwise,
8-
CheckCircle,
9-
PauseCircle,
10-
Fire,
11-
Equals,
12-
ArrowDown,
5+
LightningIcon,
6+
CircleIcon,
7+
ArrowsClockwiseIcon,
8+
CheckCircleIcon,
9+
PauseCircleIcon,
10+
FireIcon,
11+
EqualsIcon,
12+
ArrowDownIcon,
1313
} from '@phosphor-icons/react';
1414
import {
1515
getStatusClasses,
@@ -20,36 +20,36 @@ const RoadmapCard = ({ app, index }) => {
2020
const getStatusIcon = (status) => {
2121
switch (status) {
2222
case 'Planned':
23-
return <Circle weight="bold" />;
23+
return <CircleIcon weight="bold" />;
2424
case 'In Progress':
25-
return <ArrowsClockwise weight="bold" className="animate-spin" />;
25+
return <ArrowsClockwiseIcon weight="bold" className="animate-spin" />;
2626
case 'Completed':
27-
return <CheckCircle weight="bold" />;
27+
return <CheckCircleIcon weight="bold" />;
2828
case 'On Hold':
29-
return <PauseCircle weight="bold" />;
29+
return <PauseCircleIcon weight="bold" />;
3030
default:
31-
return <Circle weight="bold" />;
31+
return <CircleIcon weight="bold" />;
3232
}
3333
};
3434

3535
const getPriorityIcon = (priority) => {
3636
switch (priority) {
3737
case 'High':
38-
return <Fire weight="fill" />;
38+
return <FireIcon weight="fill" />;
3939
case 'Medium':
40-
return <Equals weight="bold" />;
40+
return <EqualsIcon weight="bold" />;
4141
case 'Low':
42-
return <ArrowDown weight="bold" />;
42+
return <ArrowDownIcon weight="bold" />;
4343
default:
44-
return <ArrowDown weight="bold" />;
44+
return <ArrowDownIcon weight="bold" />;
4545
}
4646
};
4747

4848
return (
4949
<motion.div
5050
initial={{ opacity: 0, y: 20 }}
5151
animate={{ opacity: 1, y: 0 }}
52-
transition={{ duration: 0.3, delay: index * 0.05 }}
52+
transition={{ duration: 0.3, delay: (index || 0) * 0.05 }}
5353
>
5454
<Link to={`/roadmap/${app.id}`} className="block group relative h-full">
5555
{/* Main Card Container */}
@@ -83,7 +83,7 @@ const RoadmapCard = ({ app, index }) => {
8383

8484
{app.epic && (
8585
<div className="inline-flex items-center gap-1.5 px-2 py-0.5 rounded-sm bg-purple-500/5 border border-purple-500/20 text-purple-400 text-[9px] font-mono uppercase tracking-[0.2em] font-black">
86-
<Lightning weight="fill" size={10} />
86+
<LightningIcon weight="fill" size={10} />
8787
{app.epic}
8888
</div>
8989
)}

src/pages/apps/PIMLLabPage.js

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
import React, { useState, useEffect } from 'react';
2+
import { Link } from 'react-router-dom';
3+
import { motion, AnimatePresence } from 'framer-motion';
4+
import piml from 'piml';
5+
import {
6+
ArrowLeftIcon,
7+
CodeIcon,
8+
EyeIcon,
9+
CopySimpleIcon,
10+
XCircleIcon,
11+
PlayIcon,
12+
FileTextIcon,
13+
CubeIcon,
14+
ArticleIcon,
15+
KanbanIcon,
16+
} from '@phosphor-icons/react';
17+
import { useToast } from '../../hooks/useToast';
18+
import useSeo from '../../hooks/useSeo';
19+
import GenerativeArt from '../../components/GenerativeArt';
20+
import ProjectCard from '../../components/ProjectCard';
21+
import LogCard from '../../components/LogCard';
22+
import PostTile from '../../components/PostTile';
23+
import RoadmapCard from '../../components/roadmap/RoadmapCard';
24+
25+
const TEMPLATES = {
26+
project: `(project)
27+
(id) fez-99
28+
(slug) my-cool-project
29+
(title) Quantum Neural Interface
30+
(description) A next-generation interface for neural data processing.
31+
(status) Active
32+
(technologies) React, WebGL, Rust`,
33+
log: `(log)
34+
(id) log-42
35+
(slug) the-great-read
36+
(title) The Design of Everyday Things
37+
(category) Book
38+
(by) Don Norman
39+
(date) 2025-12-21
40+
(rating) 5
41+
(platform) Physical`,
42+
post: `(post)
43+
(slug) live-from-piml-lab
44+
(title) Live Data Stream Protocol
45+
(date) 2025-12-21
46+
(description) Testing the new PIML rendering engine in real-time.
47+
(category) dev
48+
(tags) PIML, React, Experimental`,
49+
roadmap: `(issue)
50+
(id) FEZ-999
51+
(title) Full System Integration
52+
(description) Completing the neural bridge between PIML and React.
53+
(status) Planned
54+
(priority) High
55+
(created_at) 2025-12-21T18:00:00Z`
56+
};
57+
58+
const PIMLLabPage = () => {
59+
const appName = 'PIML Lab';
60+
const { addToast } = useToast();
61+
const [input, setInput] = useState(TEMPLATES.project);
62+
const [parsedData, setParsedData] = useState(null);
63+
const [error, setError] = useState(null);
64+
65+
useSeo({
66+
title: `${appName} | Fezcodex`,
67+
description: 'Real-time PIML playground. Write custom markup and see it rendered as Fezcodex components.',
68+
keywords: ['PIML', 'playground', 'editor', 'markup', 'react', 'fezcodex'],
69+
});
70+
71+
useEffect(() => {
72+
try {
73+
const result = piml.parse(input);
74+
setParsedData(result);
75+
setError(null);
76+
} catch (e) {
77+
setError(e.message);
78+
}
79+
}, [input]);
80+
81+
const loadTemplate = (type) => {
82+
setInput(TEMPLATES[type]);
83+
addToast({ title: 'Template Loaded', message: `Applied ${type.toUpperCase()} schema.` });
84+
};
85+
86+
const copyToClipboard = () => {
87+
navigator.clipboard.writeText(input).then(() => {
88+
addToast({ title: 'Copied', message: 'PIML sequence stored in memory bank.' });
89+
});
90+
};
91+
92+
const renderPreview = () => {
93+
if (!parsedData) return null;
94+
95+
// Handle different PIML structures based on templates
96+
if (parsedData.project) {
97+
return (
98+
<div className="max-w-2xl mx-auto py-12">
99+
<ProjectCard project={parsedData.project} index={0} isActive={true} />
100+
</div>
101+
);
102+
}
103+
104+
if (parsedData.log) {
105+
return (
106+
<div className="max-w-sm mx-auto py-12">
107+
<LogCard log={parsedData.log} index={0} totalLogs={1} />
108+
</div>
109+
);
110+
}
111+
112+
if (parsedData.post) {
113+
return (
114+
<div className="max-w-md mx-auto py-12">
115+
<PostTile post={parsedData.post} />
116+
</div>
117+
);
118+
}
119+
120+
if (parsedData.issue) {
121+
return (
122+
<div className="max-w-md mx-auto py-12">
123+
<RoadmapCard app={parsedData.issue} />
124+
</div>
125+
);
126+
}
127+
128+
return (
129+
<div className="p-8 border border-white/5 bg-white/[0.01] rounded-sm h-full flex flex-col">
130+
<span className="text-[10px] font-mono text-gray-600 uppercase mb-4 tracking-widest">Raw_JSON_Output</span>
131+
<pre className="text-emerald-500 font-mono text-xs overflow-auto h-full scrollbar-hide">
132+
{JSON.stringify(parsedData, null, 2)}
133+
</pre>
134+
</div>
135+
);
136+
};
137+
138+
return (
139+
<div className="min-h-screen bg-[#050505] text-white selection:bg-emerald-500/30 font-sans flex flex-col">
140+
<div className="mx-auto max-w-7xl w-full px-6 py-12 md:px-12 flex-grow flex flex-col">
141+
<header className="mb-12">
142+
<Link to="/apps" className="group mb-8 inline-flex items-center gap-2 text-xs font-mono text-gray-500 hover:text-white transition-colors uppercase tracking-[0.3em]">
143+
<ArrowLeftIcon weight="bold" className="transition-transform group-hover:-translate-x-1" />
144+
<span>Applications</span>
145+
</Link>
146+
147+
<div className="flex flex-col md:flex-row md:items-end justify-between gap-8">
148+
<div>
149+
<h1 className="text-5xl md:text-7xl font-black tracking-tighter text-white leading-none uppercase flex items-center gap-4 flex-wrap">
150+
{appName} <span className="text-[10px] border border-emerald-500/50 text-emerald-500 px-2 py-1 rounded-sm shrink-0 whitespace-nowrap tracking-normal">Experimental</span>
151+
</h1>
152+
<p className="text-gray-400 font-mono text-xs uppercase tracking-widest mt-4">
153+
Real-time technical markup visualization protocol.
154+
</p>
155+
</div>
156+
157+
<div className="flex gap-2">
158+
<button onClick={() => loadTemplate('project')} className="flex items-center gap-2 px-3 py-2 border border-white/10 hover:bg-white hover:text-black text-[10px] font-bold uppercase transition-all">
159+
<CubeIcon /> Project
160+
</button>
161+
<button onClick={() => loadTemplate('log')} className="flex items-center gap-2 px-3 py-2 border border-white/10 hover:bg-white hover:text-black text-[10px] font-bold uppercase transition-all">
162+
<FileTextIcon /> Log
163+
</button>
164+
<button onClick={() => loadTemplate('post')} className="flex items-center gap-2 px-3 py-2 border border-white/10 hover:bg-white hover:text-black text-[10px] font-bold uppercase transition-all">
165+
<ArticleIcon /> Post
166+
</button>
167+
<button onClick={() => loadTemplate('roadmap')} className="flex items-center gap-2 px-3 py-2 border border-white/10 hover:bg-white hover:text-black text-[10px] font-bold uppercase transition-all">
168+
<KanbanIcon /> Issue
169+
</button>
170+
</div>
171+
</div>
172+
</header>
173+
174+
<div className="grid grid-cols-1 lg:grid-cols-2 gap-px bg-white/10 border border-white/10 flex-grow">
175+
{/* Editor Panel */}
176+
<div className="bg-[#050505] p-6 flex flex-col h-full min-h-[500px]">
177+
<div className="flex justify-between items-center mb-4">
178+
<h3 className="font-mono text-[10px] font-bold text-gray-500 uppercase tracking-widest flex items-center gap-2">
179+
<CodeIcon weight="fill" className="text-emerald-500" />
180+
Input_PIML_Sequence
181+
</h3>
182+
<button onClick={copyToClipboard} className="text-gray-600 hover:text-white transition-colors">
183+
<CopySimpleIcon size={18} />
184+
</button>
185+
</div>
186+
<textarea
187+
value={input}
188+
onChange={(e) => setInput(e.target.value)}
189+
className="flex-grow w-full bg-black/40 border border-white/5 p-6 font-mono text-sm text-gray-300 focus:border-emerald-500/50 focus:outline-none transition-all resize-none scrollbar-hide"
190+
spellCheck="false"
191+
/>
192+
</div>
193+
194+
{/* Preview Panel */}
195+
<div className="bg-[#050505] p-6 flex flex-col h-full relative group">
196+
<div className="absolute inset-0 opacity-[0.02] pointer-events-none grayscale group-hover:opacity-[0.04] transition-opacity">
197+
<GenerativeArt seed="PIML_LAB_VISUAL" className="w-full h-full" />
198+
</div>
199+
200+
<div className="flex justify-between items-center mb-4 relative z-10">
201+
<h3 className="font-mono text-[10px] font-bold text-gray-500 uppercase tracking-widest flex items-center gap-2">
202+
<EyeIcon weight="fill" className="text-emerald-500" />
203+
Live_Render_Buffer
204+
</h3>
205+
<div className="flex items-center gap-2">
206+
<div className={`w-1.5 h-1.5 rounded-full ${error ? 'bg-red-500' : 'bg-emerald-500'} animate-pulse`} />
207+
<span className="text-[9px] font-mono text-gray-600 uppercase">{error ? 'Sync_Error' : 'Realtime_Sync_Active'}</span>
208+
</div>
209+
</div>
210+
211+
<div className="flex-grow relative z-10 border border-dashed border-white/5 p-4 overflow-y-auto scrollbar-hide">
212+
<AnimatePresence mode="wait">
213+
{error ? (
214+
<motion.div
215+
initial={{ opacity: 0, y: 10 }}
216+
animate={{ opacity: 1, y: 0 }}
217+
exit={{ opacity: 0 }}
218+
className="p-6 border border-red-500/20 bg-red-500/5 rounded-sm"
219+
>
220+
<div className="flex items-center gap-3 text-red-500 mb-2">
221+
<XCircleIcon size={20} weight="bold" />
222+
<span className="text-xs font-black uppercase tracking-widest">Parsing_Failure</span>
223+
</div>
224+
<p className="text-[10px] font-mono text-red-400/80 leading-relaxed break-words uppercase">
225+
{error}
226+
</p>
227+
</motion.div>
228+
) : (
229+
<motion.div
230+
key={JSON.stringify(parsedData)}
231+
initial={{ opacity: 0, scale: 0.98 }}
232+
animate={{ opacity: 1, scale: 1 }}
233+
className="h-full"
234+
>
235+
{renderPreview()}
236+
</motion.div>
237+
)}
238+
</AnimatePresence>
239+
</div>
240+
</div>
241+
</div>
242+
243+
<footer className="mt-12 pt-6 border-t border-white/10 flex justify-between items-center text-gray-600 font-mono text-[9px] uppercase tracking-[0.3em]">
244+
<div className="flex items-center gap-4">
245+
<PlayIcon weight="fill" className="text-emerald-500" />
246+
<span>PIML_Visualization_Engine_Enabled</span>
247+
</div>
248+
<span>Buffer_Status: Stable</span>
249+
</footer>
250+
</div>
251+
</div>
252+
);
253+
};
254+
255+
export default PIMLLabPage;

0 commit comments

Comments
 (0)