@fjell/core 4.4.7 → 4.4.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +227 -0
- package/dist/cjs/item/IUtils.js.map +1 -1
- package/dist/esm/item/IUtils.js.map +1 -1
- package/dist/index.cjs.map +1 -1
- package/docs/README.md +53 -0
- package/docs/index.html +18 -0
- package/docs/package.json +35 -0
- package/docs/public/README.md +227 -0
- package/docs/public/api.md +230 -0
- package/docs/public/basic-usage.ts +293 -0
- package/docs/public/examples-README.md +147 -0
- package/docs/public/fjell-icon.svg +1 -0
- package/docs/public/package.json +56 -0
- package/docs/public/pano.png +0 -0
- package/docs/src/App.css +1178 -0
- package/docs/src/App.tsx +684 -0
- package/docs/src/index.css +38 -0
- package/docs/src/main.tsx +10 -0
- package/docs/tsconfig.node.json +14 -0
- package/docs/vitest.config.ts +14 -0
- package/examples/README.md +147 -0
- package/examples/basic-usage.ts +293 -0
- package/package.json +7 -6
- package/src/item/IUtils.ts +1 -1
package/docs/src/App.tsx
ADDED
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
|
+
import ReactMarkdown from 'react-markdown'
|
|
3
|
+
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'
|
|
4
|
+
import { oneLight } from 'react-syntax-highlighter/dist/esm/styles/prism'
|
|
5
|
+
import remarkGfm from 'remark-gfm'
|
|
6
|
+
import './App.css'
|
|
7
|
+
|
|
8
|
+
interface DocumentSection {
|
|
9
|
+
id: string;
|
|
10
|
+
title: string;
|
|
11
|
+
subtitle: string;
|
|
12
|
+
file: string;
|
|
13
|
+
content?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const documentSections: DocumentSection[] = [
|
|
17
|
+
{ id: 'overview', title: 'Foundation', subtitle: 'Core concepts & philosophy', file: '/core/README.md' },
|
|
18
|
+
{ id: 'getting-started', title: 'Getting Started', subtitle: 'Your first steps with Fjell Core', file: '/core/README.md' },
|
|
19
|
+
{ id: 'examples', title: 'Examples', subtitle: 'Code examples & usage patterns', file: '/core/examples-README.md' },
|
|
20
|
+
{ id: 'api', title: 'API Reference', subtitle: 'Complete API documentation', file: '/core/api.md' }
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const App: React.FC = () => {
|
|
24
|
+
const [currentSection, setCurrentSection] = useState('overview')
|
|
25
|
+
const [documents, setDocuments] = useState<{ [key: string]: string }>({})
|
|
26
|
+
const [loading, setLoading] = useState(true)
|
|
27
|
+
const [sidebarOpen, setSidebarOpen] = useState(false)
|
|
28
|
+
const [fullscreenImage, setFullscreenImage] = useState<string | null>(null)
|
|
29
|
+
const [version, setVersion] = useState<string>('4.4.7')
|
|
30
|
+
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
const loadDocuments = async () => {
|
|
33
|
+
const loadedDocs: { [key: string]: string } = {}
|
|
34
|
+
|
|
35
|
+
for (const section of documentSections) {
|
|
36
|
+
try {
|
|
37
|
+
const response = await fetch(section.file)
|
|
38
|
+
|
|
39
|
+
if (response.ok) {
|
|
40
|
+
loadedDocs[section.id] = await response.text()
|
|
41
|
+
} else {
|
|
42
|
+
// Fallback content for missing files
|
|
43
|
+
loadedDocs[section.id] = getFallbackContent(section.id)
|
|
44
|
+
}
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.error(`Error loading ${section.file}:`, error)
|
|
47
|
+
loadedDocs[section.id] = getFallbackContent(section.id)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setDocuments(loadedDocs)
|
|
52
|
+
setLoading(false)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const loadVersion = async () => {
|
|
56
|
+
try {
|
|
57
|
+
const response = await fetch('/core/package.json')
|
|
58
|
+
if (response.ok) {
|
|
59
|
+
const packageData = await response.json()
|
|
60
|
+
setVersion(packageData.version)
|
|
61
|
+
} else {
|
|
62
|
+
setVersion('4.4.7') // Fallback version
|
|
63
|
+
}
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error loading version:', error)
|
|
66
|
+
setVersion('4.4.7') // Fallback version
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
loadDocuments()
|
|
71
|
+
loadVersion()
|
|
72
|
+
}, [])
|
|
73
|
+
|
|
74
|
+
const getFallbackContent = (sectionId: string): string => {
|
|
75
|
+
switch (sectionId) {
|
|
76
|
+
case 'overview':
|
|
77
|
+
return `# Fjell Core
|
|
78
|
+
|
|
79
|
+
Core Item and Key Framework for Fjell - The foundational library that provides
|
|
80
|
+
essential item management, key utilities, and service coordination for the entire
|
|
81
|
+
Fjell ecosystem.
|
|
82
|
+
|
|
83
|
+
## Installation
|
|
84
|
+
|
|
85
|
+
\`\`\`bash
|
|
86
|
+
npm install @fjell/core
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
## Quick Start
|
|
90
|
+
|
|
91
|
+
\`\`\`typescript
|
|
92
|
+
import { IFactory, KUtils, AItemService } from '@fjell/core'
|
|
93
|
+
|
|
94
|
+
// Create items with the factory
|
|
95
|
+
const item = IFactory.create('user', { id: '123', name: 'John' })
|
|
96
|
+
|
|
97
|
+
// Use key utilities for key management
|
|
98
|
+
const key = KUtils.generateKey(['user', '123'])
|
|
99
|
+
|
|
100
|
+
// Service coordination
|
|
101
|
+
const service = new AItemService()
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
## Core Features
|
|
105
|
+
|
|
106
|
+
### Item Factory (IFactory)
|
|
107
|
+
- **Type-safe item creation**: Create strongly-typed items with validation
|
|
108
|
+
- **Flexible configuration**: Support for various item types and schemas
|
|
109
|
+
- **Integration ready**: Works seamlessly with other Fjell libraries
|
|
110
|
+
|
|
111
|
+
### Key Utilities (KUtils)
|
|
112
|
+
- **Key generation**: Create hierarchical keys for item identification
|
|
113
|
+
- **Key parsing**: Extract components from complex key structures
|
|
114
|
+
- **Key validation**: Ensure key integrity across the system
|
|
115
|
+
|
|
116
|
+
### Service Layer (AItemService)
|
|
117
|
+
- **Abstract service base**: Foundation for building domain services
|
|
118
|
+
- **Lifecycle management**: Handle service initialization and cleanup
|
|
119
|
+
- **Event coordination**: Built-in event handling capabilities
|
|
120
|
+
|
|
121
|
+
### Query Support (IQFactory, IQUtils)
|
|
122
|
+
- **Query building**: Construct complex queries for item retrieval
|
|
123
|
+
- **Query optimization**: Efficient query execution strategies
|
|
124
|
+
- **Result processing**: Transform and filter query results
|
|
125
|
+
|
|
126
|
+
## Architecture Philosophy
|
|
127
|
+
|
|
128
|
+
Fjell Core is designed around the principle of **progressive enhancement**:
|
|
129
|
+
|
|
130
|
+
1. **Start Simple**: Basic item and key operations require minimal setup
|
|
131
|
+
2. **Scale Gradually**: Add complexity only when needed
|
|
132
|
+
3. **Maintain Consistency**: Common patterns across all Fjell libraries
|
|
133
|
+
4. **Enable Integration**: Designed to work with the entire Fjell ecosystem
|
|
134
|
+
|
|
135
|
+
## Integration with Fjell Ecosystem
|
|
136
|
+
|
|
137
|
+
Fjell Core serves as the foundation for:
|
|
138
|
+
|
|
139
|
+
- **@fjell/registry**: Service location and dependency injection
|
|
140
|
+
- **@fjell/cache**: High-performance caching solutions
|
|
141
|
+
- **@fjell/lib**: Database abstraction layers
|
|
142
|
+
- **@fjell/providers**: React component state management
|
|
143
|
+
- **@fjell/client-api**: HTTP client utilities
|
|
144
|
+
|
|
145
|
+
## Type Safety
|
|
146
|
+
|
|
147
|
+
Built with TypeScript-first design:
|
|
148
|
+
|
|
149
|
+
\`\`\`typescript
|
|
150
|
+
// Strongly typed item creation
|
|
151
|
+
interface UserItem {
|
|
152
|
+
id: string
|
|
153
|
+
name: string
|
|
154
|
+
email: string
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const user = IFactory.create<UserItem>('user', {
|
|
158
|
+
id: '123',
|
|
159
|
+
name: 'John Doe',
|
|
160
|
+
email: 'john@example.com'
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// Type-safe key operations
|
|
164
|
+
const userKey = KUtils.createKey(['users', user.id])
|
|
165
|
+
const [collection, id] = KUtils.parseKey(userKey)
|
|
166
|
+
\`\`\`
|
|
167
|
+
|
|
168
|
+
## Performance Characteristics
|
|
169
|
+
|
|
170
|
+
- **Minimal Runtime Overhead**: Core operations are highly optimized
|
|
171
|
+
- **Memory Efficient**: Smart object pooling and reuse strategies
|
|
172
|
+
- **Lazy Loading**: Components load only when needed
|
|
173
|
+
- **Tree Shaking**: Only bundle what you use
|
|
174
|
+
|
|
175
|
+
## Testing Support
|
|
176
|
+
|
|
177
|
+
Comprehensive testing utilities:
|
|
178
|
+
|
|
179
|
+
\`\`\`typescript
|
|
180
|
+
import { TestUtils } from '@fjell/core/testing'
|
|
181
|
+
|
|
182
|
+
// Mock factories for testing
|
|
183
|
+
const mockFactory = TestUtils.createMockFactory()
|
|
184
|
+
const testItem = mockFactory.create('test', { id: 'test-123' })
|
|
185
|
+
|
|
186
|
+
// Assertion helpers
|
|
187
|
+
expect(TestUtils.isValidKey(key)).toBe(true)
|
|
188
|
+
expect(TestUtils.getKeyComponents(key)).toEqual(['users', '123'])
|
|
189
|
+
\`\`\`
|
|
190
|
+
|
|
191
|
+
## Error Handling
|
|
192
|
+
|
|
193
|
+
Robust error handling with descriptive messages:
|
|
194
|
+
|
|
195
|
+
\`\`\`typescript
|
|
196
|
+
try {
|
|
197
|
+
const item = IFactory.create('invalid-type', data)
|
|
198
|
+
} catch (error) {
|
|
199
|
+
if (error instanceof CoreValidationError) {
|
|
200
|
+
console.log('Validation failed:', error.details)
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
\`\`\`
|
|
204
|
+
|
|
205
|
+
## Getting Help
|
|
206
|
+
|
|
207
|
+
- **GitHub Issues**: Report bugs and request features
|
|
208
|
+
- **Documentation**: Comprehensive guides and API reference
|
|
209
|
+
- **Examples**: Real-world usage patterns and best practices
|
|
210
|
+
- **Community**: Join discussions and get support
|
|
211
|
+
|
|
212
|
+
Start building with Fjell Core today and experience the power of a well-designed
|
|
213
|
+
foundational framework that grows with your application needs.`
|
|
214
|
+
|
|
215
|
+
case 'getting-started':
|
|
216
|
+
return `# Getting Started with Fjell Core
|
|
217
|
+
|
|
218
|
+
Welcome to Fjell Core! This guide will help you get up and running quickly.
|
|
219
|
+
|
|
220
|
+
## Installation
|
|
221
|
+
|
|
222
|
+
\`\`\`bash
|
|
223
|
+
# Using npm
|
|
224
|
+
npm install @fjell/core
|
|
225
|
+
|
|
226
|
+
# Using yarn
|
|
227
|
+
yarn add @fjell/core
|
|
228
|
+
|
|
229
|
+
# Using pnpm
|
|
230
|
+
pnpm add @fjell/core
|
|
231
|
+
\`\`\`
|
|
232
|
+
|
|
233
|
+
## Basic Usage
|
|
234
|
+
|
|
235
|
+
### 1. Item Creation with IFactory
|
|
236
|
+
|
|
237
|
+
\`\`\`typescript
|
|
238
|
+
import { IFactory } from '@fjell/core'
|
|
239
|
+
|
|
240
|
+
// Define your item type
|
|
241
|
+
interface User {
|
|
242
|
+
id: string
|
|
243
|
+
name: string
|
|
244
|
+
email: string
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// Create an item
|
|
248
|
+
const user = IFactory.create<User>('user', {
|
|
249
|
+
id: 'user-123',
|
|
250
|
+
name: 'John Doe',
|
|
251
|
+
email: 'john.doe@example.com'
|
|
252
|
+
})
|
|
253
|
+
|
|
254
|
+
console.log(user) // Strongly typed User item
|
|
255
|
+
\`\`\`
|
|
256
|
+
|
|
257
|
+
### 2. Key Management with KUtils
|
|
258
|
+
|
|
259
|
+
\`\`\`typescript
|
|
260
|
+
import { KUtils } from '@fjell/core'
|
|
261
|
+
|
|
262
|
+
// Generate hierarchical keys
|
|
263
|
+
const userKey = KUtils.generateKey(['users', 'user-123'])
|
|
264
|
+
const profileKey = KUtils.generateKey(['users', 'user-123', 'profile'])
|
|
265
|
+
|
|
266
|
+
// Parse keys back to components
|
|
267
|
+
const components = KUtils.parseKey(userKey)
|
|
268
|
+
console.log(components) // ['users', 'user-123']
|
|
269
|
+
|
|
270
|
+
// Validate keys
|
|
271
|
+
const isValid = KUtils.isValidKey(userKey)
|
|
272
|
+
console.log(isValid) // true
|
|
273
|
+
\`\`\`
|
|
274
|
+
|
|
275
|
+
### 3. Service Layer with AItemService
|
|
276
|
+
|
|
277
|
+
\`\`\`typescript
|
|
278
|
+
import { AItemService } from '@fjell/core'
|
|
279
|
+
|
|
280
|
+
class UserService extends AItemService {
|
|
281
|
+
async createUser(userData: Partial<User>): Promise<User> {
|
|
282
|
+
// Your business logic here
|
|
283
|
+
const user = IFactory.create<User>('user', {
|
|
284
|
+
id: this.generateId(),
|
|
285
|
+
...userData
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
// Use the service's built-in methods
|
|
289
|
+
await this.validate(user)
|
|
290
|
+
return this.save(user)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private generateId(): string {
|
|
294
|
+
return \`user-\${Date.now()}-\${Math.random().toString(36).substr(2, 9)}\`;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
// Use the service
|
|
299
|
+
const userService = new UserService()
|
|
300
|
+
const newUser = await userService.createUser({
|
|
301
|
+
name: 'Jane Smith',
|
|
302
|
+
email: 'jane@example.com'
|
|
303
|
+
})
|
|
304
|
+
\`\`\`
|
|
305
|
+
|
|
306
|
+
### 4. Query Building with IQFactory
|
|
307
|
+
|
|
308
|
+
\`\`\`typescript
|
|
309
|
+
import { IQFactory, IQUtils } from '@fjell/core'
|
|
310
|
+
|
|
311
|
+
// Build queries for item retrieval
|
|
312
|
+
const query = IQFactory.create('user')
|
|
313
|
+
.where('status', 'active')
|
|
314
|
+
.where('role', 'admin')
|
|
315
|
+
.limit(10)
|
|
316
|
+
.orderBy('createdAt', 'desc')
|
|
317
|
+
|
|
318
|
+
// Execute queries (integration with your data layer)
|
|
319
|
+
const results = await IQUtils.execute(query)
|
|
320
|
+
console.log(results) // Array of matching User items
|
|
321
|
+
\`\`\`
|
|
322
|
+
|
|
323
|
+
## Next Steps
|
|
324
|
+
|
|
325
|
+
1. **Explore Examples**: Check out our comprehensive examples
|
|
326
|
+
2. **API Reference**: Review the complete API documentation
|
|
327
|
+
3. **Integration**: Learn how to integrate with other Fjell libraries
|
|
328
|
+
4. **Testing**: Set up testing with Fjell Core utilities
|
|
329
|
+
|
|
330
|
+
Ready to build something amazing with Fjell Core!`
|
|
331
|
+
|
|
332
|
+
case 'examples':
|
|
333
|
+
return `# Fjell Core Examples
|
|
334
|
+
|
|
335
|
+
Examples are loading from the examples directory...
|
|
336
|
+
|
|
337
|
+
If you're seeing this message, there may be an issue loading the examples content.
|
|
338
|
+
Please check the console for any errors.`
|
|
339
|
+
|
|
340
|
+
case 'api':
|
|
341
|
+
return `# Fjell Core API Reference
|
|
342
|
+
|
|
343
|
+
Complete API documentation for all Fjell Core modules.
|
|
344
|
+
|
|
345
|
+
## IFactory
|
|
346
|
+
|
|
347
|
+
Factory for creating strongly-typed items.
|
|
348
|
+
|
|
349
|
+
### Methods
|
|
350
|
+
|
|
351
|
+
#### \`create<T>(type: string, data: Partial<T>): T\`
|
|
352
|
+
Creates a new item of the specified type.
|
|
353
|
+
|
|
354
|
+
**Parameters:**
|
|
355
|
+
- \`type\`: String identifier for the item type
|
|
356
|
+
- \`data\`: Partial item data to initialize the item
|
|
357
|
+
|
|
358
|
+
**Returns:** Fully constructed item of type T
|
|
359
|
+
|
|
360
|
+
**Example:**
|
|
361
|
+
\`\`\`typescript
|
|
362
|
+
const user = IFactory.create<User>('user', {
|
|
363
|
+
id: 'user-123',
|
|
364
|
+
name: 'John Doe'
|
|
365
|
+
})
|
|
366
|
+
\`\`\`
|
|
367
|
+
|
|
368
|
+
## KUtils
|
|
369
|
+
|
|
370
|
+
Utilities for key generation and manipulation.
|
|
371
|
+
|
|
372
|
+
### Methods
|
|
373
|
+
|
|
374
|
+
#### \`generateKey(components: string[]): string\`
|
|
375
|
+
Generates a hierarchical key from components.
|
|
376
|
+
|
|
377
|
+
**Parameters:**
|
|
378
|
+
- \`components\`: Array of string components
|
|
379
|
+
|
|
380
|
+
**Returns:** Generated key string
|
|
381
|
+
|
|
382
|
+
**Example:**
|
|
383
|
+
\`\`\`typescript
|
|
384
|
+
const key = KUtils.generateKey(['users', 'user-123', 'profile'])
|
|
385
|
+
// Returns: "users:user-123:profile"
|
|
386
|
+
\`\`\`
|
|
387
|
+
|
|
388
|
+
#### \`parseKey(key: string): string[]\`
|
|
389
|
+
Parses a key back into its components.
|
|
390
|
+
|
|
391
|
+
#### \`isValidKey(key: string): boolean\`
|
|
392
|
+
Validates whether a key is properly formatted.
|
|
393
|
+
|
|
394
|
+
## AItemService
|
|
395
|
+
|
|
396
|
+
Abstract base class for building domain services.
|
|
397
|
+
|
|
398
|
+
### Methods
|
|
399
|
+
|
|
400
|
+
#### \`validate<T>(item: T): Promise<void>\`
|
|
401
|
+
Validates an item. Override in subclasses.
|
|
402
|
+
|
|
403
|
+
#### \`save<T>(item: T): Promise<T>\`
|
|
404
|
+
Saves an item. Override in subclasses.
|
|
405
|
+
|
|
406
|
+
#### \`findById<T>(id: string): Promise<T | null>\`
|
|
407
|
+
Finds an item by ID. Override in subclasses.
|
|
408
|
+
|
|
409
|
+
## IQFactory
|
|
410
|
+
|
|
411
|
+
Factory for building queries.
|
|
412
|
+
|
|
413
|
+
### Methods
|
|
414
|
+
|
|
415
|
+
#### \`create(type: string): QueryBuilder\`
|
|
416
|
+
Creates a new query builder for the specified type.
|
|
417
|
+
|
|
418
|
+
This API reference provides complete coverage of all Fjell Core functionality.`
|
|
419
|
+
|
|
420
|
+
default:
|
|
421
|
+
return `# ${sectionId}
|
|
422
|
+
|
|
423
|
+
Documentation for this section is being prepared.
|
|
424
|
+
|
|
425
|
+
Please check back soon for comprehensive content.`
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
const toggleSidebar = () => {
|
|
430
|
+
setSidebarOpen(!sidebarOpen)
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const currentSectionData = documentSections.find(section => section.id === currentSection)
|
|
434
|
+
const currentContent = documents[currentSection]
|
|
435
|
+
|
|
436
|
+
if (loading) {
|
|
437
|
+
return (
|
|
438
|
+
<div className="loading">
|
|
439
|
+
<div className="spinner"></div>
|
|
440
|
+
Loading documentation...
|
|
441
|
+
</div>
|
|
442
|
+
)
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return (
|
|
446
|
+
<div className="app">
|
|
447
|
+
{/* Header */}
|
|
448
|
+
<header className="header">
|
|
449
|
+
<div className="header-container">
|
|
450
|
+
<div className="brand">
|
|
451
|
+
<h1 className="brand-title">
|
|
452
|
+
<span className="brand-fjell">Fjell</span>
|
|
453
|
+
<span className="brand-core">Core</span>
|
|
454
|
+
</h1>
|
|
455
|
+
<p className="brand-tagline">Core Item and Key Framework for Fjell</p>
|
|
456
|
+
</div>
|
|
457
|
+
|
|
458
|
+
<div className="header-actions">
|
|
459
|
+
<span className="version-badge">v{version}</span>
|
|
460
|
+
<a
|
|
461
|
+
href="https://github.com/getfjell/fjell-core"
|
|
462
|
+
className="header-link"
|
|
463
|
+
target="_blank"
|
|
464
|
+
rel="noopener noreferrer"
|
|
465
|
+
>
|
|
466
|
+
View Source
|
|
467
|
+
</a>
|
|
468
|
+
<a
|
|
469
|
+
href="https://www.npmjs.com/package/@fjell/core"
|
|
470
|
+
className="header-link"
|
|
471
|
+
target="_blank"
|
|
472
|
+
rel="noopener noreferrer"
|
|
473
|
+
>
|
|
474
|
+
Install Package
|
|
475
|
+
</a>
|
|
476
|
+
</div>
|
|
477
|
+
|
|
478
|
+
<button
|
|
479
|
+
className="menu-toggle"
|
|
480
|
+
onClick={toggleSidebar}
|
|
481
|
+
aria-label="Toggle navigation"
|
|
482
|
+
>
|
|
483
|
+
<span className="menu-line"></span>
|
|
484
|
+
<span className="menu-line"></span>
|
|
485
|
+
<span className="menu-line"></span>
|
|
486
|
+
</button>
|
|
487
|
+
</div>
|
|
488
|
+
</header>
|
|
489
|
+
|
|
490
|
+
{/* Layout */}
|
|
491
|
+
<div className="layout">
|
|
492
|
+
{/* Sidebar */}
|
|
493
|
+
<nav className={`sidebar ${sidebarOpen ? 'sidebar-open' : ''}`}>
|
|
494
|
+
<div className="sidebar-content">
|
|
495
|
+
<div className="sidebar-header">
|
|
496
|
+
<h2 className="sidebar-title">Documentation</h2>
|
|
497
|
+
<p className="sidebar-subtitle">Core concepts & philosophy</p>
|
|
498
|
+
</div>
|
|
499
|
+
|
|
500
|
+
<div className="nav-menu">
|
|
501
|
+
{documentSections.map((section) => (
|
|
502
|
+
<button
|
|
503
|
+
key={section.id}
|
|
504
|
+
className={`nav-item ${currentSection === section.id ? 'nav-item-active' : ''}`}
|
|
505
|
+
onClick={() => {
|
|
506
|
+
setCurrentSection(section.id)
|
|
507
|
+
setSidebarOpen(false)
|
|
508
|
+
}}
|
|
509
|
+
>
|
|
510
|
+
<span className="nav-item-title">{section.title}</span>
|
|
511
|
+
<span className="nav-item-subtitle">{section.subtitle}</span>
|
|
512
|
+
</button>
|
|
513
|
+
))}
|
|
514
|
+
</div>
|
|
515
|
+
</div>
|
|
516
|
+
</nav>
|
|
517
|
+
|
|
518
|
+
{/* Main Content */}
|
|
519
|
+
<main className="main">
|
|
520
|
+
<div className="main-content">
|
|
521
|
+
{currentSectionData && (
|
|
522
|
+
<div className="section-header">
|
|
523
|
+
<h1 className="section-title">{currentSectionData.title}</h1>
|
|
524
|
+
<p className="section-subtitle">{currentSectionData.subtitle}</p>
|
|
525
|
+
</div>
|
|
526
|
+
)}
|
|
527
|
+
|
|
528
|
+
<div className="content">
|
|
529
|
+
<div className="markdown-content">
|
|
530
|
+
<ReactMarkdown
|
|
531
|
+
remarkPlugins={[remarkGfm]}
|
|
532
|
+
components={{
|
|
533
|
+
code({ inline, className, children, ...props }: any) {
|
|
534
|
+
const match = /language-(\w+)/.exec(className || '')
|
|
535
|
+
return !inline && match ? (
|
|
536
|
+
<SyntaxHighlighter
|
|
537
|
+
style={oneLight as any}
|
|
538
|
+
language={match[1]}
|
|
539
|
+
PreTag="div"
|
|
540
|
+
{...props}
|
|
541
|
+
>
|
|
542
|
+
{String(children).replace(/\n$/, '')}
|
|
543
|
+
</SyntaxHighlighter>
|
|
544
|
+
) : (
|
|
545
|
+
<code className={className} {...props}>
|
|
546
|
+
{children}
|
|
547
|
+
</code>
|
|
548
|
+
)
|
|
549
|
+
},
|
|
550
|
+
img({ src, alt, ...props }) {
|
|
551
|
+
return (
|
|
552
|
+
<img
|
|
553
|
+
src={src}
|
|
554
|
+
alt={alt}
|
|
555
|
+
{...props}
|
|
556
|
+
onClick={() => src && setFullscreenImage(src)}
|
|
557
|
+
style={{ cursor: 'pointer' }}
|
|
558
|
+
/>
|
|
559
|
+
)
|
|
560
|
+
},
|
|
561
|
+
a({ href, children, ...props }) {
|
|
562
|
+
// Handle links to .ts files specially - load and display code
|
|
563
|
+
if (href && href.endsWith('.ts')) {
|
|
564
|
+
return (
|
|
565
|
+
<button
|
|
566
|
+
className="code-example-link"
|
|
567
|
+
onClick={async () => {
|
|
568
|
+
try {
|
|
569
|
+
const response = await fetch(href.startsWith('/') ? `/core${href}` : `/core/${href}`)
|
|
570
|
+
if (response.ok) {
|
|
571
|
+
const code = await response.text()
|
|
572
|
+
// Create a modal or section to display the code
|
|
573
|
+
const codeSection = document.createElement('div')
|
|
574
|
+
codeSection.className = 'code-example-modal'
|
|
575
|
+
codeSection.innerHTML = `
|
|
576
|
+
<div class="code-example-content">
|
|
577
|
+
<div class="code-example-header">
|
|
578
|
+
<h3>${href}</h3>
|
|
579
|
+
<button onclick="this.parentElement.parentElement.parentElement.remove()">×</button>
|
|
580
|
+
</div>
|
|
581
|
+
<pre><code class="language-typescript">${code}</code></pre>
|
|
582
|
+
</div>
|
|
583
|
+
`
|
|
584
|
+
document.body.appendChild(codeSection)
|
|
585
|
+
} else {
|
|
586
|
+
console.error('Failed to load code example:', href)
|
|
587
|
+
}
|
|
588
|
+
} catch (error) {
|
|
589
|
+
console.error('Error loading code example:', error)
|
|
590
|
+
}
|
|
591
|
+
}}
|
|
592
|
+
style={{
|
|
593
|
+
background: 'none',
|
|
594
|
+
border: 'none',
|
|
595
|
+
color: '#0066cc',
|
|
596
|
+
textDecoration: 'underline',
|
|
597
|
+
cursor: 'pointer',
|
|
598
|
+
padding: 0,
|
|
599
|
+
font: 'inherit'
|
|
600
|
+
}}
|
|
601
|
+
{...props}
|
|
602
|
+
>
|
|
603
|
+
{children}
|
|
604
|
+
</button>
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Regular links
|
|
609
|
+
return (
|
|
610
|
+
<a href={href} {...props}>
|
|
611
|
+
{children}
|
|
612
|
+
</a>
|
|
613
|
+
)
|
|
614
|
+
}
|
|
615
|
+
}}
|
|
616
|
+
>
|
|
617
|
+
{currentContent || 'Loading content...'}
|
|
618
|
+
</ReactMarkdown>
|
|
619
|
+
</div>
|
|
620
|
+
|
|
621
|
+
{/* Navigation suggestions */}
|
|
622
|
+
<div className="nav-suggestions">
|
|
623
|
+
{documentSections.map((section) => {
|
|
624
|
+
if (section.id === currentSection) return null
|
|
625
|
+
return (
|
|
626
|
+
<button
|
|
627
|
+
key={section.id}
|
|
628
|
+
className="nav-suggestion"
|
|
629
|
+
onClick={() => setCurrentSection(section.id)}
|
|
630
|
+
>
|
|
631
|
+
<span className="nav-suggestion-label">Next</span>
|
|
632
|
+
<span className="nav-suggestion-title">{section.title}</span>
|
|
633
|
+
</button>
|
|
634
|
+
)
|
|
635
|
+
}).filter(Boolean).slice(0, 1)}
|
|
636
|
+
</div>
|
|
637
|
+
</div>
|
|
638
|
+
</div>
|
|
639
|
+
</main>
|
|
640
|
+
</div>
|
|
641
|
+
|
|
642
|
+
<footer className="footer">
|
|
643
|
+
<div className="footer-container">
|
|
644
|
+
<div className="footer-content">
|
|
645
|
+
<p className="footer-text">
|
|
646
|
+
Crafted with intention for the Fjell ecosystem
|
|
647
|
+
</p>
|
|
648
|
+
<p className="footer-license">
|
|
649
|
+
Licensed under Apache-2.0 • 2024
|
|
650
|
+
</p>
|
|
651
|
+
</div>
|
|
652
|
+
</div>
|
|
653
|
+
</footer>
|
|
654
|
+
|
|
655
|
+
{/* Fullscreen Image Modal */}
|
|
656
|
+
{fullscreenImage && (
|
|
657
|
+
<div className="fullscreen-modal" onClick={() => setFullscreenImage(null)}>
|
|
658
|
+
<div className="fullscreen-content">
|
|
659
|
+
<img
|
|
660
|
+
src={fullscreenImage}
|
|
661
|
+
alt="Fullscreen view"
|
|
662
|
+
className="fullscreen-image"
|
|
663
|
+
/>
|
|
664
|
+
<button
|
|
665
|
+
className="close-button"
|
|
666
|
+
onClick={() => setFullscreenImage(null)}
|
|
667
|
+
aria-label="Close fullscreen view"
|
|
668
|
+
>
|
|
669
|
+
×
|
|
670
|
+
</button>
|
|
671
|
+
</div>
|
|
672
|
+
</div>
|
|
673
|
+
)}
|
|
674
|
+
|
|
675
|
+
{/* Mobile overlay */}
|
|
676
|
+
<div
|
|
677
|
+
className={`mobile-overlay ${sidebarOpen ? 'mobile-overlay-visible' : ''}`}
|
|
678
|
+
onClick={() => setSidebarOpen(false)}
|
|
679
|
+
/>
|
|
680
|
+
</div>
|
|
681
|
+
)
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
export default App
|