Skip to content

Fixes and refactors #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 16 commits into from
May 20, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 9 additions & 8 deletions functions/Element/setElementHealth.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ shared: &shared
description: |
This function sets the health of a [player](/player), [ped](/ped), [vehicle](/vehicle), or [object](/object) element.
notes:
- |
In the case of the [vehicle](/vehicle) element, the health ranges from 0 to 1000.

- *1000:* no damage at all
- *650:* white steam 0%, black smoke 0%
- *450:* white steam 100%, black smoke 50%
- *250:* white steam 0%, black smoke 100%
- *249:* fire with big black smoke (blowing up)
- type: 'standard'
content: |
In the case of the [vehicle](/vehicle) element, the health ranges from 0 to 1000.

- *1000:* no damage at all
- *650:* white steam 0%, black smoke 0%
- *450:* white steam 100%, black smoke 50%
- *250:* white steam 0%, black smoke 100%
- *249:* fire with big black smoke (blowing up)
parameters:
- name: 'theElement'
type: 'element'
Expand Down
20 changes: 18 additions & 2 deletions schemas/common-defs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,25 @@ type: object
$defs:
notes:
type: array
description: List of noteworthy pieces of information for the item.
description: |
List of noteworthy pieces of information for the item.
Each note can be of a specific type, e.g., 'standard' or 'important'.
items:
type: string
type: object
description: An individual note item.
required:
- content
properties:
type:
type: string
description: The type of the note, influencing its presentation.
enum:
- standard
- important
default: standard
content:
type: string
description: The textual content of the note. Can use markdown and YAML multi-line strings.

meta:
type: array
Expand Down
2 changes: 1 addition & 1 deletion schemas/function.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,4 @@ $defs:
description: Type of the return value.
name:
type: string
description: Name of the return value.
description: Name of the return value.
63 changes: 63 additions & 0 deletions web/src/components/NoteBox.astro
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
---
import type { HTMLAttributes } from 'astro/types';

interface Props extends HTMLAttributes<'div'> {
type?: 'standard' | 'important';
}

const { type = 'standard', class: className, ...rest } = Astro.props;
---
<div
class:list={["custom-note-box", { 'note-important': type === 'important' }, className]}
{...rest}
>
{type === 'important' && (
<span class="note-icon" aria-label="Importante">
<svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true">
<circle cx="10" cy="10" r="10" fill="#e53935"/>
<text x="10" y="15" text-anchor="middle" font-size="14" fill="#fff" font-family="Arial" font-weight="bold">!</text>
</svg>
</span>
)}
<slot />
</div >

<style>
.custom-note-box {
background-color: var(--sl-color-blue-low);
border-left: 4px solid var(--sl-color-blue);
border-radius: 8px;
padding: 1rem 1.25rem;
margin-bottom: 1rem;
color: var(--sl-color-text);
position: relative;
}

html[data-theme="dark"] .custom-note-box {
background-color: var(--sl-color-gray-5);
}

.custom-note-box.note-important {
background-color: var(--sl-color-red-low);
border-left-color: #d32f2f;
}

html[data-theme="dark"] .custom-note-box.note-important {
--sl-color-red-low: hsl(var(--sl-color-red-hue), 50%, 20%);
}

.note-icon {
position: absolute;
top: 0.75rem;
right: 0.75rem;
z-index: 1;
display: flex;
align-items: center;
justify-content: center;
pointer-events: none;
}
.custom-note-box.note-important {
padding-right: 2.5rem;
padding-left: 1.25rem;
}
</style>
58 changes: 42 additions & 16 deletions web/src/pages/[func].astro
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ import fs from "fs";
import path from "path";
import { Code } from '@astrojs/starlight/components';

import NoteBox from '@src/components/NoteBox.astro';
import '@src/styles/function-page.css';
import type { NotesType } from '@src/utils/types';

export async function getStaticPaths() {
const functions = await getCollection('functions');
return functions.map(func => ({
Expand All @@ -19,24 +23,30 @@ const { func } = Astro.props;

const funcInfo = getFunctionInfo(func.data);
const funcType = funcInfo.type;
const funcTypePretty = funcInfo.typePretty;

const funcPair = funcInfo.pair;
const funcPath = path.dirname(func.filePath ?? "")
let funcExamples = funcInfo.examples
const funcPath = path.dirname(func.filePath ?? "");

if ( funcExamples.length > 0 ){
funcExamples = funcInfo.examples.map((example: any) => {
const { description, notes: funcNotes, examples: rawExamples } = funcInfo;

let processedExamples: any[] = [];
if (Array.isArray(rawExamples) && rawExamples.length > 0) {
processedExamples = rawExamples.map((example: any) => {
try {
const luaCode = fs.readFileSync(path.resolve(`${funcPath}`, example.path), "utf8");
const exampleFilePath = path.resolve(funcPath, example.path);
const luaCode = fs.readFileSync(exampleFilePath, "utf8");
return { ...example, luaCode };
} catch (error) {
console.error(`Error reading ${example.path}:`, error);
return { ...example, luaCode: "Loading example error." };
console.error(`Error reading example file ${example.path} at ${path.resolve(funcPath, example.path)}:`, error);
return { ...example, luaCode: `Error loading example: ${example.path}` };
}
});
}

let notesContent: NotesType = [];
if (Array.isArray(funcNotes) && funcNotes.length > 0) {
notesContent = funcNotes;
}

---

<div class={"show-type-badge-" + funcType}>
Expand All @@ -45,18 +55,34 @@ if ( funcExamples.length > 0 ){
title: func.id,
tableOfContents: false,
}}>
<!-- Pair Function Ref -->
{funcPair && (
<p><strong>Pair:</strong> <a href={ funcPair }>{ funcPair }</a></p>
<p><strong>Pair:</strong> <a href={ `/${funcPair}` }>{ funcPair }</a></p>
)}

<!-- Description -->
<Fragment set:html={marked(funcInfo.description)} />
{description && <Fragment set:html={marked(description)} />}

<!-- Notes -->
<div class="notes-section">
{notesContent.map((note) => (
<NoteBox type={note.type}>
<Fragment set:html={marked(note.content)} />
</NoteBox>
))}
</div>

{funcExamples.length > 0 && funcExamples.map((example: any) => (
<div>
<p set:html={marked(example.description)}></p>
<Code code={example.luaCode} lang="lua" title={path.basename(example.path)} />
<!-- Examples -->
{processedExamples.length > 0 && (
<div class="examples-section">
<h3>Exemplos</h3>
{processedExamples.map((example: any) => (
<div class="function-example">
<Fragment set:html={marked(example.description)} />
<Code code={example.luaCode} lang="lua" title={path.basename(example.path)} />
</div>
))}
</div>
))}
)}
</StarlightPage>
</div>
26 changes: 26 additions & 0 deletions web/src/styles/function-page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
.function-syntax,
.function-oop,
.notes-section,
.examples-section {
margin-top: 1.5rem;
margin-bottom: 1.5rem;
}

.function-syntax h3,
.function-oop h3,
.examples-section h3 {
margin-bottom: 0.75rem;
font-size: 1.25em;
border-bottom: 1px solid var(--sl-color-gray-5);
padding-bottom: 0.25rem;
}

.function-example {
margin-bottom: 1.5rem;
}
.function-example > :first-child {
margin-bottom: 0.5rem;
}
.examples-section .function-example:last-child {
margin-bottom: 0;
}
60 changes: 47 additions & 13 deletions web/src/utils/functions.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,49 @@
import { getCollection } from 'astro:content';
import path from 'path';

import type { FunctionType } from './types';
import type { FunctionType, NotesType } from './types';

type FunctionItem = Awaited<ReturnType<typeof getCollection>>[number];

type FunctionParameter = {
name: string;
type: string;
description?: string;
};

type FunctionDetails = {
description?: string;
pair?: boolean;
examples?: { code: string; description?: string }[];
notes?: NotesType;
parameters?: FunctionParameter[];
};

type FunctionsByCategory = {
[folder: string]: FunctionItem[];
};
type FunctionsByTypeByCategory = {
shared: FunctionsByCategory;
client: FunctionsByCategory;
server: FunctionsByCategory;
[key in FunctionType]: FunctionsByCategory;
};


export type FunctionData = {
shared?: any;
client?: any;
server?: any;
};

export type TypedFunctionData = {
shared?: FunctionDetails;
client?: FunctionDetails;
server?: FunctionDetails;
};

export const functionTypePrettyName = {
'client': 'Client-side',
'server': 'Server-side',
'shared': 'Shared',
};
} as const;

function getFunctionType(data: FunctionData): FunctionType {
if (data.shared) return 'shared';
Expand All @@ -33,16 +52,31 @@ function getFunctionType(data: FunctionData): FunctionType {
}
function getFunctionTypePretty(data: FunctionData): string {
const funcType = getFunctionType(data);
return functionTypePrettyName[funcType] ?? 'Server-side';
return functionTypePrettyName[funcType];
}

export function getFunctionInfo(data: FunctionData): any {
export type FunctionInfo = {
description: string;
type: FunctionType;
typePretty: string;
pair: boolean;
examples: { code: string; description?: string }[];
notes?: NotesType;
parameters?: FunctionParameter[];
};

export function getFunctionInfo(data: TypedFunctionData): FunctionInfo {
const type = getFunctionType(data);
const details = data[type] ?? {};

return {
description: data.shared?.description || data.client?.description || data.server?.description || '',
type: getFunctionType(data),
description: details.description || '',
type: type,
typePretty: getFunctionTypePretty(data),
pair: data.shared?.pair || data.client?.pair || data.server?.pair || false,
examples: data.shared?.examples || data.client?.examples || data.server?.examples || [ ],
pair: details.pair || false,
examples: details.examples || [],
notes: details.notes || [],
parameters: details.parameters || [],
};
}

Expand All @@ -55,15 +89,15 @@ let functionsByTypeByCategory: FunctionsByTypeByCategory = {
};

for (let func of functionsCollection) {
const normalizedPath = path.normalize(func.filePath || '');
const normalizedPath = path.normalize(func.id);
const folder = path.basename(path.dirname(normalizedPath));
if (!functionsByCategory[folder]) {
functionsByCategory[folder] = [];
}
functionsByCategory[folder].push(func);

const funcType = getFunctionType(func.data);
if (!functionsByTypeByCategory[funcType][folder]) {
if (!functionsByTypeByCategory[funcType]?.[folder]) {
functionsByTypeByCategory[funcType][folder] = [];
}
functionsByTypeByCategory[funcType][folder].push(func);
Expand Down
6 changes: 5 additions & 1 deletion web/src/utils/types.ts
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
export type FunctionType = 'shared' | 'client' | 'server';
export type FunctionType = 'shared' | 'client' | 'server';
export type NotesType = {
type: 'standard' | 'important';
content: string;
}[];