mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-03-28 03:29:25 +01:00
fix(Docs): documentation renderer fixes
This commit is contained in:
parent
6ac9d147cf
commit
0c8527921c
6
admin/app/exceptions/internal_server_error_exception.ts
Normal file
6
admin/app/exceptions/internal_server_error_exception.ts
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
import { Exception } from '@adonisjs/core/exceptions'
|
||||||
|
|
||||||
|
export default class InternalServerErrorException extends Exception {
|
||||||
|
static status = 500
|
||||||
|
static code = 'E_INTERNAL_SERVER_ERROR'
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import Markdoc from '@markdoc/markdoc'
|
||||||
import { streamToString } from '../../util/docs.js'
|
import { streamToString } from '../../util/docs.js'
|
||||||
import { getFile, getFileStatsIfExists, listDirectoryContentsRecursive } from '../utils/fs.js'
|
import { getFile, getFileStatsIfExists, listDirectoryContentsRecursive } from '../utils/fs.js'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
import InternalServerErrorException from '#exceptions/internal_server_error_exception'
|
||||||
|
|
||||||
export class DocsService {
|
export class DocsService {
|
||||||
private docsPath = path.join(process.cwd(), 'docs')
|
private docsPath = path.join(process.cwd(), 'docs')
|
||||||
|
|
@ -23,35 +24,47 @@ export class DocsService {
|
||||||
}
|
}
|
||||||
|
|
||||||
parse(content: string) {
|
parse(content: string) {
|
||||||
const ast = Markdoc.parse(content)
|
try {
|
||||||
const config = this.getConfig()
|
const ast = Markdoc.parse(content)
|
||||||
const errors = Markdoc.validate(ast, config)
|
const config = this.getConfig()
|
||||||
|
const errors = Markdoc.validate(ast, config)
|
||||||
|
|
||||||
if (errors.length > 0) {
|
// Filter out attribute-undefined errors which may be caused by emojis and special characters
|
||||||
throw new Error(`Markdoc validation errors: ${errors.map((e) => e.error).join(', ')}`)
|
const criticalErrors = errors.filter((e) => e.error.id !== 'attribute-undefined')
|
||||||
|
if (criticalErrors.length > 0) {
|
||||||
|
console.error('Markdoc validation errors:', errors.map((e) => JSON.stringify(e.error)).join(', '))
|
||||||
|
throw new Error('Markdoc validation failed')
|
||||||
|
}
|
||||||
|
|
||||||
|
return Markdoc.transform(ast, config)
|
||||||
|
} catch (error) {
|
||||||
|
console.log('Error parsing Markdoc content:', error)
|
||||||
|
throw new InternalServerErrorException(`Error parsing content: ${(error as Error).message}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
return Markdoc.transform(ast, config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async parseFile(_filename: string) {
|
async parseFile(_filename: string) {
|
||||||
if (!_filename) {
|
try {
|
||||||
throw new Error('Filename is required')
|
if (!_filename) {
|
||||||
}
|
throw new Error('Filename is required')
|
||||||
|
}
|
||||||
|
|
||||||
const filename = _filename.endsWith('.md') ? _filename : `${_filename}.md`
|
const filename = _filename.endsWith('.md') ? _filename : `${_filename}.md`
|
||||||
|
|
||||||
const fileExists = await getFileStatsIfExists(path.join(this.docsPath, filename))
|
const fileExists = await getFileStatsIfExists(path.join(this.docsPath, filename))
|
||||||
if (!fileExists) {
|
if (!fileExists) {
|
||||||
throw new Error(`File not found: ${filename}`)
|
throw new Error(`File not found: ${filename}`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fileStream = await getFile(path.join(this.docsPath, filename), 'stream')
|
const fileStream = await getFile(path.join(this.docsPath, filename), 'stream')
|
||||||
if (!fileStream) {
|
if (!fileStream) {
|
||||||
throw new Error(`Failed to read file stream: ${filename}`)
|
throw new Error(`Failed to read file stream: ${filename}`)
|
||||||
|
}
|
||||||
|
const content = await streamToString(fileStream)
|
||||||
|
return this.parse(content)
|
||||||
|
} catch (error) {
|
||||||
|
throw new InternalServerErrorException(`Error parsing file: ${(error as Error).message}`)
|
||||||
}
|
}
|
||||||
const content = await streamToString(fileStream)
|
|
||||||
return this.parse(content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private prettify(filename: string) {
|
private prettify(filename: string) {
|
||||||
|
|
@ -87,6 +100,21 @@ export class DocsService {
|
||||||
id: { type: String },
|
id: { type: String },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
list: {
|
||||||
|
render: 'List',
|
||||||
|
attributes: {
|
||||||
|
ordered: { type: Boolean },
|
||||||
|
start: { type: Number },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
list_item: {
|
||||||
|
render: 'ListItem',
|
||||||
|
attributes: {
|
||||||
|
marker: { type: String },
|
||||||
|
className: { type: String },
|
||||||
|
class: { type: String }
|
||||||
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
5
admin/docs/about.md
Normal file
5
admin/docs/about.md
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# About Project N.O.M.A.D.
|
||||||
|
|
||||||
|
Project N.O.M.A.D. (Node for Offline Media, Archives, and Data; "Nomad" for short) is a project started in 2025 by Chris Sherwood of [Crosstalk Solutions, LLC.](https://crosstalksolutions.com). The goal of the project is not to create just another utility for storing offline resources, but rather to allow users to run their own ultime "survival computer".
|
||||||
|
|
||||||
|
While many similar offline survival computers are designed to be run on bare-minimum, lightweight hardware, Project N.O.M.A.D. is quite the opposite. To install and run the available AI tools, we highly encourage the use of a beefy, GPU-backed device to make the most of your install.
|
||||||
|
|
@ -1,6 +1,148 @@
|
||||||
# Release Notes
|
# Release Notes
|
||||||
|
|
||||||
## Version 1.1.0 - July 20, 2025
|
## Version 1.10.1 - December 5, 2025
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
1. This is a test
|
||||||
|
- **Kiwix**: ZIM storage path
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.10.0 - December 5, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Disk info monitoring
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **Install**: Add Redis env variables to compose file
|
||||||
|
- **Kiwix**: initial download and setup
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.9.0 - December 5, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Background job management with BullMQ
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **Install**: Character escaping in env variables
|
||||||
|
- **Install**: Host env variable
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.8.0 - December 5, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Alert and button styles redesign
|
||||||
|
- System info page redesign
|
||||||
|
- **Collections**: Curated ZIM Collections
|
||||||
|
- **Collections**: add Preppers Library
|
||||||
|
- **Collections**: add slug, icon, and language
|
||||||
|
- **Collections**: store additional data with resources list
|
||||||
|
- Custom map and ZIM file downloads (WIP)
|
||||||
|
- New maps system (WIP)
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **DockerService**: cleanup old OSM stuff
|
||||||
|
- **Install**: standardize compose file names
|
||||||
|
- Hide query devtools in prod
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.7.0 - December 5, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Alert and button styles redesign
|
||||||
|
- System info page redesign
|
||||||
|
- **Collections**: Curated ZIM Collections
|
||||||
|
- **Collections**: add Preppers Library
|
||||||
|
- **Collections**: add slug, icon, and language
|
||||||
|
- **Collections**: store additional data with resources list
|
||||||
|
- Custom map and ZIM file downloads (WIP)
|
||||||
|
- New maps system (WIP)
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **DockerService**: cleanup old OSM stuff
|
||||||
|
- **Install**: standardize compose file names
|
||||||
|
- Hide query devtools in prod
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.6.0 - November 18, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Added Kolibri to standard app library
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- Standardize container names in management-compose
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.5.0 - November 18, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- Version footer and fix CI version handling
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.4.0 - November 18, 2025
|
||||||
|
|
||||||
|
### 🚀 Features
|
||||||
|
|
||||||
|
- **Services**: Friendly names and descriptions
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **Scripts**: logs directory creation improvements
|
||||||
|
- **Scripts**: fix type in management-compose file path
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.3.0 - October 9, 2025
|
||||||
|
|
||||||
|
### 🚀 New Features
|
||||||
|
|
||||||
|
- Uninstall script now removes non-management Nomad app containers
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **OpenStreetMap**: Apply dir permission fixes more robustly
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.2.0 - October 7, 2025
|
||||||
|
|
||||||
|
### 🚀 New Features
|
||||||
|
|
||||||
|
- Added CyberChef to standard app library
|
||||||
|
- Added Dozzle to core containers for enhanced logs and metrics
|
||||||
|
- Added FlatNotes to standard app library
|
||||||
|
- Uninstall helper script available
|
||||||
|
|
||||||
|
### ✨ Improvements
|
||||||
|
|
||||||
|
- **OpenStreetMap**:
|
||||||
|
- Fixed directory paths and access issues
|
||||||
|
- Improved error handling
|
||||||
|
- Fixed renderer file permissions
|
||||||
|
- Fixed absolute host path issue
|
||||||
|
- **ZIM Manager**:
|
||||||
|
- Initial ZIM download now hosted in Project Nomad GitHub repo for better availability
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Version 1.1.0 - August 20, 2025
|
||||||
|
|
||||||
### 🚀 New Features
|
### 🚀 New Features
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
import React, { JSX } from 'react'
|
import React from 'react'
|
||||||
import Markdoc from '@markdoc/markdoc'
|
import Markdoc from '@markdoc/markdoc'
|
||||||
|
import { Heading } from './markdoc/Heading'
|
||||||
|
import { List } from './markdoc/List'
|
||||||
|
import { ListItem } from './markdoc/ListItem'
|
||||||
|
|
||||||
// Custom components for Markdoc tags
|
// Custom components for Markdoc tags
|
||||||
const Callout = ({
|
const Callout = ({
|
||||||
|
|
@ -27,37 +30,12 @@ const Callout = ({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const Heading = ({
|
|
||||||
level,
|
|
||||||
id,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
level: number
|
|
||||||
id: string
|
|
||||||
children: React.ReactNode
|
|
||||||
}) => {
|
|
||||||
const Tag = `h${level}` as keyof JSX.IntrinsicElements
|
|
||||||
const sizes = {
|
|
||||||
1: 'text-3xl font-bold',
|
|
||||||
2: 'text-2xl font-semibold',
|
|
||||||
3: 'text-xl font-semibold',
|
|
||||||
4: 'text-lg font-semibold',
|
|
||||||
5: 'text-base font-semibold',
|
|
||||||
6: 'text-sm font-semibold',
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
// @ts-ignore
|
|
||||||
<Tag id={id} className={`${sizes[level]} mb-4 mt-6`}>
|
|
||||||
{children}
|
|
||||||
</Tag>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Component mapping for Markdoc
|
// Component mapping for Markdoc
|
||||||
const components = {
|
const components = {
|
||||||
Callout,
|
Callout,
|
||||||
Heading,
|
Heading,
|
||||||
|
List,
|
||||||
|
ListItem,
|
||||||
}
|
}
|
||||||
|
|
||||||
interface MarkdocRendererProps {
|
interface MarkdocRendererProps {
|
||||||
|
|
@ -65,11 +43,9 @@ interface MarkdocRendererProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const MarkdocRenderer: React.FC<MarkdocRendererProps> = ({ content }) => {
|
const MarkdocRenderer: React.FC<MarkdocRendererProps> = ({ content }) => {
|
||||||
return (
|
console.log('Markdoc content:', content)
|
||||||
<div className="prose prose-lg max-w-none">
|
|
||||||
{Markdoc.renderers.react(content, React, { components })}
|
return <div className="tracking-wide">{Markdoc.renderers.react(content, React, { components })}</div>
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default MarkdocRenderer
|
export default MarkdocRenderer
|
||||||
|
|
|
||||||
28
admin/inertia/components/markdoc/Heading.tsx
Normal file
28
admin/inertia/components/markdoc/Heading.tsx
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
import React, { JSX } from 'react'
|
||||||
|
|
||||||
|
export function Heading({
|
||||||
|
level,
|
||||||
|
id,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
level: number
|
||||||
|
id: string
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const Component = `h${level}` as keyof JSX.IntrinsicElements
|
||||||
|
const sizes = {
|
||||||
|
1: 'text-3xl font-bold',
|
||||||
|
2: 'text-2xl font-semibold',
|
||||||
|
3: 'text-xl font-semibold',
|
||||||
|
4: 'text-lg font-semibold',
|
||||||
|
5: 'text-base font-semibold',
|
||||||
|
6: 'text-sm font-semibold',
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<Component id={id} className={`${sizes[level]} mb-2 mt-6`}>
|
||||||
|
{children}
|
||||||
|
</Component>
|
||||||
|
)
|
||||||
|
}
|
||||||
20
admin/inertia/components/markdoc/List.tsx
Normal file
20
admin/inertia/components/markdoc/List.tsx
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
export function List({
|
||||||
|
ordered = false,
|
||||||
|
start,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
ordered?: boolean
|
||||||
|
start?: number
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const className = ordered
|
||||||
|
? 'list-decimal list-outside !ml-12 mb-4 space-y-1'
|
||||||
|
: 'list-disc list-outside !ml-12 mb-4 space-y-1'
|
||||||
|
const Tag = ordered ? 'ol' : 'ul'
|
||||||
|
return (
|
||||||
|
// @ts-ignore
|
||||||
|
<Tag start={ordered ? start : undefined} className={className}>
|
||||||
|
{children}
|
||||||
|
</Tag>
|
||||||
|
)
|
||||||
|
}
|
||||||
4
admin/inertia/components/markdoc/ListItem.tsx
Normal file
4
admin/inertia/components/markdoc/ListItem.tsx
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
|
||||||
|
export function ListItem({ children }: { children: React.ReactNode }) {
|
||||||
|
return <li className="ml-0 !pl-4">{children}</li>
|
||||||
|
}
|
||||||
|
|
@ -8,5 +8,5 @@
|
||||||
"~/*": ["./*"],
|
"~/*": ["./*"],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"include": ["./**/*.ts", "./**/*.tsx"],
|
"include": ["./**/*.ts", "./**/*.tsx", "components/markdoc/nodes/heading.markdoc.js"],
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue
Block a user