feat(garage): resolve virtual garage spawns by lane and selected session vehicle #11
60
.github/workflows/deploy-docs.yml
vendored
Normal file
60
.github/workflows/deploy-docs.yml
vendored
Normal file
@ -0,0 +1,60 @@
|
||||
name: Deploy Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pages: write
|
||||
id-token: write
|
||||
|
||||
concurrency:
|
||||
group: github-pages
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Node
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 22
|
||||
cache: npm
|
||||
cache-dependency-path: docus/package-lock.json
|
||||
|
||||
- name: Install docs dependencies
|
||||
run: npm ci --prefix docus
|
||||
|
||||
- name: Generate docs content
|
||||
run: node tools/sync-docus-docs.mjs
|
||||
|
||||
- name: Build static docs
|
||||
run: npm run build --prefix docus
|
||||
env:
|
||||
DOCS_BASE_URL: /${{ github.event.repository.name }}/
|
||||
DOCS_SITE_URL: https://${{ github.repository_owner }}.github.io
|
||||
DOCS_REPO_URL: https://github.com/${{ github.repository }}
|
||||
DOCS_REPO_BRANCH: ${{ github.ref_name }}
|
||||
|
||||
- name: Upload Pages artifact
|
||||
uses: actions/upload-pages-artifact@v3
|
||||
with:
|
||||
path: docus/.output/public
|
||||
|
||||
deploy:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- name: Deploy to GitHub Pages
|
||||
id: deployment
|
||||
uses: actions/deploy-pages@v4
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
@ -23,6 +23,10 @@ target/
|
||||
|
||||
# Misc
|
||||
node_modules/
|
||||
docus/.nuxt/
|
||||
docus/.output/
|
||||
docus/.data/
|
||||
docus/.nitro/
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
|
||||
@ -33,6 +33,9 @@ password = "root"
|
||||
connect_timeout_ms = 5000
|
||||
```
|
||||
|
||||
For install links and Forge-specific setup steps, see
|
||||
[SurrealDB Setup](../../../docs/SURREALDB_SETUP.md).
|
||||
|
||||
## References
|
||||
|
||||
- [API Reference](./api-reference.md)
|
||||
|
||||
@ -4,6 +4,9 @@ This guide covers the usual path for adding or changing a Forge module.
|
||||
|
||||
## Local Checks
|
||||
|
||||
Before running storage-backed workflows locally, complete
|
||||
[SurrealDB Setup](./SURREALDB_SETUP.md).
|
||||
|
||||
Run these before pushing Rust or extension changes:
|
||||
|
||||
```powershell
|
||||
|
||||
@ -125,6 +125,9 @@ password = "root"
|
||||
connect_timeout_ms = 5000
|
||||
```
|
||||
|
||||
For install links and role-based setup guidance, see
|
||||
[SurrealDB Setup](./SURREALDB_SETUP.md).
|
||||
|
||||
Check persistence readiness before issuing commands that require storage:
|
||||
|
||||
```sqf
|
||||
|
||||
@ -13,6 +13,8 @@ collects framework-level documentation for those pieces.
|
||||
crates.
|
||||
- [Development Guide](./DEVELOPMENT_GUIDE.md): how to add or change a module
|
||||
without breaking the framework boundaries.
|
||||
- [SurrealDB Setup](./SURREALDB_SETUP.md): where to get SurrealDB or
|
||||
Surrealist and how to connect Forge to it for local or live use.
|
||||
|
||||
## Server and Extension Usage Guides
|
||||
|
||||
|
||||
101
docs/SURREALDB_SETUP.md
Normal file
101
docs/SURREALDB_SETUP.md
Normal file
@ -0,0 +1,101 @@
|
||||
# SurrealDB Setup
|
||||
|
||||
Forge uses SurrealDB for durable storage. The Rust server extension connects to
|
||||
SurrealDB on startup and applies Forge schema modules automatically, so setup
|
||||
comes down to running a reachable database and matching the Forge config.
|
||||
|
||||
## Choose the Right Path
|
||||
|
||||
### Developer or Server Operator
|
||||
|
||||
Use this path if you are building Forge, running a local test server, or
|
||||
hosting the live Arma server.
|
||||
|
||||
Official SurrealDB resources:
|
||||
|
||||
- [SurrealDB install page](https://surrealdb.com/install)
|
||||
- [SurrealDB CLI `start` reference](https://surrealdb.com/docs/reference/cli/surrealdb-cli/commands/start)
|
||||
|
||||
Install SurrealDB with the official method for your platform:
|
||||
|
||||
```powershell
|
||||
# Windows
|
||||
iwr https://windows.surrealdb.com -useb | iex
|
||||
```
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
brew install surrealdb/tap/surreal
|
||||
```
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
curl -sSf https://install.surrealdb.com | sh
|
||||
```
|
||||
|
||||
For Forge, start a persistent local database instead of the default in-memory
|
||||
mode:
|
||||
|
||||
```powershell
|
||||
surreal start surrealkv://forge.db --bind 127.0.0.1:8000 --user root --pass root
|
||||
```
|
||||
|
||||
Then copy `arma/server/extension/config.example.toml` to `config.toml` next to
|
||||
`forge_server_x64.dll` and keep the values aligned with the database you
|
||||
started:
|
||||
|
||||
```toml
|
||||
[surreal]
|
||||
endpoint = "127.0.0.1:8000"
|
||||
namespace = "forge"
|
||||
database = "main"
|
||||
username = "root"
|
||||
password = "root"
|
||||
connect_timeout_ms = 5000
|
||||
```
|
||||
|
||||
After that:
|
||||
|
||||
1. Start the Arma server with the Forge extension enabled.
|
||||
2. Let the extension connect and apply the Forge schema modules.
|
||||
3. Verify the connection state:
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["status", []];
|
||||
"forge_server" callExtension ["surreal:status", []];
|
||||
```
|
||||
|
||||
If you change the endpoint, namespace, database, username, or password in
|
||||
SurrealDB, change the same values in Forge's `config.toml`.
|
||||
|
||||
### Mission Designer or Community Manager/Leader
|
||||
|
||||
Use this path if you mostly need to inspect, query, or adjust data for a test
|
||||
or live server and you are not changing Forge source code.
|
||||
|
||||
Official SurrealDB resources:
|
||||
|
||||
- [Surrealist installation](https://surrealdb.com/docs/explore/surrealist/installation)
|
||||
- [Surrealist web app](https://app.surrealdb.com)
|
||||
- [Surrealist local database serving](https://surrealdb.com/docs/explore/surrealist/concepts/local-database-serving)
|
||||
|
||||
Recommended approach:
|
||||
|
||||
1. Install **Surrealist Desktop**. It is the better fit for Forge because the
|
||||
official docs note that the web app can be limited when connecting to
|
||||
`localhost` or non-HTTPS endpoints.
|
||||
2. Connect Surrealist to the same database Forge uses.
|
||||
3. Use the values from the server's `config.toml`:
|
||||
|
||||
```text
|
||||
Endpoint: http://127.0.0.1:8000
|
||||
Namespace: forge
|
||||
Database: main
|
||||
Username: root
|
||||
Password: root
|
||||
```
|
||||
|
||||
If you need your own local sandbox instead of connecting to an existing Forge
|
||||
server, install SurrealDB first and follow the developer/server-operator path
|
||||
above. Surrealist Desktop can also launch a local database for you after the
|
||||
`surreal` executable is installed and available on your `PATH`.
|
||||
5
docus/.gitignore
vendored
Normal file
5
docus/.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
.nuxt
|
||||
.output
|
||||
.data
|
||||
.nitro
|
||||
node_modules
|
||||
37
docus/README.md
Normal file
37
docus/README.md
Normal file
@ -0,0 +1,37 @@
|
||||
# Forge Docs
|
||||
|
||||
This directory contains the online documentation site for the Forge framework.
|
||||
The site is built with Nuxt and Docus, and its content is generated from the
|
||||
repository's source markdown files.
|
||||
|
||||
## Local Development
|
||||
|
||||
Install dependencies:
|
||||
|
||||
```powershell
|
||||
npm install
|
||||
```
|
||||
|
||||
Start the docs site:
|
||||
|
||||
```powershell
|
||||
npm run dev
|
||||
```
|
||||
|
||||
The content tree is refreshed automatically from:
|
||||
|
||||
- `docs/`
|
||||
- `arma/server/docs/`
|
||||
|
||||
## Production Build
|
||||
|
||||
```powershell
|
||||
npm run build
|
||||
```
|
||||
|
||||
Use these environment variables when deploying to a custom host:
|
||||
|
||||
- `DOCS_BASE_URL`
|
||||
- `DOCS_SITE_URL`
|
||||
- `DOCS_REPO_URL`
|
||||
- `DOCS_REPO_BRANCH`
|
||||
34
docus/app.config.ts
Normal file
34
docus/app.config.ts
Normal file
@ -0,0 +1,34 @@
|
||||
const repoUrl =
|
||||
process.env.DOCS_REPO_URL ||
|
||||
'https://github.com/InnovativeDevSolutions/forge';
|
||||
const repoBranch = process.env.DOCS_REPO_BRANCH || 'master';
|
||||
const siteUrl =
|
||||
process.env.DOCS_SITE_URL ||
|
||||
'https://innovativedevsolutions.github.io';
|
||||
|
||||
export default defineAppConfig({
|
||||
site: {
|
||||
name: 'Forge Framework',
|
||||
description:
|
||||
'Persistent Arma 3 framework with Rust services, SurrealDB storage, and browser-backed client UIs.',
|
||||
url: siteUrl,
|
||||
socials: {
|
||||
github: 'InnovativeDevSolutions/forge'
|
||||
}
|
||||
},
|
||||
github: {
|
||||
url: repoUrl,
|
||||
branch: repoBranch,
|
||||
rootDir: 'docus'
|
||||
},
|
||||
footer: {
|
||||
credits: 'Copyright © 2025-2026 Forge Framework',
|
||||
links: [
|
||||
{
|
||||
icon: 'simple-icons:github',
|
||||
href: repoUrl,
|
||||
target: '_blank'
|
||||
}
|
||||
]
|
||||
}
|
||||
});
|
||||
2
docus/content/1.getting-started/.navigation.yml
Normal file
2
docus/content/1.getting-started/.navigation.yml
Normal file
@ -0,0 +1,2 @@
|
||||
title: Getting Started
|
||||
icon: i-lucide-rocket
|
||||
88
docus/content/1.getting-started/0.index.md
Normal file
88
docus/content/1.getting-started/0.index.md
Normal file
@ -0,0 +1,88 @@
|
||||
# Getting Started
|
||||
|
||||
Use this section as the main entry point for the Forge framework.
|
||||
|
||||
Forge combines:
|
||||
|
||||
- Arma 3 client addons for UX and browser-hosted interfaces
|
||||
- Arma 3 server addons for mission integration and authoritative flow control
|
||||
- a Rust server extension for command routing and persistence
|
||||
- shared Rust crates for models, repositories, and services
|
||||
- SurrealDB for durable storage
|
||||
|
||||
## Common Commands
|
||||
|
||||
```powershell
|
||||
cargo test
|
||||
npm run build:webui
|
||||
.\build-arma.ps1
|
||||
```
|
||||
|
||||
## Start Here
|
||||
|
||||
::u-page-grid
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-network
|
||||
title: Architecture
|
||||
to: /getting-started/architecture
|
||||
---
|
||||
Understand how SQF, Rust services, SurrealDB, and browser UIs fit together.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-boxes
|
||||
title: Module Reference
|
||||
to: /getting-started/module-reference
|
||||
---
|
||||
Review gameplay domains, infrastructure modules, and extension command groups.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-wrench
|
||||
title: Development Guide
|
||||
to: /getting-started/development
|
||||
---
|
||||
See the rules for adding modules and changing boundaries without regressions.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-database
|
||||
title: SurrealDB Setup
|
||||
to: /getting-started/surrealdb-setup
|
||||
---
|
||||
Install SurrealDB, match Forge config values, and choose the right setup path
|
||||
for developers or admin-facing roles.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-server-cog
|
||||
title: Server Extension
|
||||
to: /server-extension
|
||||
---
|
||||
Follow the extension architecture, API surface, and SQF usage examples.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-layers-3
|
||||
title: Server Modules
|
||||
to: /server-modules
|
||||
---
|
||||
Dive into the actor, bank, CAD, garage, locker, organization, phone, store,
|
||||
task, and owned-storage guides.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-monitor-smartphone
|
||||
title: Client Addons
|
||||
to: /client-addons
|
||||
---
|
||||
Explore the client bridge model and addon-specific browser integration rules.
|
||||
:::
|
||||
::
|
||||
136
docus/content/1.getting-started/1.architecture.md
Normal file
136
docus/content/1.getting-started/1.architecture.md
Normal file
@ -0,0 +1,136 @@
|
||||
# Framework Architecture
|
||||
|
||||
Forge is organized around domain modules. A domain usually has SQF addon
|
||||
entry points, Rust models, repository traits, service logic, extension command
|
||||
handlers, and optional browser UI.
|
||||
|
||||
## Runtime Flow
|
||||
|
||||
```text
|
||||
Arma client UI or SQF action
|
||||
-> client addon bridge
|
||||
-> server addon function
|
||||
-> forge_server callExtension command
|
||||
-> extension command group
|
||||
-> forge-services domain service
|
||||
-> forge-repositories trait
|
||||
-> SurrealDB repository implementation
|
||||
-> SurrealDB
|
||||
```
|
||||
|
||||
For small payloads, server SQF calls `forge_server` directly through the
|
||||
extension bridge. For large payloads, `arma/server/addons/extension` stages
|
||||
request and response chunks through the extension transport module.
|
||||
|
||||
## Main Layers
|
||||
|
||||
### Client Addons
|
||||
|
||||
Client addons live under `arma/client/addons`. They own local player UX,
|
||||
keybinds, browser UI dialogs, and UI-to-SQF event handling. When a client needs
|
||||
durable or authoritative state, it routes work to the matching server addon
|
||||
instead of touching persistence directly.
|
||||
|
||||
### Server Addons
|
||||
|
||||
Server addons live under `arma/server/addons`. They own server-side SQF
|
||||
initialization, game-object integration, validation near the Arma runtime, and
|
||||
calls into the Rust extension. The `extension` addon is the shared bridge for
|
||||
`callExtension` and transport handling.
|
||||
|
||||
### Rust Extension
|
||||
|
||||
The server extension lives under `arma/server/extension`. It registers the
|
||||
`forge_server` command groups, loads configuration, initializes SurrealDB, and
|
||||
maps SQF command inputs into service calls.
|
||||
|
||||
The extension should stay thin:
|
||||
|
||||
- Parse and validate command arguments that arrive from SQF.
|
||||
- Resolve Arma-specific context such as player UID when required.
|
||||
- Call the matching service.
|
||||
- Serialize the service result back to JSON or a simple string.
|
||||
|
||||
### Shared Rust Crates
|
||||
|
||||
The `lib` workspace contains reusable Rust crates:
|
||||
|
||||
- `forge-models`: shared domain structs and serialization rules.
|
||||
- `forge-repositories`: storage-agnostic repository traits and in-memory
|
||||
implementations used by tests and hot-state services.
|
||||
- `forge-services`: domain behavior, validation, and mutation workflows.
|
||||
- `forge-shared`: cross-crate helpers.
|
||||
|
||||
### Persistence
|
||||
|
||||
Durable storage is SurrealDB. Schema modules live under
|
||||
`arma/server/extension/src/schema`, and concrete SurrealDB repository
|
||||
implementations live under `arma/server/extension/src/storage`.
|
||||
|
||||
Repository traits stay in `lib/repositories` so service logic remains testable
|
||||
without a database.
|
||||
|
||||
## Hot State
|
||||
|
||||
Several domains have `hot` command groups. Hot state keeps a runtime copy of
|
||||
frequently accessed data in memory, then saves it back to durable storage when
|
||||
requested. This is useful for player state that changes often during a session.
|
||||
|
||||
Typical hot-state flow:
|
||||
|
||||
```text
|
||||
actor:hot:init
|
||||
actor:hot:get
|
||||
actor:hot:override
|
||||
actor:hot:save
|
||||
actor:hot:remove
|
||||
```
|
||||
|
||||
Use hot state for session workflows. Use normal domain commands for direct
|
||||
durable CRUD operations.
|
||||
|
||||
## Transport Layer
|
||||
|
||||
The transport layer exists because Arma extension calls have practical payload
|
||||
size limits. It provides chunked request and response handling while still
|
||||
routing to the same domain command groups.
|
||||
|
||||
Common direct command:
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["status", []];
|
||||
```
|
||||
|
||||
Common transport path:
|
||||
|
||||
```text
|
||||
server addon fnc_extCall
|
||||
-> transport:request:append
|
||||
-> transport:invoke_stored
|
||||
-> transport:response:get
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
The server extension reads `config.toml` next to the extension DLL. The current
|
||||
persistence section is:
|
||||
|
||||
```toml
|
||||
[surreal]
|
||||
endpoint = "127.0.0.1:8000"
|
||||
namespace = "forge"
|
||||
database = "main"
|
||||
username = "root"
|
||||
password = "root"
|
||||
connect_timeout_ms = 5000
|
||||
```
|
||||
|
||||
For install links and role-based setup guidance, see
|
||||
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
||||
|
||||
Check persistence readiness before issuing commands that require storage:
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["status", []];
|
||||
"forge_server" callExtension ["surreal:status", []];
|
||||
```
|
||||
221
docus/content/1.getting-started/2.module-reference.md
Normal file
221
docus/content/1.getting-started/2.module-reference.md
Normal file
@ -0,0 +1,221 @@
|
||||
# Module Reference
|
||||
|
||||
This reference lists the main Forge modules and where each layer lives.
|
||||
|
||||
## Directory Map
|
||||
|
||||
```text
|
||||
arma/client/addons/ Client-side Arma addons and browser UIs
|
||||
arma/server/addons/ Server-side Arma addons and extension bridge
|
||||
arma/server/extension/ Rust arma-rs extension and SurrealDB adapters
|
||||
bin/icom/ Interprocess communication helper
|
||||
lib/models/ Shared domain data models
|
||||
lib/repositories/ Repository traits and in-memory stores
|
||||
lib/services/ Domain services and workflow logic
|
||||
lib/shared/ Cross-crate helpers
|
||||
tools/ Web UI build tooling
|
||||
docs/ Framework-level documentation
|
||||
```
|
||||
|
||||
## Gameplay Domains
|
||||
|
||||
| Domain | Purpose | Client addon | Server addon | Service/model layer | Extension group |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| Actor | Player identity, loadout, position, status, contact identifiers, and persistent character data. | `arma/client/addons/actor` | `arma/server/addons/actor` | `lib/models/src/actor.rs`, `lib/services/src/actor.rs` | `actor:*` |
|
||||
| Bank | Player accounts, cash/bank balances, PIN validation, transfers, checkout charging, and transaction context. | `arma/client/addons/bank` | `arma/server/addons/bank` | `lib/models/src/bank.rs`, `lib/services/src/bank.rs` | `bank:*`, `bank:hot:*` |
|
||||
| CAD | Dispatch requests, assignments, orders, activity stream, profiles, groups, and hydrated dispatcher views. | `arma/client/addons/cad` | `arma/server/addons/cad` | `lib/models/src/cad.rs`, `lib/services/src/cad.rs` | `cad:*` |
|
||||
| Garage | Player vehicle storage with plate IDs, fuel, damage, and hit point state. | `arma/client/addons/garage` | `arma/server/addons/garage` | `lib/models/src/garage.rs`, `lib/services/src/garage.rs` | `garage:*`, `garage:hot:*` |
|
||||
| Locker | Player item storage keyed by classname with category and amount. | `arma/client/addons/locker` | `arma/server/addons/locker` | `lib/models/src/locker.rs`, `lib/services/src/locker.rs` | `locker:*`, `locker:hot:*` |
|
||||
| Organization | Player organizations, membership, treasury, credit lines, shared assets, and fleet data. | `arma/client/addons/org` | `arma/server/addons/org` | `lib/models/src/org.rs`, `lib/services/src/org.rs` | `org:*`, `org:hot:*` |
|
||||
| Phone | Contacts, messages, and email state. | `arma/client/addons/phone` | `arma/server/addons/phone` | `lib/models/src/phone.rs`, `lib/services/src/phone.rs` | `phone:*` |
|
||||
| Store | Storefront entity setup, catalog hydration, checkout workflows, and checkout charging integration. | `arma/client/addons/store` | `arma/server/addons/store` | `lib/models/src/store.rs`, `lib/services/src/store.rs` | `store:checkout` |
|
||||
| Task | Server-owned mission/task flows, catalog, ownership, status, participant tracking, rewards, and defuse counters. | none | `arma/server/addons/task` | `lib/models/src/task.rs`, `lib/services/src/task.rs` | `task:*` |
|
||||
| Owned Garage | Organization or owner-scoped vehicle unlock storage. | via garage/org UI | server extension only | `lib/models/src/v_garage.rs`, `lib/services/src/v_garage.rs` | `owned:garage:*` |
|
||||
| Owned Locker | Organization or owner-scoped arsenal unlock storage. | via locker/org UI | server extension only | `lib/models/src/v_locker.rs`, `lib/services/src/v_locker.rs` | `owned:locker:*` |
|
||||
|
||||
Server and extension guides:
|
||||
[Actor](/server-modules/actor),
|
||||
[Bank](/server-modules/bank),
|
||||
[CAD](/server-modules/cad),
|
||||
[Economy](/server-modules/economy),
|
||||
[Garage](/server-modules/garage),
|
||||
[Locker](/server-modules/locker),
|
||||
[Organization](/server-modules/organization),
|
||||
[Owned Storage](/server-modules/owned-storage),
|
||||
[Phone](/server-modules/phone),
|
||||
[Store](/server-modules/store),
|
||||
[Task](/server-modules/task).
|
||||
|
||||
Client guides:
|
||||
[Client Overview](/client-addons),
|
||||
[Main](/client-addons/main),
|
||||
[Common](/client-addons/common),
|
||||
[Actor](/client-addons/actor),
|
||||
[Bank](/client-addons/bank),
|
||||
[CAD](/client-addons/cad),
|
||||
[Garage](/client-addons/garage),
|
||||
[Locker](/client-addons/locker),
|
||||
[Notifications](/client-addons/notifications),
|
||||
[Organization](/client-addons/organization),
|
||||
[Phone](/client-addons/phone),
|
||||
[Store](/client-addons/store).
|
||||
|
||||
## Infrastructure Modules
|
||||
|
||||
| Module | Purpose | Location |
|
||||
| --- | --- | --- |
|
||||
| `common` | Shared SQF helpers, base stores, utility functions, and shared UI bridge pieces. | `arma/client/addons/common`, `arma/server/addons/common` |
|
||||
| `extension` | Server SQF bridge around `forge_server` extension calls and chunked transport. | `arma/server/addons/extension` |
|
||||
| `main` | Mod-level configuration, pre-init wiring, and server/client startup glue. | `arma/client/addons/main`, `arma/server/addons/main` |
|
||||
| `economy` | Server-side fuel, medical, and service economy helpers. Fuel and repair charge organization hot state; medical charges player bank/cash first, then organization funds with repayable member debt when personal funds cannot cover the bill. | `arma/server/addons/economy` |
|
||||
| `notifications` | Client notification UI, sounds, and UI event handling. | `arma/client/addons/notifications` |
|
||||
| `icom` | Rust helper for interprocess communication and event broadcasting. | `bin/icom`, `arma/server/extension/src/icom.rs` |
|
||||
| `terrain` | Extension-side terrain export helper. | `arma/server/extension/src/terrain.rs` |
|
||||
| `transport` | Chunked request/response handling for large extension payloads. | `arma/server/extension/src/transport.rs` |
|
||||
| `surreal` | SurrealDB connection lifecycle and status reporting. | `arma/server/extension/src/surreal.rs` |
|
||||
|
||||
## Extension Command Groups
|
||||
|
||||
Commands are invoked with:
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["group:command", [_arg1, _arg2]];
|
||||
```
|
||||
|
||||
Nested groups use additional `:` separators, for example
|
||||
`bank:hot:deposit`.
|
||||
|
||||
### Core
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `version` | Return the extension version string. |
|
||||
| `status` | Return SurrealDB connection state. |
|
||||
| `surreal:status` | Return SurrealDB connection state directly from the Surreal module. |
|
||||
|
||||
### Actor
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `actor:get` | Fetch actor data for a resolved player UID. |
|
||||
| `actor:create` | Create actor data from JSON. |
|
||||
| `actor:update` | Apply actor JSON updates. |
|
||||
| `actor:exists` | Return `true` or `false`. |
|
||||
| `actor:delete` | Delete actor data. |
|
||||
| `actor:hot:init`, `actor:hot:get`, `actor:hot:keys`, `actor:hot:override`, `actor:hot:save`, `actor:hot:remove` | Manage actor hot state. |
|
||||
|
||||
See [Actor Usage Guide](/server-modules/actor) for examples.
|
||||
|
||||
### Bank
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `bank:get`, `bank:create`, `bank:update`, `bank:exists`, `bank:delete` | Durable bank CRUD. |
|
||||
| `bank:hot:init`, `bank:hot:get`, `bank:hot:override`, `bank:hot:patch`, `bank:hot:save`, `bank:hot:remove` | Manage bank hot state. |
|
||||
| `bank:hot:deposit`, `bank:hot:withdraw`, `bank:hot:deposit_earnings`, `bank:hot:transfer` | Mutate hot bank balances with operation context. |
|
||||
| `bank:hot:charge_checkout` | Charge a checkout against hot bank state. |
|
||||
| `bank:hot:validate_pin` | Validate a PIN for bank operations. |
|
||||
|
||||
See [Bank Usage Guide](/server-modules/bank) for examples.
|
||||
|
||||
### Garage
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `garage:create`, `garage:get`, `garage:add`, `garage:update`, `garage:patch`, `garage:remove`, `garage:delete`, `garage:exists` | Durable player garage operations. |
|
||||
| `garage:hot:init`, `garage:hot:get`, `garage:hot:override`, `garage:hot:add`, `garage:hot:remove_vehicle`, `garage:hot:save`, `garage:hot:remove` | Manage player garage hot state. |
|
||||
|
||||
See [Garage Usage Guide](/server-modules/garage) for examples.
|
||||
|
||||
### Locker
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `locker:create`, `locker:get`, `locker:add`, `locker:update`, `locker:patch`, `locker:remove`, `locker:delete`, `locker:exists` | Durable player locker operations. |
|
||||
| `locker:hot:init`, `locker:hot:get`, `locker:hot:override`, `locker:hot:save`, `locker:hot:remove` | Manage player locker hot state. |
|
||||
|
||||
See [Locker Usage Guide](/server-modules/locker) for examples.
|
||||
|
||||
### Organization
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `org:get`, `org:create`, `org:update`, `org:exists`, `org:delete` | Durable organization CRUD. |
|
||||
| `org:assets:get`, `org:assets:update` | Manage organization assets. |
|
||||
| `org:fleet:get`, `org:fleet:update` | Manage organization fleet entries. |
|
||||
| `org:members:get`, `org:members:add`, `org:members:remove` | Manage organization membership. |
|
||||
| `org:hot:*` | Runtime organization workflows including registration, invites, credit lines, checkout charging, assets, fleet, leave, disband, save, and remove. |
|
||||
|
||||
See [Org Usage Guide](/server-modules/organization) for examples.
|
||||
|
||||
### Phone
|
||||
|
||||
| Command | Purpose |
|
||||
| --- | --- |
|
||||
| `phone:init` | Initialize phone state for a UID. |
|
||||
| `phone:contacts:list`, `phone:contacts:add`, `phone:contacts:remove` | Manage contacts. |
|
||||
| `phone:messages:list`, `phone:messages:thread`, `phone:messages:send`, `phone:messages:mark_read`, `phone:messages:delete` | Manage messages. |
|
||||
| `phone:emails:list`, `phone:emails:send`, `phone:emails:mark_read`, `phone:emails:delete` | Manage emails. |
|
||||
| `phone:remove` | Remove phone state for a UID. |
|
||||
|
||||
See [Phone Usage Guide](/server-modules/phone) for examples.
|
||||
|
||||
### CAD
|
||||
|
||||
| Command Group | Purpose |
|
||||
| --- | --- |
|
||||
| `cad:activity:append`, `cad:activity:recent` | Append and read recent activity. |
|
||||
| `cad:assignments:list`, `cad:assignments:assign`, `cad:assignments:acknowledge`, `cad:assignments:decline`, `cad:assignments:upsert`, `cad:assignments:delete` | Manage dispatch assignments. |
|
||||
| `cad:orders:list`, `cad:orders:create`, `cad:orders:create_from_context`, `cad:orders:close`, `cad:orders:upsert`, `cad:orders:delete` | Manage orders. |
|
||||
| `cad:requests:list`, `cad:requests:submit`, `cad:requests:submit_from_context`, `cad:requests:close`, `cad:requests:upsert`, `cad:requests:delete` | Manage requests. |
|
||||
| `cad:profiles:list`, `cad:profiles:update_from_context`, `cad:profiles:upsert`, `cad:profiles:delete` | Manage profiles. |
|
||||
| `cad:groups:build` | Build grouped CAD state. |
|
||||
| `cad:view:hydrate` | Build the dispatcher view model. |
|
||||
|
||||
See [CAD Usage Guide](/server-modules/cad) for examples.
|
||||
|
||||
### Task
|
||||
|
||||
| Command Group | Purpose |
|
||||
| --- | --- |
|
||||
| `task:reset` | Reset task state. |
|
||||
| `task:catalog:active`, `task:catalog:get`, `task:catalog:upsert`, `task:catalog:delete` | Manage task catalog entries. |
|
||||
| `task:ownership:bind`, `task:ownership:release`, `task:ownership:accept`, `task:ownership:reward_context` | Manage task ownership and rewards. |
|
||||
| `task:status:set`, `task:status:get`, `task:status:clear` | Manage task status. |
|
||||
| `task:defuse:increment`, `task:defuse:get` | Manage defuse counters. |
|
||||
| `task:clear` | Clear task state. |
|
||||
|
||||
See [Task Usage Guide](/server-modules/task) for examples.
|
||||
|
||||
### Owned Storage
|
||||
|
||||
| Command Group | Purpose |
|
||||
| --- | --- |
|
||||
| `owned:garage:create`, `owned:garage:fetch`, `owned:garage:get`, `owned:garage:add`, `owned:garage:remove`, `owned:garage:delete`, `owned:garage:exists` | Owner-scoped vehicle storage. |
|
||||
| `owned:garage:hot:*` | Owner-scoped vehicle hot state. |
|
||||
| `owned:locker:create`, `owned:locker:fetch`, `owned:locker:get`, `owned:locker:add`, `owned:locker:remove`, `owned:locker:delete`, `owned:locker:exists` | Owner-scoped item storage. |
|
||||
| `owned:locker:hot:*` | Owner-scoped item hot state. |
|
||||
|
||||
See [Owned Storage Usage Guide](/server-modules/owned-storage) for examples.
|
||||
|
||||
### Other Extension Groups
|
||||
|
||||
| Command Group | Purpose |
|
||||
| --- | --- |
|
||||
| `store:checkout` | Run store checkout behavior. |
|
||||
| `icom:connect`, `icom:broadcast`, `icom:send_event` | ICom connection and event forwarding. |
|
||||
| `terrain:exportSVG` | Export terrain data as SVG. |
|
||||
| `transport:invoke`, `transport:invoke_stored` | Invoke commands through transport. |
|
||||
| `transport:request:append`, `transport:request:clear` | Manage stored request chunks. |
|
||||
| `transport:response:get`, `transport:response:clear` | Manage stored response chunks. |
|
||||
|
||||
## Rust Crates
|
||||
|
||||
| Crate | Role |
|
||||
| --- | --- |
|
||||
| `forge-models` | Domain models and validation. Keep these serializable and free of persistence details. |
|
||||
| `forge-repositories` | Repository traits and in-memory implementations. Keep these storage-agnostic. |
|
||||
| `forge-services` | Business rules and workflows. Depend on repository traits, not concrete databases. |
|
||||
| `forge-shared` | Cross-crate helpers. Keep dependencies light. |
|
||||
| `forge-server` | Arma extension crate. Owns command registration, SurrealDB runtime wiring, and concrete storage adapters. |
|
||||
| `forge-icom` | ICom helper binary and client library. |
|
||||
132
docus/content/1.getting-started/3.development.md
Normal file
132
docus/content/1.getting-started/3.development.md
Normal file
@ -0,0 +1,132 @@
|
||||
# Development Guide
|
||||
|
||||
This guide covers the usual path for adding or changing a Forge module.
|
||||
|
||||
## Local Checks
|
||||
|
||||
Before running storage-backed workflows locally, complete
|
||||
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
||||
|
||||
Run these before pushing Rust or extension changes:
|
||||
|
||||
```powershell
|
||||
cargo fmt --check
|
||||
cargo check
|
||||
cargo test
|
||||
cargo build
|
||||
cargo clippy --all-targets --all-features -- -D warnings
|
||||
```
|
||||
|
||||
Run this after changing browser UI sources:
|
||||
|
||||
```powershell
|
||||
npm run build:webui
|
||||
```
|
||||
|
||||
Build Arma packages with:
|
||||
|
||||
```powershell
|
||||
.\build-arma.ps1
|
||||
```
|
||||
|
||||
## Module Boundaries
|
||||
|
||||
Keep each layer responsible for one kind of work:
|
||||
|
||||
| Layer | Owns | Avoid |
|
||||
| --- | --- | --- |
|
||||
| `lib/models` | Data structures, serde defaults, validation helpers. | Database calls, SQF-specific context. |
|
||||
| `lib/repositories` | Repository traits and in-memory stores. | SurrealDB-specific code. |
|
||||
| `lib/services` | Business rules, workflow orchestration, structured results. | Arma engine calls, extension transport details. |
|
||||
| `arma/server/extension` | Command parsing, context resolution, SurrealDB implementations, serialization to SQF. | Business rules that belong in services. |
|
||||
| `arma/server/addons` | Server SQF lifecycle, game-object integration, calls into `forge_server`. | Direct database logic. |
|
||||
| `arma/client/addons` | Client UI, keybinds, local UI events. | Authoritative persistence. |
|
||||
|
||||
## Adding a Domain Module
|
||||
|
||||
1. Add the model in `lib/models/src/<module>.rs`.
|
||||
2. Export the model from `lib/models/src/lib.rs`.
|
||||
3. Add repository traits in `lib/repositories/src/<module>.rs`.
|
||||
4. Add in-memory repository support if the service needs tests or hot state.
|
||||
5. Export the traits from `lib/repositories/src/lib.rs`.
|
||||
6. Add service logic in `lib/services/src/<module>.rs`.
|
||||
7. Add focused unit tests for service behavior.
|
||||
8. Export the service from `lib/services/src/lib.rs`.
|
||||
9. Add a SurrealDB schema module under `arma/server/extension/src/schema`.
|
||||
10. Add the concrete storage adapter under `arma/server/extension/src/storage`.
|
||||
11. Register the storage adapter in `arma/server/extension/src/storage.rs`.
|
||||
12. Add an extension command group under `arma/server/extension/src/<module>.rs`.
|
||||
13. Register the command group in `arma/server/extension/src/lib.rs`.
|
||||
14. Add server addon functions under `arma/server/addons/<module>` if SQF needs a module-level API.
|
||||
15. Add client addon or browser UI files under `arma/client/addons/<module>` if the module has player-facing UI.
|
||||
16. Add documentation in `docs/` and module-level READMEs.
|
||||
|
||||
## Extension Command Rules
|
||||
|
||||
Commands should return one of these forms:
|
||||
|
||||
- JSON string for structured results.
|
||||
- `"true"` or `"false"` for simple existence and boolean operations.
|
||||
- `"OK"` for successful destructive operations with no response body.
|
||||
- `"Error: <message>"` for failures.
|
||||
|
||||
Prefer stable JSON shapes over ad hoc strings. SQF callers should always check
|
||||
for the `"Error:"` prefix before parsing JSON.
|
||||
|
||||
Example:
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["actor:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Actor request failed: %1", _payload];
|
||||
};
|
||||
|
||||
private _actor = fromJSON _payload;
|
||||
```
|
||||
|
||||
## Persistence Rules
|
||||
|
||||
SurrealDB is the durable store. Keep database-specific mapping in the extension
|
||||
storage adapters, not in services or repository traits.
|
||||
|
||||
When changing persisted data:
|
||||
|
||||
- Update or add the matching `.surql` schema module.
|
||||
- Update the concrete storage adapter.
|
||||
- Preserve existing records when possible through serde defaults or migration
|
||||
logic.
|
||||
- Add tests at the service level for behavior, and add storage tests only when
|
||||
database mapping is the risk.
|
||||
|
||||
## Hot-State Rules
|
||||
|
||||
Use hot state for data that is read or mutated frequently during a player
|
||||
session. Hot-state modules usually provide:
|
||||
|
||||
- `init` to load durable state into memory.
|
||||
- `get` to read the runtime copy.
|
||||
- `override` or focused mutation commands to update the runtime copy.
|
||||
- `save` to write the runtime copy back to SurrealDB.
|
||||
- `remove` to evict the runtime copy.
|
||||
|
||||
Do not assume hot state is durable until `save` succeeds.
|
||||
|
||||
## Web UI Rules
|
||||
|
||||
Browser UI source files live under each client addon. Built assets usually land
|
||||
under that addon's `ui/_site` directory.
|
||||
|
||||
Use the existing common bridge in `arma/client/addons/common` when a UI needs
|
||||
to send events back to SQF. Keep UI state and rendering in JavaScript, and keep
|
||||
server-authoritative decisions in server SQF or Rust services.
|
||||
|
||||
## Documentation Checklist
|
||||
|
||||
When adding or changing a module, update:
|
||||
|
||||
- `docs/MODULE_REFERENCE.md` for framework-level inventory.
|
||||
- A module-specific README in the addon directory when SQF or UI usage changes.
|
||||
- `arma/server/docs/api-reference.md` when extension commands change.
|
||||
- Existing usage guides when payload shapes or workflows change.
|
||||
101
docus/content/1.getting-started/4.surrealdb-setup.md
Normal file
101
docus/content/1.getting-started/4.surrealdb-setup.md
Normal file
@ -0,0 +1,101 @@
|
||||
# SurrealDB Setup
|
||||
|
||||
Forge uses SurrealDB for durable storage. The Rust server extension connects to
|
||||
SurrealDB on startup and applies Forge schema modules automatically, so setup
|
||||
comes down to running a reachable database and matching the Forge config.
|
||||
|
||||
## Choose the Right Path
|
||||
|
||||
### Developer or Server Operator
|
||||
|
||||
Use this path if you are building Forge, running a local test server, or
|
||||
hosting the live Arma server.
|
||||
|
||||
Official SurrealDB resources:
|
||||
|
||||
- [SurrealDB install page](https://surrealdb.com/install)
|
||||
- [SurrealDB CLI `start` reference](https://surrealdb.com/docs/reference/cli/surrealdb-cli/commands/start)
|
||||
|
||||
Install SurrealDB with the official method for your platform:
|
||||
|
||||
```powershell
|
||||
# Windows
|
||||
iwr https://windows.surrealdb.com -useb | iex
|
||||
```
|
||||
|
||||
```bash
|
||||
# macOS
|
||||
brew install surrealdb/tap/surreal
|
||||
```
|
||||
|
||||
```bash
|
||||
# Linux
|
||||
curl -sSf https://install.surrealdb.com | sh
|
||||
```
|
||||
|
||||
For Forge, start a persistent local database instead of the default in-memory
|
||||
mode:
|
||||
|
||||
```powershell
|
||||
surreal start surrealkv://forge.db --bind 127.0.0.1:8000 --user root --pass root
|
||||
```
|
||||
|
||||
Then copy `arma/server/extension/config.example.toml` to `config.toml` next to
|
||||
`forge_server_x64.dll` and keep the values aligned with the database you
|
||||
started:
|
||||
|
||||
```toml
|
||||
[surreal]
|
||||
endpoint = "127.0.0.1:8000"
|
||||
namespace = "forge"
|
||||
database = "main"
|
||||
username = "root"
|
||||
password = "root"
|
||||
connect_timeout_ms = 5000
|
||||
```
|
||||
|
||||
After that:
|
||||
|
||||
1. Start the Arma server with the Forge extension enabled.
|
||||
2. Let the extension connect and apply the Forge schema modules.
|
||||
3. Verify the connection state:
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["status", []];
|
||||
"forge_server" callExtension ["surreal:status", []];
|
||||
```
|
||||
|
||||
If you change the endpoint, namespace, database, username, or password in
|
||||
SurrealDB, change the same values in Forge's `config.toml`.
|
||||
|
||||
### Mission Designer or Community Manager/Leader
|
||||
|
||||
Use this path if you mostly need to inspect, query, or adjust data for a test
|
||||
or live server and you are not changing Forge source code.
|
||||
|
||||
Official SurrealDB resources:
|
||||
|
||||
- [Surrealist installation](https://surrealdb.com/docs/explore/surrealist/installation)
|
||||
- [Surrealist web app](https://app.surrealdb.com)
|
||||
- [Surrealist local database serving](https://surrealdb.com/docs/explore/surrealist/concepts/local-database-serving)
|
||||
|
||||
Recommended approach:
|
||||
|
||||
1. Install **Surrealist Desktop**. It is the better fit for Forge because the
|
||||
official docs note that the web app can be limited when connecting to
|
||||
`localhost` or non-HTTPS endpoints.
|
||||
2. Connect Surrealist to the same database Forge uses.
|
||||
3. Use the values from the server's `config.toml`:
|
||||
|
||||
```text
|
||||
Endpoint: http://127.0.0.1:8000
|
||||
Namespace: forge
|
||||
Database: main
|
||||
Username: root
|
||||
Password: root
|
||||
```
|
||||
|
||||
If you need your own local sandbox instead of connecting to an existing Forge
|
||||
server, install SurrealDB first and follow the developer/server-operator path
|
||||
above. Surrealist Desktop can also launch a local database for you after the
|
||||
`surreal` executable is installed and available on your `PATH`.
|
||||
43
docus/content/2.server-extension/0.index.md
Normal file
43
docus/content/2.server-extension/0.index.md
Normal file
@ -0,0 +1,43 @@
|
||||
# Forge Server Extension
|
||||
|
||||
Forge Server is an arma-rs extension for Arma 3 server-side persistence and
|
||||
domain services. It exposes game-facing commands and stores durable state in
|
||||
SurrealDB.
|
||||
|
||||
## Architecture
|
||||
|
||||
SQF modules call `forge_server` through `fnc_extCall`. Small requests use the
|
||||
direct `callExtension` path, while large payloads are staged through the
|
||||
transport layer.
|
||||
|
||||
```text
|
||||
SQF module
|
||||
-> extension bridge
|
||||
-> domain command
|
||||
-> service layer
|
||||
-> repository
|
||||
-> SurrealDB
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Copy `config.example.toml` to `config.toml` next to the extension DLL.
|
||||
|
||||
```toml
|
||||
[surreal]
|
||||
endpoint = "127.0.0.1:8000"
|
||||
namespace = "forge"
|
||||
database = "main"
|
||||
username = "root"
|
||||
password = "root"
|
||||
connect_timeout_ms = 5000
|
||||
```
|
||||
|
||||
For install links and Forge-specific setup steps, see
|
||||
[SurrealDB Setup](/getting-started/surrealdb-setup).
|
||||
|
||||
## References
|
||||
|
||||
- [API Reference](/server-extension/api-reference)
|
||||
- [Usage Examples](/server-extension/usage-examples)
|
||||
- [Framework Module Guides](/getting-started)
|
||||
48
docus/content/2.server-extension/1.api-reference.md
Normal file
48
docus/content/2.server-extension/1.api-reference.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Forge Server API Reference
|
||||
|
||||
The Forge server extension exposes domain-oriented commands through
|
||||
`callExtension`. Persistent data is stored through the configured SurrealDB
|
||||
connection and schema modules.
|
||||
|
||||
## Core Commands
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["version", []];
|
||||
"forge_server" callExtension ["status", []];
|
||||
"forge_server" callExtension ["surreal:status", []];
|
||||
```
|
||||
|
||||
`status` and `surreal:status` return `initializing`, `connected`, or `failed`.
|
||||
|
||||
## Domain Commands
|
||||
|
||||
Game systems should call the domain APIs instead of raw database operations:
|
||||
|
||||
- `actor:*`
|
||||
- `bank:*`
|
||||
- `garage:*`
|
||||
- `locker:*`
|
||||
- `org:*`
|
||||
- `phone:*`
|
||||
- `store:*`
|
||||
- `task:*`
|
||||
- `cad:*`
|
||||
- `owned:garage:*`
|
||||
- `owned:locker:*`
|
||||
- `transport:*`
|
||||
|
||||
Large request and response payloads are routed through the transport layer when
|
||||
needed by `forge_server_addons_extension_fnc_extCall`.
|
||||
|
||||
## Module Guides
|
||||
|
||||
- [Actor](/server-modules/actor)
|
||||
- [Bank](/server-modules/bank)
|
||||
- [CAD](/server-modules/cad)
|
||||
- [Garage](/server-modules/garage)
|
||||
- [Locker](/server-modules/locker)
|
||||
- [Organization](/server-modules/organization)
|
||||
- [Owned Storage](/server-modules/owned-storage)
|
||||
- [Phone](/server-modules/phone)
|
||||
- [Store](/server-modules/store)
|
||||
- [Task](/server-modules/task)
|
||||
47
docus/content/2.server-extension/2.usage-examples.md
Normal file
47
docus/content/2.server-extension/2.usage-examples.md
Normal file
@ -0,0 +1,47 @@
|
||||
# Forge Server Usage Examples
|
||||
|
||||
These examples use the domain command surface exposed by the extension.
|
||||
Persistence is handled by the server through SurrealDB.
|
||||
|
||||
## Status Check
|
||||
|
||||
```sqf
|
||||
["status", []] call forge_server_extension_fnc_extCall params ["_status", "_ok"];
|
||||
if (_ok && {_status isEqualTo "connected"}) then {
|
||||
systemChat "Forge persistence is online.";
|
||||
};
|
||||
```
|
||||
|
||||
## Actor Fetch
|
||||
|
||||
```sqf
|
||||
private _uid = getPlayerUID player;
|
||||
["actor:get", [_uid]] call forge_server_extension_fnc_extCall params ["_payload", "_ok"];
|
||||
if (_ok) then {
|
||||
private _actor = fromJSON _payload;
|
||||
systemChat format ["Loaded actor %1", _actor getOrDefault ["uid", _uid]];
|
||||
};
|
||||
```
|
||||
|
||||
## Store Checkout
|
||||
|
||||
```sqf
|
||||
private _checkout = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["requesterName", name player],
|
||||
["orgId", "default"],
|
||||
["requesterIsDefaultOrgCeo", false],
|
||||
["paymentMethod", "bank"],
|
||||
["items", [
|
||||
createHashMapFromArray [
|
||||
["classname", "FirstAidKit"],
|
||||
["category", "item"],
|
||||
["priceValue", 50],
|
||||
["quantity", 2]
|
||||
]
|
||||
]],
|
||||
["vehicles", []]
|
||||
];
|
||||
|
||||
["store:checkout", [toJSON _checkout]] call forge_server_extension_fnc_extCall;
|
||||
```
|
||||
2
docus/content/3.server-modules/.navigation.yml
Normal file
2
docus/content/3.server-modules/.navigation.yml
Normal file
@ -0,0 +1,2 @@
|
||||
title: Server Modules
|
||||
icon: i-lucide-layers-3
|
||||
113
docus/content/3.server-modules/0.index.md
Normal file
113
docus/content/3.server-modules/0.index.md
Normal file
@ -0,0 +1,113 @@
|
||||
# Server Module Guides
|
||||
|
||||
These pages document the authoritative server-side workflows in Forge.
|
||||
|
||||
Most modules follow the same shape:
|
||||
|
||||
1. Server SQF gathers game context and validates mission/runtime assumptions.
|
||||
2. The `forge_server` extension routes the request into the matching command group.
|
||||
3. Services apply business rules through storage-agnostic repository traits.
|
||||
4. The extension persists durable state through SurrealDB adapters when needed.
|
||||
|
||||
## Gameplay Domains
|
||||
|
||||
::u-page-grid
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-user-round
|
||||
title: Actor
|
||||
to: /server-modules/actor
|
||||
---
|
||||
Persistent player identity, position, loadout, contact fields, and hot state.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-wallet
|
||||
title: Bank
|
||||
to: /server-modules/bank
|
||||
---
|
||||
Player funds, transfers, PIN validation, checkout charging, and bank hot state.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-map
|
||||
title: CAD
|
||||
to: /server-modules/cad
|
||||
---
|
||||
Dispatch requests, assignments, profiles, grouped state, and hydrated views.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-ambulance
|
||||
title: Economy
|
||||
to: /server-modules/economy
|
||||
---
|
||||
Fuel, service, and medical charging rules across player and organization funds.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-car-front
|
||||
title: Garage
|
||||
to: /server-modules/garage
|
||||
---
|
||||
Vehicle storage, hot-state updates, and persistence of vehicle condition.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-package
|
||||
title: Locker
|
||||
to: /server-modules/locker
|
||||
---
|
||||
Player inventory storage, unique item limits, and locker hot-state behavior.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-building-2
|
||||
title: Organization
|
||||
to: /server-modules/organization
|
||||
---
|
||||
Membership, treasury, shared assets, fleet, and organization hot workflows.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-key-round
|
||||
title: Owned Storage
|
||||
to: /server-modules/owned-storage
|
||||
---
|
||||
Owner-scoped locker and vehicle unlock storage used by org-linked features.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-smartphone
|
||||
title: Phone
|
||||
to: /server-modules/phone
|
||||
---
|
||||
Contacts, message threads, and email state for in-game phone workflows.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-shopping-cart
|
||||
title: Store
|
||||
to: /server-modules/store
|
||||
---
|
||||
Checkout orchestration across pricing, grants, payment sources, and rollback.
|
||||
:::
|
||||
|
||||
:::u-page-card
|
||||
---
|
||||
icon: i-lucide-flag
|
||||
title: Task
|
||||
to: /server-modules/task
|
||||
---
|
||||
Task catalog, ownership, status transitions, defuse counters, and rewards.
|
||||
:::
|
||||
::
|
||||
127
docus/content/3.server-modules/1.actor.md
Normal file
127
docus/content/3.server-modules/1.actor.md
Normal file
@ -0,0 +1,127 @@
|
||||
# Actor Usage Guide
|
||||
|
||||
The actor module stores persistent player character data: identity, loadout,
|
||||
position, direction, stance, contact fields, state, holster status, rank, and
|
||||
organization.
|
||||
|
||||
## Storage Model
|
||||
|
||||
Actor data is persisted through SurrealDB by the server extension.
|
||||
|
||||
```json
|
||||
{
|
||||
"uid": "76561198000000000",
|
||||
"name": "Player Name",
|
||||
"loadout": {},
|
||||
"position": [1234.5, 6789.0, 0.0],
|
||||
"direction": 90.0,
|
||||
"stance": "STAND",
|
||||
"email": "0160000000@spearnet.mil",
|
||||
"phone_number": "0160000000",
|
||||
"state": "HEALTHY",
|
||||
"holster": true,
|
||||
"rank": null,
|
||||
"organization": "default"
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- `uid` is authoritative from the command argument and must be a 17-digit Steam
|
||||
UID.
|
||||
- `name` is optional, but cannot be empty when set and cannot exceed 50
|
||||
characters.
|
||||
- `position` must be three finite numbers when set.
|
||||
- `direction` must be in the `0.0 <= direction < 360.0` range.
|
||||
- `email` must contain `@` and end with `.mil` when set.
|
||||
- `phone_number` must start with `0160` and be 10 digits when set.
|
||||
- Empty `phone_number`, `email`, or `organization` fields are filled on create.
|
||||
|
||||
## Commands
|
||||
|
||||
All commands are called on the `actor` group.
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `actor:get` | `uid` | Actor JSON. If no actor exists, returns a default actor but does not persist it. |
|
||||
| `actor:create` | `uid`, `actor_json` | Persisted actor JSON. |
|
||||
| `actor:update` | `uid`, `patch_json` | Updated actor JSON. |
|
||||
| `actor:exists` | `uid` | `true` or `false`. |
|
||||
| `actor:delete` | `uid` | `OK`. |
|
||||
|
||||
## Create an Actor
|
||||
|
||||
The `uid` field in the JSON is overwritten with the command UID.
|
||||
|
||||
```sqf
|
||||
private _actor = createHashMapFromArray [
|
||||
["uid", getPlayerUID player],
|
||||
["name", name player],
|
||||
["loadout", getUnitLoadout player],
|
||||
["position", getPosATL player],
|
||||
["direction", getDir player],
|
||||
["stance", stance player],
|
||||
["email", ""],
|
||||
["phone_number", ""],
|
||||
["state", "HEALTHY"],
|
||||
["holster", true],
|
||||
["organization", "default"]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["actor:create", [
|
||||
getPlayerUID player,
|
||||
toJSON _actor
|
||||
]];
|
||||
```
|
||||
|
||||
## Update an Actor
|
||||
|
||||
`actor:update` accepts a JSON object containing only fields to change.
|
||||
|
||||
```sqf
|
||||
private _patch = createHashMapFromArray [
|
||||
["position", getPosATL player],
|
||||
["direction", getDir player],
|
||||
["stance", stance player],
|
||||
["loadout", getUnitLoadout player]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["actor:update", [
|
||||
getPlayerUID player,
|
||||
toJSON _patch
|
||||
]];
|
||||
```
|
||||
|
||||
Supported patch fields are `name`, `position`, `direction`, `stance`, `email`,
|
||||
`phone_number`, `state`, `holster`, `rank`, `organization`, and `loadout`.
|
||||
`uid` is ignored.
|
||||
|
||||
## Hot State
|
||||
|
||||
The `actor:hot:*` commands keep a runtime copy of actor data and write it back
|
||||
only when `actor:hot:save` runs.
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `actor:hot:init` | `uid` | Actor JSON from durable storage. |
|
||||
| `actor:hot:get` | `uid` | Actor JSON. |
|
||||
| `actor:hot:keys` | none | JSON array of hot actor UIDs. |
|
||||
| `actor:hot:override` | `uid`, `actor_json` | Actor JSON. |
|
||||
| `actor:hot:save` | `uid` | Current hot actor JSON and async durable save. |
|
||||
| `actor:hot:remove` | `uid` | `OK`. |
|
||||
|
||||
Use hot state for frequently updated session data such as position and loadout.
|
||||
Use durable commands for account creation and administrative changes.
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["actor:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Actor error: %1", _payload];
|
||||
};
|
||||
|
||||
private _actor = fromJSON _payload;
|
||||
```
|
||||
151
docus/content/3.server-modules/10.store.md
Normal file
151
docus/content/3.server-modules/10.store.md
Normal file
@ -0,0 +1,151 @@
|
||||
# Store Usage Guide
|
||||
|
||||
The store module processes checkout requests. It charges a payment source and
|
||||
grants purchased items to the player locker, virtual arsenal locker, and
|
||||
virtual garage unlocks.
|
||||
|
||||
## Server SQF Module
|
||||
|
||||
The server addon uses two long-lived module objects:
|
||||
|
||||
- `StorefrontStore` is the storefront workflow facade. It builds hydrate
|
||||
payloads, validates checkout requests, calls the Rust `store:checkout`
|
||||
command, syncs UI patches, and asks related module stores to save hot state.
|
||||
- `StoreCatalogService` scans configured item and vehicle categories, builds
|
||||
catalog responses, resolves checkout entries, and calculates authoritative
|
||||
prices.
|
||||
|
||||
Editor-placed store entities are initialized by `fnc_initStore` during store
|
||||
post-init. The initializer matches non-null mission namespace objects whose
|
||||
variable names contain `store` and sets `isStore = true`, following the same
|
||||
pattern used by garage entities.
|
||||
|
||||
## Checkout Model
|
||||
|
||||
`store:checkout` accepts one JSON context.
|
||||
|
||||
```json
|
||||
{
|
||||
"requesterUid": "76561198000000000",
|
||||
"requesterName": "Player Name",
|
||||
"orgId": "default",
|
||||
"requesterIsDefaultOrgCeo": false,
|
||||
"paymentMethod": "bank",
|
||||
"items": [
|
||||
{
|
||||
"classname": "arifle_MX_F",
|
||||
"category": "weapon",
|
||||
"priceValue": 500,
|
||||
"quantity": 1
|
||||
}
|
||||
],
|
||||
"vehicles": [
|
||||
{
|
||||
"classname": "B_Quadbike_01_F",
|
||||
"category": "cars",
|
||||
"priceValue": 1500
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- `requesterUid` is required.
|
||||
- At least one item or vehicle is required.
|
||||
- The checkout total must be greater than zero.
|
||||
- Item categories must be `item`, `attachment`, `weapon`, `magazine`, or
|
||||
`backpack`.
|
||||
- Vehicle categories must be `cars`, `armor`, `helis`, `planes`, `naval`, or
|
||||
`other`.
|
||||
- Payment method must be `cash`, `bank`, `org_funds`, or `credit_line`.
|
||||
- Player locker capacity cannot exceed 25 unique items after checkout.
|
||||
- Organization funds can only be charged by the org owner or the default org
|
||||
CEO flag.
|
||||
|
||||
## Command
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `store:checkout` | `checkout_json` | Checkout result JSON. |
|
||||
|
||||
## Result Model
|
||||
|
||||
```json
|
||||
{
|
||||
"chargedTotal": 2000.0,
|
||||
"paymentMethod": "bank",
|
||||
"message": "Checkout completed. $2,000 charged, 1 locker grant(s), 1 vehicle unlock(s).",
|
||||
"lockerGranted": [],
|
||||
"vehicleGranted": [],
|
||||
"lockerPatch": {},
|
||||
"vaPatch": {},
|
||||
"vgaragePatch": {},
|
||||
"bankPatch": {},
|
||||
"orgPatch": {},
|
||||
"orgTargetUids": []
|
||||
}
|
||||
```
|
||||
|
||||
Patch fields are intended for UI updates after checkout. The service commits
|
||||
all grants and payment changes together, and attempts rollback if a later write
|
||||
fails.
|
||||
|
||||
## Player Bank Checkout
|
||||
|
||||
```sqf
|
||||
private _item = createHashMapFromArray [
|
||||
["classname", "arifle_MX_F"],
|
||||
["category", "weapon"],
|
||||
["priceValue", 500],
|
||||
["quantity", 1]
|
||||
];
|
||||
|
||||
private _checkout = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["requesterName", name player],
|
||||
["orgId", "default"],
|
||||
["requesterIsDefaultOrgCeo", false],
|
||||
["paymentMethod", "bank"],
|
||||
["items", [_item]],
|
||||
["vehicles", []]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["store:checkout", [toJSON _checkout]];
|
||||
```
|
||||
|
||||
## Organization Funds Checkout
|
||||
|
||||
When `paymentMethod` is `org_funds`, vehicles are also added to the
|
||||
organization fleet patch.
|
||||
|
||||
```sqf
|
||||
private _vehicle = createHashMapFromArray [
|
||||
["classname", "B_Quadbike_01_F"],
|
||||
["category", "cars"],
|
||||
["priceValue", 1500]
|
||||
];
|
||||
|
||||
private _checkout = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["requesterName", name player],
|
||||
["orgId", _orgId],
|
||||
["requesterIsDefaultOrgCeo", false],
|
||||
["paymentMethod", "org_funds"],
|
||||
["items", []],
|
||||
["vehicles", [_vehicle]]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["store:checkout", [toJSON _checkout]];
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
hint format ["Checkout failed: %1", _payload];
|
||||
};
|
||||
|
||||
private _checkoutResult = fromJSON _payload;
|
||||
```
|
||||
289
docus/content/3.server-modules/11.task.md
Normal file
289
docus/content/3.server-modules/11.task.md
Normal file
@ -0,0 +1,289 @@
|
||||
# Task Usage Guide
|
||||
|
||||
The task module stores transient mission task metadata for active server or
|
||||
mission lifecycle workflows. SQF still owns Arma-only runtime state such as
|
||||
objects and participants.
|
||||
|
||||
The server addon at `arma/server/addons/task` also owns task execution:
|
||||
creating BIS tasks, registering task entities, tracking participants, binding
|
||||
task ownership, applying player/org rewards, and clearing task state when a
|
||||
task completes.
|
||||
|
||||
Runtime dependencies:
|
||||
|
||||
- `forge_server_extension`
|
||||
- `forge_server_common`
|
||||
- `forge_server_actor`
|
||||
- `forge_server_bank`
|
||||
- `forge_server_org`
|
||||
- `forge_client_notifications`
|
||||
|
||||
## Data Model
|
||||
|
||||
Catalog entries are flexible JSON objects. The service normalizes these fields
|
||||
when a catalog entry is inserted or ownership changes:
|
||||
|
||||
- `taskId`
|
||||
- `taskID`
|
||||
- `accepted`
|
||||
- `requesterUid`
|
||||
- `orgID`
|
||||
|
||||
Ownership context:
|
||||
|
||||
```json
|
||||
{
|
||||
"requesterUid": "76561198000000000",
|
||||
"orgId": "default"
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `task:reset` | none | `true`. |
|
||||
| `task:catalog:active` | none | Active catalog entry array JSON. |
|
||||
| `task:catalog:get` | `task_id` | Catalog entry JSON or `null`. |
|
||||
| `task:catalog:upsert` | `task_id`, `entry_json` | Stored catalog entry JSON. |
|
||||
| `task:catalog:delete` | `task_id` | `true`. |
|
||||
| `task:ownership:bind` | `task_id`, `ownership_json` | Ownership mutation result JSON. |
|
||||
| `task:ownership:release` | `task_id` | Ownership mutation result JSON. |
|
||||
| `task:ownership:accept` | `task_id`, `ownership_json` | Ownership mutation result JSON. |
|
||||
| `task:ownership:reward_context` | `task_id` | Reward context JSON. |
|
||||
| `task:status:set` | `task_id`, `status` | `true`. |
|
||||
| `task:status:get` | `task_id` | Status string JSON. |
|
||||
| `task:status:clear` | `task_id` | `true`. |
|
||||
| `task:defuse:increment` | `task_id` | New counter value JSON. |
|
||||
| `task:defuse:get` | `task_id` | Counter value JSON. |
|
||||
| `task:clear` | `task_id` | `true`. |
|
||||
|
||||
## Upsert a Catalog Entry
|
||||
|
||||
```sqf
|
||||
private _entry = createHashMapFromArray [
|
||||
["title", "Destroy Cache"],
|
||||
["description", "Destroy the enemy supply cache."],
|
||||
["reward", 1500]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["task:catalog:upsert", [
|
||||
"task-cache-1",
|
||||
toJSON _entry
|
||||
]];
|
||||
```
|
||||
|
||||
## Mark a Task Active
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["task:status:set", [
|
||||
"task-cache-1",
|
||||
"active"
|
||||
]];
|
||||
|
||||
private _active = "forge_server" callExtension ["task:catalog:active", []];
|
||||
```
|
||||
|
||||
Completed statuses `succeeded` and `failed` are also stored as completed status
|
||||
fallbacks. Clearing status removes active and completed state.
|
||||
|
||||
## Accept a Task
|
||||
|
||||
```sqf
|
||||
private _ownership = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["orgId", "default"]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["task:ownership:accept", [
|
||||
"task-cache-1",
|
||||
toJSON _ownership
|
||||
]];
|
||||
```
|
||||
|
||||
`task:ownership:accept` fails if the task is not active or another requester
|
||||
already accepted it.
|
||||
|
||||
## Rewards
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["task:ownership:reward_context", [
|
||||
"task-cache-1"
|
||||
]];
|
||||
|
||||
private _context = fromJSON (_result select 0);
|
||||
```
|
||||
|
||||
The reward context contains `requesterUid` and `orgId`.
|
||||
|
||||
## Server Task Flows
|
||||
|
||||
The task addon provides these server-owned task flows:
|
||||
|
||||
- `attack`
|
||||
- `defend`
|
||||
- `defuse`
|
||||
- `delivery`
|
||||
- `destroy`
|
||||
- `hostage`
|
||||
- `hvt`
|
||||
|
||||
Mission designers can create tasks in four ways:
|
||||
|
||||
- Eden modules for editor-authored tasks.
|
||||
- `forge_server_task_fnc_startTask` for script-authored tasks.
|
||||
- `forge_server_task_fnc_handler` for pre-registered entities with reputation
|
||||
gating and ownership binding. This path expects the BIS task and catalog
|
||||
entry to already exist if map-task and CAD visibility are required.
|
||||
- Direct task function calls for server-owned or mission-authored flows that
|
||||
intentionally fall back to the `default` org. This path expects the BIS task
|
||||
to already exist if map-task visibility is required.
|
||||
|
||||
The dynamic mission manager can also generate attack tasks from config. That is
|
||||
system-generated content rather than a hand-authored task creation path.
|
||||
|
||||
## CAD Compatibility
|
||||
|
||||
CAD hydrates assignable tasks from `TaskStore.getActiveTaskCatalog`. A task must
|
||||
have a catalog entry and active task status before CAD can show and assign it.
|
||||
|
||||
CAD-compatible creation paths:
|
||||
|
||||
- Eden modules: compatible because they delegate to
|
||||
`forge_server_task_fnc_startTask`.
|
||||
- `forge_server_task_fnc_startTask`: compatible because it registers the
|
||||
catalog entry, creates the BIS task, and dispatches through the handler.
|
||||
- Dynamic mission manager attack tasks: compatible because the mission manager
|
||||
uses `forge_server_task_fnc_startTask`.
|
||||
|
||||
Limited or incompatible paths:
|
||||
|
||||
- `forge_server_task_fnc_handler`: only compatible if a catalog entry was
|
||||
already registered elsewhere. The handler sets active status and ownership,
|
||||
but it does not create the BIS task shown in the map task tab or upsert the
|
||||
catalog entry.
|
||||
- Direct task function calls: not CAD-compatible by default. They bypass
|
||||
`startTask` and usually do not register the task catalog entry or active
|
||||
status that CAD hydrates from. They also only call `BIS_fnc_taskSetState` at
|
||||
completion/failure; they do not create the BIS task first.
|
||||
|
||||
## BIS Map Task Prerequisite
|
||||
|
||||
Only the Eden task modules and `forge_server_task_fnc_startTask` create the BIS
|
||||
task automatically through `BIS_fnc_taskCreate`.
|
||||
|
||||
If a mission uses `forge_server_task_fnc_handler` directly or calls a task flow
|
||||
function such as `forge_server_task_fnc_attack`, the mission must create a BIS
|
||||
task with the same task ID before the Forge task completes. Otherwise the
|
||||
success/failure `BIS_fnc_taskSetState` call has no visible map task to update.
|
||||
|
||||
That prerequisite can be satisfied with a vanilla Eden task creation module or
|
||||
a scripted `BIS_fnc_taskCreate` call. `forge_server_task_fnc_startTask` is the
|
||||
preferred Forge path because it handles BIS task creation, Forge catalog
|
||||
registration, entity registration, and handler dispatch together.
|
||||
|
||||
## Eden Modules
|
||||
|
||||
Eden task modules are the normal designer-facing path. Place the module,
|
||||
configure its attributes, and sync it to the relevant entities or grouping
|
||||
modules.
|
||||
|
||||
Available task modules:
|
||||
|
||||
- `FORGE_Module_Attack`: sync directly to target units or vehicles.
|
||||
- `FORGE_Module_Destroy`: sync directly to objects, vehicles, or units.
|
||||
- `FORGE_Module_Defuse`: sync to `FORGE_Module_Explosives` and optionally
|
||||
`FORGE_Module_Protected`.
|
||||
- `FORGE_Module_Delivery`: sync to `FORGE_Module_Cargo`; the cargo module syncs
|
||||
to cargo objects.
|
||||
- `FORGE_Module_Hostage`: sync to `FORGE_Module_Hostages` and
|
||||
`FORGE_Module_Shooters`.
|
||||
- `FORGE_Module_HVT`: sync directly to HVT units.
|
||||
- `FORGE_Module_Defend`: configure the defense marker and wave settings.
|
||||
|
||||
These modules delegate to `forge_server_task_fnc_startTask`.
|
||||
|
||||
## Scripted Start Task
|
||||
|
||||
Use `forge_server_task_fnc_startTask` when creating tasks from modules,
|
||||
mission scripts, or generated mission-manager content. It registers task
|
||||
entities, creates the BIS task, stores the catalog entry, then dispatches
|
||||
through `forge_server_task_fnc_handler`.
|
||||
|
||||
```sqf
|
||||
[
|
||||
"attack",
|
||||
"compound_attack_01",
|
||||
getPosATL leader1,
|
||||
"Attack: East Compound",
|
||||
"Eliminate all hostile forces.",
|
||||
createHashMapFromArray [["targets", [unit1, unit2, unit3]]],
|
||||
createHashMapFromArray [
|
||||
["limitFail", 0],
|
||||
["limitSuccess", 3],
|
||||
["funds", 50000],
|
||||
["ratingFail", -10],
|
||||
["ratingSuccess", 20],
|
||||
["timeLimit", 900]
|
||||
],
|
||||
0,
|
||||
getPlayerUID player,
|
||||
"script"
|
||||
] call forge_server_task_fnc_startTask;
|
||||
```
|
||||
|
||||
## Handler Calls
|
||||
|
||||
Use `forge_server_task_fnc_handler` directly when the task entities are already
|
||||
registered and you want reputation gating plus ownership binding. Create the
|
||||
BIS task and catalog entry separately if this task should appear in the map
|
||||
task tab or CAD:
|
||||
|
||||
```sqf
|
||||
[
|
||||
"delivery",
|
||||
["delivery_1", 1, 3, "delivery_zone", 250000, -75, 300, false, false, 900],
|
||||
250,
|
||||
getPlayerUID player
|
||||
] call forge_server_task_fnc_handler;
|
||||
```
|
||||
|
||||
## Direct Task Calls
|
||||
|
||||
Direct task function calls still work for mission-authored or server-owned
|
||||
tasks, but they do not provide a requester UID. Ownership falls back to the
|
||||
`default` org. Create the BIS task separately if this task should appear in the
|
||||
map task tab.
|
||||
|
||||
## Timer Semantics
|
||||
|
||||
Task time limits use `0` for no limit:
|
||||
|
||||
- attack `timeLimit`
|
||||
- destroy `timeLimit`
|
||||
- delivery `timeLimit`
|
||||
- hostage `timeLimit`
|
||||
- HVT `timeLimit`
|
||||
|
||||
Positive values are measured in seconds. Do not pass `-1` as a no-limit value;
|
||||
the task runtime treats any non-zero task time limit as active.
|
||||
|
||||
Defuse IED timers are different. `iedTimer` must be greater than `0`, because
|
||||
IEDs are expected to have an active countdown. The Eden defuse module defaults
|
||||
to `300` seconds.
|
||||
|
||||
## Defuse Counter
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["task:defuse:increment", ["task-cache-1"]];
|
||||
private _count = "forge_server" callExtension ["task:defuse:get", ["task-cache-1"]];
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Task error: %1", _payload];
|
||||
};
|
||||
```
|
||||
169
docus/content/3.server-modules/2.bank.md
Normal file
169
docus/content/3.server-modules/2.bank.md
Normal file
@ -0,0 +1,169 @@
|
||||
# Bank Usage Guide
|
||||
|
||||
The bank module stores player account balances, earnings, PINs, and transaction
|
||||
strings. The hot-state API also owns the active banking workflows used by the
|
||||
UI: deposit, withdraw, transfer, checkout charge, and PIN validation.
|
||||
|
||||
## Storage Model
|
||||
|
||||
Bank data is persisted through SurrealDB by the server extension.
|
||||
|
||||
```json
|
||||
{
|
||||
"uid": "76561198000000000",
|
||||
"name": "Player Name",
|
||||
"bank": 1000.0,
|
||||
"cash": 250.0,
|
||||
"earnings": 0.0,
|
||||
"pin": 1234,
|
||||
"transactions": []
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- `uid` is authoritative from the command argument.
|
||||
- `name` cannot be empty.
|
||||
- `bank` and `cash` cannot be negative.
|
||||
- `pin` must be a four-digit number.
|
||||
- Durable `bank:get` requires an existing bank account.
|
||||
|
||||
## Durable Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `bank:create` | `uid`, `bank_json` | Persisted bank JSON. |
|
||||
| `bank:get` | `uid` | Bank JSON. |
|
||||
| `bank:update` | `uid`, `patch_json` | Updated bank JSON. |
|
||||
| `bank:exists` | `uid` | `true` or `false`. |
|
||||
| `bank:delete` | `uid` | `OK`. |
|
||||
|
||||
## Create an Account
|
||||
|
||||
The `uid` field in the JSON is overwritten with the command UID.
|
||||
|
||||
```sqf
|
||||
private _account = createHashMapFromArray [
|
||||
["uid", getPlayerUID player],
|
||||
["name", name player],
|
||||
["bank", 0],
|
||||
["cash", 0],
|
||||
["earnings", 0],
|
||||
["pin", 1234],
|
||||
["transactions", []]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["bank:create", [
|
||||
getPlayerUID player,
|
||||
toJSON _account
|
||||
]];
|
||||
```
|
||||
|
||||
## Hot-State Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `bank:hot:init` | `uid` | Bank JSON loaded into hot state. |
|
||||
| `bank:hot:get` | `uid` | Bank JSON. |
|
||||
| `bank:hot:override` | `uid`, `bank_json` | Bank JSON. |
|
||||
| `bank:hot:patch` | `uid`, `patch_json` | `{ account, patch }`. |
|
||||
| `bank:hot:deposit` | `uid`, `amount`, `context_json` | `{ account, patch }`. |
|
||||
| `bank:hot:withdraw` | `uid`, `amount`, `context_json` | `{ account, patch }`. |
|
||||
| `bank:hot:deposit_earnings` | `uid`, `amount`, `context_json` | `{ account, patch }`. |
|
||||
| `bank:hot:transfer` | `source_uid`, `target_uid`, `amount`, `context_json` | Transfer result JSON. |
|
||||
| `bank:hot:charge_checkout` | `uid`, `amount`, `context_json` | `{ account, patch }`. |
|
||||
| `bank:hot:validate_pin` | `uid`, `pin`, `context_json` | `{}` on success. |
|
||||
| `bank:hot:save` | `uid` | Current hot bank JSON and async durable save. |
|
||||
| `bank:hot:remove` | `uid` | `OK`. |
|
||||
|
||||
Use hot-state commands for UI workflows. They return patch objects so the UI can
|
||||
update only changed fields.
|
||||
|
||||
## Deposit and Withdraw
|
||||
|
||||
ATM sessions require `atmAuthorized: true`. Full bank sessions can set
|
||||
`mode: "bank"`.
|
||||
|
||||
```sqf
|
||||
private _context = createHashMapFromArray [
|
||||
["mode", "atm"],
|
||||
["atmAuthorized", true]
|
||||
];
|
||||
|
||||
private _deposit = "forge_server" callExtension ["bank:hot:deposit", [
|
||||
getPlayerUID player,
|
||||
"100",
|
||||
toJSON _context
|
||||
]];
|
||||
|
||||
private _withdraw = "forge_server" callExtension ["bank:hot:withdraw", [
|
||||
getPlayerUID player,
|
||||
"50",
|
||||
toJSON _context
|
||||
]];
|
||||
```
|
||||
|
||||
## Transfer
|
||||
|
||||
Transfers are only available from the full bank interface. `fromField` can be
|
||||
`bank` or `cash`.
|
||||
|
||||
```sqf
|
||||
private _context = createHashMapFromArray [
|
||||
["mode", "bank"],
|
||||
["atmAuthorized", false],
|
||||
["fromField", "bank"]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["bank:hot:transfer", [
|
||||
getPlayerUID player,
|
||||
_targetUid,
|
||||
"250",
|
||||
toJSON _context
|
||||
]];
|
||||
```
|
||||
|
||||
## Checkout Charge
|
||||
|
||||
Checkout charging supports `sourceField: "cash"` or `sourceField: "bank"`.
|
||||
Set `commit` to `false` to preview the patch without saving.
|
||||
|
||||
```sqf
|
||||
private _context = createHashMapFromArray [
|
||||
["sourceField", "bank"],
|
||||
["commit", true]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["bank:hot:charge_checkout", [
|
||||
getPlayerUID player,
|
||||
"125",
|
||||
toJSON _context
|
||||
]];
|
||||
```
|
||||
|
||||
## PIN Validation
|
||||
|
||||
PIN entry is only valid in ATM mode.
|
||||
|
||||
```sqf
|
||||
private _context = createHashMapFromArray [["mode", "atm"]];
|
||||
|
||||
private _result = "forge_server" callExtension ["bank:hot:validate_pin", [
|
||||
getPlayerUID player,
|
||||
"1234",
|
||||
toJSON _context
|
||||
]];
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["bank:hot:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Bank error: %1", _payload];
|
||||
};
|
||||
|
||||
private _bank = fromJSON _payload;
|
||||
```
|
||||
183
docus/content/3.server-modules/3.cad.md
Normal file
183
docus/content/3.server-modules/3.cad.md
Normal file
@ -0,0 +1,183 @@
|
||||
# CAD Usage Guide
|
||||
|
||||
The CAD module stores transient operational state for dispatch activity,
|
||||
assignments, dispatch orders, support requests, group profiles, grouped views,
|
||||
and hydrated UI payloads. CAD state is in-memory and follows the active server
|
||||
or mission lifecycle.
|
||||
|
||||
## Data Model
|
||||
|
||||
Most CAD records are flexible JSON objects. The service normalizes important
|
||||
IDs and returns structured mutation results for higher-level workflows.
|
||||
|
||||
Common generated IDs:
|
||||
|
||||
- Orders: `cad-order:<sequence>`
|
||||
- Requests: `cad-request:<sequence>`
|
||||
- Assignments usually share a task ID or order ID.
|
||||
|
||||
## Commands
|
||||
|
||||
### Activity
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `cad:activity:append` | `activity_json` | `OK`. |
|
||||
| `cad:activity:recent` | `limit` | Recent activity array JSON. |
|
||||
|
||||
### Assignments
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `cad:assignments:list` | none | Assignment array JSON. |
|
||||
| `cad:assignments:assign` | `entry_id`, `assignment_json` | Assignment mutation result JSON. |
|
||||
| `cad:assignments:acknowledge` | `entry_id`, `patch_json` | Assignment mutation result JSON. |
|
||||
| `cad:assignments:decline` | `entry_id`, `patch_json` | Assignment mutation result JSON and removes assignment. |
|
||||
| `cad:assignments:upsert` | `entry_id`, `assignment_json` | `OK`. |
|
||||
| `cad:assignments:delete` | `entry_id` | `OK`. |
|
||||
|
||||
### Orders
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `cad:orders:list` | none | Order array JSON. |
|
||||
| `cad:orders:create` | `order_seed_json` | Dispatch order mutation result JSON. |
|
||||
| `cad:orders:create_from_context` | `context_json` | Dispatch order mutation result JSON. |
|
||||
| `cad:orders:close` | `entry_id` | Dispatch order mutation result JSON and removes order/assignment. |
|
||||
| `cad:orders:upsert` | `entry_id`, `order_json` | `OK`. |
|
||||
| `cad:orders:delete` | `entry_id` | `OK`. |
|
||||
|
||||
### Requests
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `cad:requests:list` | none | Request array JSON. |
|
||||
| `cad:requests:submit` | `request_json` | Request mutation result JSON. |
|
||||
| `cad:requests:submit_from_context` | `context_json` | Request mutation result JSON. |
|
||||
| `cad:requests:close` | `entry_id` | Request mutation result JSON and removes request. |
|
||||
| `cad:requests:upsert` | `entry_id`, `request_json` | `OK`. |
|
||||
| `cad:requests:delete` | `entry_id` | `OK`. |
|
||||
|
||||
### Profiles and Views
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `cad:profiles:list` | none | Profile array JSON. |
|
||||
| `cad:profiles:update_from_context` | `context_json` | Profile mutation result JSON. |
|
||||
| `cad:profiles:upsert` | `entry_id`, `profile_json` | `OK`. |
|
||||
| `cad:profiles:delete` | `entry_id` | `OK`. |
|
||||
| `cad:groups:build` | `groups_seed_json` | Group array JSON. |
|
||||
| `cad:view:hydrate` | `hydrate_seed_json` | Hydrated CAD payload JSON. |
|
||||
|
||||
## Submit a Support Request
|
||||
|
||||
```sqf
|
||||
private _fields = createHashMapFromArray [
|
||||
["pickup_location", "Grid 123456"],
|
||||
["precedence", "urgent"],
|
||||
["security", "secure"]
|
||||
];
|
||||
|
||||
private _context = createHashMapFromArray [
|
||||
["type", "medevac_9line"],
|
||||
["fields", _fields],
|
||||
["groupId", "alpha"],
|
||||
["groupCallsign", "Alpha 1-1"],
|
||||
["submittedByUid", getPlayerUID player],
|
||||
["submittedByName", name player],
|
||||
["priority", "emergency"],
|
||||
["position", getPosATL player],
|
||||
["createdAt", diag_tickTime]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["cad:requests:submit_from_context", [
|
||||
toJSON _context
|
||||
]];
|
||||
```
|
||||
|
||||
Supported priority values are `routine`, `priority`, and `emergency`. Unknown
|
||||
values normalize to `priority`.
|
||||
|
||||
## Create a Dispatch Order
|
||||
|
||||
```sqf
|
||||
private _context = createHashMapFromArray [
|
||||
["assigneeGroupId", "bravo"],
|
||||
["assigneeGroupCallsign", "Bravo 1-1"],
|
||||
["targetGroupId", "alpha"],
|
||||
["targetGroupCallsign", "Alpha 1-1"],
|
||||
["targetPosition", getPosATL player],
|
||||
["createdByUid", getPlayerUID player],
|
||||
["createdByName", name player],
|
||||
["requestId", "cad-request:1"],
|
||||
["requestType", "logreq"],
|
||||
["requestTitle", "LOGREQ | Alpha 1-1"],
|
||||
["requestSummary", "Ammo resupply requested"],
|
||||
["requestFields", createHashMap],
|
||||
["note", "Support Alpha 1-1 at current position."],
|
||||
["priority", "priority"],
|
||||
["createdAt", diag_tickTime]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["cad:orders:create_from_context", [
|
||||
toJSON _context
|
||||
]];
|
||||
```
|
||||
|
||||
## Assignment Workflow
|
||||
|
||||
```sqf
|
||||
private _assignment = createHashMapFromArray [
|
||||
["groupId", "bravo"],
|
||||
["assigneeGroupCallsign", "Bravo 1-1"],
|
||||
["assignedByUid", getPlayerUID player],
|
||||
["assignedByName", name player],
|
||||
["assignedAt", diag_tickTime],
|
||||
["state", "assigned"]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["cad:assignments:assign", [
|
||||
"task-123",
|
||||
toJSON _assignment
|
||||
]];
|
||||
|
||||
private _ack = createHashMapFromArray [
|
||||
["state", "acknowledged"],
|
||||
["acknowledgedByUid", getPlayerUID player],
|
||||
["acknowledgedAt", diag_tickTime]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["cad:assignments:acknowledge", [
|
||||
"task-123",
|
||||
toJSON _ack
|
||||
]];
|
||||
```
|
||||
|
||||
## Hydrate the CAD UI
|
||||
|
||||
```sqf
|
||||
private _session = createHashMapFromArray [
|
||||
["uid", getPlayerUID player],
|
||||
["orgId", "default"],
|
||||
["isDispatcher", true],
|
||||
["groupId", "alpha"],
|
||||
["isLeader", true]
|
||||
];
|
||||
|
||||
private _seed = createHashMapFromArray [
|
||||
["groups", _liveGroups],
|
||||
["activeTasks", _activeTasks],
|
||||
["session", _session]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["cad:view:hydrate", [toJSON _seed]];
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["CAD error: %1", _payload];
|
||||
};
|
||||
```
|
||||
77
docus/content/3.server-modules/4.economy.md
Normal file
77
docus/content/3.server-modules/4.economy.md
Normal file
@ -0,0 +1,77 @@
|
||||
# Economy Usage Guide
|
||||
|
||||
The economy server addon owns Arma-world service behavior for fuel, medical,
|
||||
and repair interactions. It does not own money state. Money mutations go
|
||||
through extension-backed bank and organization hot state before the world
|
||||
effect is applied.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- `forge_server_common` for logging, formatting, and player lookup.
|
||||
- `forge_server_bank` for personal medical billing.
|
||||
- `forge_server_org` for organization-funded services and medical fallback
|
||||
debt.
|
||||
- `forge_client_actor` and `forge_client_notifications` for targeted client
|
||||
responses.
|
||||
|
||||
## Fuel
|
||||
|
||||
Fuel is organization-funded.
|
||||
|
||||
When refueling stops, `fnc_initFEconomyStore.sqf` calculates the fuel delta and
|
||||
cost, charges the player's organization through `OrgStore chargeCheckout`, and
|
||||
syncs the organization patch to online members. If organization funds cannot
|
||||
cover the refuel, the vehicle is rolled back to the fuel level it had when the
|
||||
session started.
|
||||
|
||||
Garage UI refuel requests use the server `RefuelService` event. The fuel store
|
||||
calculates missing fuel from the vehicle config `fuelCapacity`, charges the
|
||||
player's organization, and fills the vehicle only after the organization charge
|
||||
succeeds.
|
||||
|
||||
## Repair
|
||||
|
||||
Repair is organization-funded.
|
||||
|
||||
Use the repair service event:
|
||||
|
||||
```sqf
|
||||
[QEGVAR(economy,RepairService), [_target, _unit, _cost]] call CBA_fnc_serverEvent;
|
||||
```
|
||||
|
||||
`_cost` is optional. Passing `-1` uses the configured service repair cost.
|
||||
The target is only repaired after the organization charge succeeds.
|
||||
|
||||
The client garage UI forwards selected nearby vehicle repair requests through
|
||||
the same event.
|
||||
|
||||
## Medical
|
||||
|
||||
Medical is player-funded first.
|
||||
|
||||
When a heal is requested, `fnc_initMEconomyStore.sqf` uses this billing order:
|
||||
|
||||
1. Charge the player's bank balance when it can cover the medical fee.
|
||||
2. Otherwise charge the player's cash when it can cover the fee.
|
||||
3. If neither personal balance can cover the fee, charge organization funds.
|
||||
4. When organization funds cover the fallback charge, record the same amount as
|
||||
debt on the player's organization credit line.
|
||||
|
||||
The heal only completes after one of those charges succeeds. If personal
|
||||
billing is unavailable, the heal does not fall back to organization funds
|
||||
because the server cannot verify that the player is unable to cover the fee.
|
||||
|
||||
## Medical Debt Repayment
|
||||
|
||||
Medical fallback debt uses the existing organization credit-line repayment
|
||||
flow. The organization treasury is reduced when the service is rendered, and
|
||||
the player's credit-line `amount_due` increases by the medical fee. When the
|
||||
player repays through the bank credit-line repayment action, player bank funds
|
||||
are moved back into the organization treasury.
|
||||
|
||||
## Hot-Cache Boundary
|
||||
|
||||
The economy addon should stay server-authoritative for world effects such as
|
||||
vehicle fuel, vehicle repair, healing, respawn placement, and death inventory
|
||||
movement. Bank and organization balances should continue to mutate through the
|
||||
extension-backed hot-cache services.
|
||||
212
docus/content/3.server-modules/5.garage.md
Normal file
212
docus/content/3.server-modules/5.garage.md
Normal file
@ -0,0 +1,212 @@
|
||||
# Garage Usage Guide
|
||||
|
||||
The garage module stores physical player vehicles. Each record keeps the
|
||||
vehicle classname, generated plate UUID, fuel, overall damage, and detailed hit
|
||||
point damage.
|
||||
|
||||
## Storage Model
|
||||
|
||||
Garage data is persisted through SurrealDB by the server extension.
|
||||
|
||||
```json
|
||||
{
|
||||
"plate-uuid": {
|
||||
"plate": "plate-uuid",
|
||||
"classname": "B_Quadbike_01_F",
|
||||
"fuel": 1.0,
|
||||
"damage": 0.0,
|
||||
"hit_points": {
|
||||
"names": ["hitengine"],
|
||||
"selections": ["engine_hitpoint"],
|
||||
"values": [0.0]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- A player garage can contain up to 5 vehicles.
|
||||
- `garage:add` generates a UUID plate automatically.
|
||||
- `fuel`, `damage`, and every hit point value must be between `0.0` and `1.0`.
|
||||
- `hit_points.names`, `hit_points.selections`, and `hit_points.values` must have
|
||||
the same length.
|
||||
- `garage:get`, `garage:patch`, and `garage:remove` require an existing garage.
|
||||
- `garage:add` creates an empty garage automatically when one does not exist.
|
||||
|
||||
## Commands
|
||||
|
||||
All commands are called on the `garage` group.
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `garage:create` | `uid` | Empty vehicle map as JSON. |
|
||||
| `garage:get` | `uid` | Vehicle map as JSON. |
|
||||
| `garage:add` | `uid`, `vehicle_json` | Updated vehicle map as JSON. |
|
||||
| `garage:update` | `uid`, `vehicles_json` | Replaced vehicle map as JSON. |
|
||||
| `garage:patch` | `uid`, `patch_json` | Updated vehicle map as JSON. |
|
||||
| `garage:remove` | `uid`, `remove_json` | Updated vehicle map as JSON. |
|
||||
| `garage:delete` | `uid` | `OK`. |
|
||||
| `garage:exists` | `uid` | `true` or `false`. |
|
||||
|
||||
## Error Handling
|
||||
|
||||
Every command returns a string payload. Always check for the `Error:` prefix
|
||||
before parsing JSON.
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["garage:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Garage error: %1", _payload];
|
||||
};
|
||||
|
||||
private _garage = fromJSON _payload;
|
||||
```
|
||||
|
||||
## Add a Vehicle
|
||||
|
||||
`garage:add` requires `classname`, `fuel`, `damage`, and `hit_points`.
|
||||
|
||||
```sqf
|
||||
private _hitPointData = getAllHitPointsDamage _vehicle;
|
||||
private _hitPoints = createHashMapFromArray [
|
||||
["names", _hitPointData select 0],
|
||||
["selections", _hitPointData select 1],
|
||||
["values", _hitPointData select 2]
|
||||
];
|
||||
|
||||
private _vehicleData = createHashMapFromArray [
|
||||
["classname", typeOf _vehicle],
|
||||
["fuel", fuel _vehicle],
|
||||
["damage", damage _vehicle],
|
||||
["hit_points", _hitPoints]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["garage:add", [
|
||||
getPlayerUID player,
|
||||
toJSON _vehicleData
|
||||
]];
|
||||
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
hint format ["Failed to store vehicle: %1", _payload];
|
||||
};
|
||||
|
||||
private _garage = fromJSON _payload;
|
||||
```
|
||||
|
||||
The returned value is a hash map keyed by generated plate. To find the newly
|
||||
stored vehicle, compare returned keys before and after the add, or search by
|
||||
classname if your workflow guarantees a unique pending vehicle.
|
||||
|
||||
```sqf
|
||||
private _storedPlate = "";
|
||||
{
|
||||
private _vehicleRecord = _garage get _x;
|
||||
if ((_vehicleRecord get "classname") == typeOf _vehicle) then {
|
||||
_storedPlate = _x;
|
||||
};
|
||||
} forEach keys _garage;
|
||||
```
|
||||
|
||||
## Patch a Vehicle
|
||||
|
||||
`garage:patch` updates selected fields for one plate. The `plate` field is
|
||||
required. `fuel`, `damage`, and `hit_points` are optional.
|
||||
|
||||
```sqf
|
||||
private _patch = createHashMapFromArray [
|
||||
["plate", _vehicle getVariable ["forge_garage_plate", ""]],
|
||||
["fuel", fuel _vehicle],
|
||||
["damage", damage _vehicle]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["garage:patch", [
|
||||
getPlayerUID player,
|
||||
toJSON _patch
|
||||
]];
|
||||
```
|
||||
|
||||
## Remove a Vehicle
|
||||
|
||||
`garage:remove` expects JSON with a `plate` field.
|
||||
|
||||
```sqf
|
||||
private _remove = createHashMapFromArray [
|
||||
["plate", _plate]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["garage:remove", [
|
||||
getPlayerUID player,
|
||||
toJSON _remove
|
||||
]];
|
||||
```
|
||||
|
||||
## Spawn a Stored Vehicle
|
||||
|
||||
```sqf
|
||||
fnc_spawnGarageVehicle = {
|
||||
params ["_plate"];
|
||||
|
||||
private _result = "forge_server" callExtension ["garage:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
hint format ["Failed to load garage: %1", _payload];
|
||||
objNull
|
||||
};
|
||||
|
||||
private _garage = fromJSON _payload;
|
||||
private _vehicleData = _garage getOrDefault [_plate, createHashMap];
|
||||
if (_vehicleData isEqualTo createHashMap) exitWith {
|
||||
hint "Vehicle plate was not found in your garage.";
|
||||
objNull
|
||||
};
|
||||
|
||||
private _vehicle = (_vehicleData get "classname") createVehicle (player getPos [10, getDir player]);
|
||||
_vehicle setFuel (_vehicleData getOrDefault ["fuel", 1]);
|
||||
_vehicle setDamage (_vehicleData getOrDefault ["damage", 0]);
|
||||
_vehicle setVariable ["forge_garage_plate", _plate, true];
|
||||
|
||||
private _hitPoints = _vehicleData getOrDefault ["hit_points", createHashMap];
|
||||
private _names = _hitPoints getOrDefault ["names", []];
|
||||
private _values = _hitPoints getOrDefault ["values", []];
|
||||
|
||||
{
|
||||
_vehicle setHitPointDamage [_x, _values select _forEachIndex];
|
||||
} forEach _names;
|
||||
|
||||
private _remove = createHashMapFromArray [["plate", _plate]];
|
||||
"forge_server" callExtension ["garage:remove", [getPlayerUID player, toJSON _remove]];
|
||||
|
||||
_vehicle
|
||||
};
|
||||
```
|
||||
|
||||
## Hot State
|
||||
|
||||
The `garage:hot:*` commands keep a runtime copy of a player's garage and write
|
||||
it back only when `garage:hot:save` runs.
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `garage:hot:init` | `uid` | Vehicle map as JSON. |
|
||||
| `garage:hot:get` | `uid` | Vehicle map as JSON. |
|
||||
| `garage:hot:override` | `uid`, `vehicles_json` | Vehicle map as JSON. |
|
||||
| `garage:hot:add` | `uid`, `vehicle_json` | Vehicle map as JSON. |
|
||||
| `garage:hot:remove_vehicle` | `uid`, `remove_json` | Vehicle map as JSON. |
|
||||
| `garage:hot:save` | `uid` | Current hot vehicle map as JSON. |
|
||||
| `garage:hot:remove` | `uid` | `OK`. |
|
||||
|
||||
Use hot state for session-heavy vehicle workflows. Use the durable commands for
|
||||
simple store/retrieve operations.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Store the generated plate on spawned vehicles with `setVariable`.
|
||||
- Use `garage:patch` for frequent fuel and damage syncs.
|
||||
- Use `garage:update` only when replacing the whole vehicle map intentionally.
|
||||
- Do not delete the world vehicle until `garage:add` succeeds.
|
||||
- Treat vehicle maps as hash maps keyed by plate, not arrays.
|
||||
203
docus/content/3.server-modules/6.locker.md
Normal file
203
docus/content/3.server-modules/6.locker.md
Normal file
@ -0,0 +1,203 @@
|
||||
# Locker Usage Guide
|
||||
|
||||
The locker module stores physical player inventory items by classname. It is
|
||||
separate from the virtual arsenal unlock module documented in
|
||||
[Owned Storage Usage Guide](/server-modules/owned-storage).
|
||||
|
||||
## Storage Model
|
||||
|
||||
Locker data is persisted through SurrealDB by the server extension.
|
||||
|
||||
```json
|
||||
{
|
||||
"arifle_MX_F": {
|
||||
"category": "weapon",
|
||||
"classname": "arifle_MX_F",
|
||||
"amount": 1
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- A locker can contain up to 25 unique classnames.
|
||||
- `category` and `classname` cannot be empty.
|
||||
- `amount` must be greater than `0`.
|
||||
- `locker:add` creates an empty locker automatically when one does not exist.
|
||||
- `locker:get`, `locker:patch`, and `locker:remove` require an existing locker.
|
||||
- `locker:remove` takes the classname directly, not a JSON object.
|
||||
|
||||
## Commands
|
||||
|
||||
All commands are called on the `locker` group.
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `locker:create` | `uid` | Empty item map as JSON. |
|
||||
| `locker:get` | `uid` | Item map as JSON. |
|
||||
| `locker:add` | `uid`, `item_json` | Updated item map as JSON. |
|
||||
| `locker:update` | `uid`, `items_json` | Replaced item map as JSON. |
|
||||
| `locker:patch` | `uid`, `patch_json` | Updated item map as JSON. |
|
||||
| `locker:remove` | `uid`, `classname` | Updated item map as JSON. |
|
||||
| `locker:delete` | `uid` | `OK`. |
|
||||
| `locker:exists` | `uid` | `true` or `false`. |
|
||||
|
||||
## Error Handling
|
||||
|
||||
Every command returns a string payload. Always check for the `Error:` prefix
|
||||
before parsing JSON.
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["locker:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Locker error: %1", _payload];
|
||||
};
|
||||
|
||||
private _locker = fromJSON _payload;
|
||||
```
|
||||
|
||||
## Add an Item
|
||||
|
||||
`locker:add` creates or overwrites one classname entry.
|
||||
|
||||
```sqf
|
||||
private _item = createHashMapFromArray [
|
||||
["category", "weapon"],
|
||||
["classname", "arifle_MX_F"],
|
||||
["amount", 1]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["locker:add", [
|
||||
getPlayerUID player,
|
||||
toJSON _item
|
||||
]];
|
||||
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
hint format ["Failed to store item: %1", _payload];
|
||||
};
|
||||
|
||||
private _locker = fromJSON _payload;
|
||||
```
|
||||
|
||||
## Patch an Amount
|
||||
|
||||
`locker:patch` currently patches the `amount` field for an existing classname.
|
||||
|
||||
```sqf
|
||||
private _patch = createHashMapFromArray [
|
||||
["classname", "arifle_MX_F"],
|
||||
["amount", 5]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["locker:patch", [
|
||||
getPlayerUID player,
|
||||
toJSON _patch
|
||||
]];
|
||||
```
|
||||
|
||||
## Remove an Item
|
||||
|
||||
`locker:remove` takes the classname as the second argument.
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["locker:remove", [
|
||||
getPlayerUID player,
|
||||
"arifle_MX_F"
|
||||
]];
|
||||
```
|
||||
|
||||
## Retrieve an Item
|
||||
|
||||
```sqf
|
||||
fnc_retrieveLockerItem = {
|
||||
params ["_classname"];
|
||||
|
||||
private _result = "forge_server" callExtension ["locker:get", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
hint format ["Failed to load locker: %1", _payload];
|
||||
false
|
||||
};
|
||||
|
||||
private _locker = fromJSON _payload;
|
||||
private _item = _locker getOrDefault [_classname, createHashMap];
|
||||
if (_item isEqualTo createHashMap) exitWith {
|
||||
hint "Item was not found in your locker.";
|
||||
false
|
||||
};
|
||||
|
||||
private _amount = _item getOrDefault ["amount", 0];
|
||||
if (_amount <= 0) exitWith {
|
||||
hint "Item is out of stock.";
|
||||
false
|
||||
};
|
||||
|
||||
if !(player canAdd _classname) exitWith {
|
||||
hint "Not enough inventory space.";
|
||||
false
|
||||
};
|
||||
|
||||
player addItem _classname;
|
||||
|
||||
if (_amount > 1) then {
|
||||
private _patch = createHashMapFromArray [
|
||||
["classname", _classname],
|
||||
["amount", _amount - 1]
|
||||
];
|
||||
"forge_server" callExtension ["locker:patch", [getPlayerUID player, toJSON _patch]];
|
||||
} else {
|
||||
"forge_server" callExtension ["locker:remove", [getPlayerUID player, _classname]];
|
||||
};
|
||||
|
||||
true
|
||||
};
|
||||
```
|
||||
|
||||
## Replace the Whole Locker
|
||||
|
||||
`locker:update` replaces the whole item map. Use it for explicit bulk syncs,
|
||||
not single-item changes.
|
||||
|
||||
```sqf
|
||||
private _items = createHashMapFromArray [
|
||||
["arifle_MX_F", createHashMapFromArray [
|
||||
["category", "weapon"],
|
||||
["classname", "arifle_MX_F"],
|
||||
["amount", 1]
|
||||
]]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["locker:update", [
|
||||
getPlayerUID player,
|
||||
toJSON _items
|
||||
]];
|
||||
```
|
||||
|
||||
## Hot State
|
||||
|
||||
The `locker:hot:*` commands keep a runtime copy of a player's locker and write
|
||||
it back only when `locker:hot:save` runs.
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `locker:hot:init` | `uid` | Item map as JSON. |
|
||||
| `locker:hot:get` | `uid` | Item map as JSON. |
|
||||
| `locker:hot:override` | `uid`, `items_json` | Item map as JSON. |
|
||||
| `locker:hot:save` | `uid` | Current hot item map as JSON. |
|
||||
| `locker:hot:remove` | `uid` | `OK`. |
|
||||
|
||||
Use hot state for session-heavy locker workflows. Use the durable commands for
|
||||
simple item deposits and withdrawals.
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Keep categories normalized, for example `weapon`, `magazine`, `item`, or
|
||||
`backpack`.
|
||||
- Use `locker:patch` for quantity changes.
|
||||
- Use `locker:remove` when quantity reaches zero.
|
||||
- Treat the locker response as a hash map keyed by classname.
|
||||
- Check capacity before bulk operations that may exceed 25 unique items.
|
||||
232
docus/content/3.server-modules/7.organization.md
Normal file
232
docus/content/3.server-modules/7.organization.md
Normal file
@ -0,0 +1,232 @@
|
||||
# Organization Usage Guide
|
||||
|
||||
The organization module stores organization records, members, assets, fleet
|
||||
entries, and credit lines. Durable commands manage persisted records directly.
|
||||
Hot-state commands support the active organization UI workflows.
|
||||
|
||||
## Storage Model
|
||||
|
||||
Core organization:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "default",
|
||||
"owner": "server",
|
||||
"name": "Default Organization",
|
||||
"funds": 0.0,
|
||||
"reputation": 0,
|
||||
"credit_lines": {}
|
||||
}
|
||||
```
|
||||
|
||||
Hot organization:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "default",
|
||||
"owner": "server",
|
||||
"name": "Default Organization",
|
||||
"funds": 0.0,
|
||||
"reputation": 0,
|
||||
"credit_lines": {},
|
||||
"assets": {},
|
||||
"fleet": {},
|
||||
"members": {},
|
||||
"pending_invites": {}
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- `id` must be non-empty and contain only alphanumeric characters or `_`.
|
||||
- `owner` must be `server` or a 17-digit Steam UID.
|
||||
- `name` cannot be empty, cannot exceed 100 characters, and cannot contain
|
||||
control characters.
|
||||
- `funds`, reputation, and credit line amounts cannot be negative.
|
||||
- Player registration is rejected when the player already belongs to a
|
||||
non-default organization.
|
||||
|
||||
## Durable Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `org:create` | `org_id`, `org_json` | Organization JSON. |
|
||||
| `org:get` | `org_id` | Organization JSON. |
|
||||
| `org:update` | `org_id`, `patch_json` | Updated organization JSON. |
|
||||
| `org:exists` | `org_id` | `true` or `false`. |
|
||||
| `org:delete` | `org_id` | `OK`. |
|
||||
| `org:assets:get` | `org_id` | Asset map JSON. |
|
||||
| `org:assets:update` | `org_id`, `assets_json` | Updated asset map JSON. |
|
||||
| `org:fleet:get` | `org_id` | Fleet map JSON. |
|
||||
| `org:fleet:update` | `org_id`, `fleet_json` | Updated fleet map JSON. |
|
||||
| `org:members:get` | `org_id` | Member array JSON. |
|
||||
| `org:members:add` | `org_id`, `member_uid` | `OK`. |
|
||||
| `org:members:remove` | `org_id`, `member_uid` | `OK`. |
|
||||
|
||||
## Create an Organization
|
||||
|
||||
The command key is authoritative for `id`.
|
||||
|
||||
```sqf
|
||||
private _org = createHashMapFromArray [
|
||||
["id", _orgId],
|
||||
["owner", getPlayerUID player],
|
||||
["name", "Spearnet Logistics"],
|
||||
["funds", 0],
|
||||
["reputation", 0],
|
||||
["credit_lines", createHashMap]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["org:create", [
|
||||
_orgId,
|
||||
toJSON _org
|
||||
]];
|
||||
```
|
||||
|
||||
## Update Organization Funds
|
||||
|
||||
```sqf
|
||||
private _patch = createHashMapFromArray [
|
||||
["funds", 5000],
|
||||
["reputation", 10]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["org:update", [
|
||||
_orgId,
|
||||
toJSON _patch
|
||||
]];
|
||||
```
|
||||
|
||||
Supported durable patch fields are `id`, `owner`, `name`, `funds`,
|
||||
`reputation`, and `credit_lines`.
|
||||
|
||||
## Assets and Fleet
|
||||
|
||||
Assets are grouped by category, then classname.
|
||||
|
||||
```sqf
|
||||
private _assets = createHashMapFromArray [
|
||||
["ammo", createHashMapFromArray [
|
||||
["ACE_30Rnd_65x39_caseless_mag", createHashMapFromArray [
|
||||
["classname", "ACE_30Rnd_65x39_caseless_mag"],
|
||||
["type", "ammo"],
|
||||
["quantity", 20]
|
||||
]]
|
||||
]]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["org:assets:update", [_orgId, toJSON _assets]];
|
||||
```
|
||||
|
||||
Fleet is keyed by an internal fleet entry ID.
|
||||
|
||||
```sqf
|
||||
private _fleet = createHashMapFromArray [
|
||||
["B_Truck_01_transport_F_0", createHashMapFromArray [
|
||||
["classname", "B_Truck_01_transport_F"],
|
||||
["name", "Transport Truck"],
|
||||
["type", "cars"],
|
||||
["status", "Ready"],
|
||||
["damage", "0%"]
|
||||
]]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["org:fleet:update", [_orgId, toJSON _fleet]];
|
||||
```
|
||||
|
||||
## Hot-State Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `org:hot:init` | `org_id` | Hot organization JSON. |
|
||||
| `org:hot:get` | `org_id` | Hot organization JSON. |
|
||||
| `org:hot:override` | `org_id`, `hot_org_json` | Hot organization JSON. |
|
||||
| `org:hot:ensure_member` | `context_json` | Hot organization JSON. |
|
||||
| `org:hot:member_invites` | `member_uid` | Invite array JSON. |
|
||||
| `org:hot:register` | `context_json` | Register result JSON. |
|
||||
| `org:hot:invite_member` | `context_json` | Invite result JSON. |
|
||||
| `org:hot:accept_invite` | `context_json` | Invite decision result JSON. |
|
||||
| `org:hot:decline_invite` | `context_json` | Invite decision result JSON. |
|
||||
| `org:hot:assign_credit_line` | `context_json` | Mutation result JSON. |
|
||||
| `org:hot:repay_credit_line` | `context_json` | Repayment result JSON. |
|
||||
| `org:hot:charge_checkout` | `context_json` | Mutation result JSON. |
|
||||
| `org:hot:add_assets` | `context_json`, `assets_json` | Mutation result JSON. |
|
||||
| `org:hot:add_fleet` | `context_json`, `fleet_json` | Mutation result JSON. |
|
||||
| `org:hot:leave` | `context_json` | Leave result JSON. |
|
||||
| `org:hot:disband` | `context_json` | Disband result JSON. |
|
||||
| `org:hot:save` | `org_id` | Current hot organization JSON and async durable save. |
|
||||
| `org:hot:remove` | `org_id` | `OK`. |
|
||||
|
||||
## Register from UI Context
|
||||
|
||||
```sqf
|
||||
private _context = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["requesterName", name player],
|
||||
["orgId", _orgId],
|
||||
["orgName", "Spearnet Logistics"],
|
||||
["existingOrgId", "default"]
|
||||
];
|
||||
|
||||
private _result = "forge_server" callExtension ["org:hot:register", [toJSON _context]];
|
||||
```
|
||||
|
||||
## Invite and Accept
|
||||
|
||||
```sqf
|
||||
private _invite = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["requesterName", name player],
|
||||
["orgId", _orgId],
|
||||
["requesterIsDefaultOrgCeo", false],
|
||||
["targetUid", _targetUid],
|
||||
["targetName", _targetName],
|
||||
["targetOrgId", "default"]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["org:hot:invite_member", [toJSON _invite]];
|
||||
|
||||
private _decision = createHashMapFromArray [
|
||||
["requesterUid", _targetUid],
|
||||
["requesterName", _targetName],
|
||||
["orgId", _orgId],
|
||||
["existingOrgId", "default"]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["org:hot:accept_invite", [toJSON _decision]];
|
||||
```
|
||||
|
||||
## Credit Line Checkout
|
||||
|
||||
```sqf
|
||||
private _credit = createHashMapFromArray [
|
||||
["requesterUid", getPlayerUID player],
|
||||
["orgId", _orgId],
|
||||
["requesterIsDefaultOrgCeo", false],
|
||||
["memberUid", _memberUid],
|
||||
["memberName", _memberName],
|
||||
["amount", 1000]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["org:hot:assign_credit_line", [toJSON _credit]];
|
||||
|
||||
private _charge = createHashMapFromArray [
|
||||
["requesterUid", _memberUid],
|
||||
["orgId", _orgId],
|
||||
["requesterIsDefaultOrgCeo", false],
|
||||
["source", "credit_line"],
|
||||
["amount", 250],
|
||||
["commit", true]
|
||||
];
|
||||
|
||||
"forge_server" callExtension ["org:hot:charge_checkout", [toJSON _charge]];
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Organization error: %1", _payload];
|
||||
};
|
||||
```
|
||||
158
docus/content/3.server-modules/8.owned-storage.md
Normal file
158
docus/content/3.server-modules/8.owned-storage.md
Normal file
@ -0,0 +1,158 @@
|
||||
# Owned Storage Usage Guide
|
||||
|
||||
Owned storage covers the `owned:locker` and `owned:garage` extension command
|
||||
groups. These modules store unlock lists rather than physical item or vehicle
|
||||
instances.
|
||||
|
||||
Use these modules for virtual arsenal and virtual garage unlocks. Use
|
||||
[Locker Usage Guide](/server-modules/locker) and
|
||||
[Garage Usage Guide](/server-modules/garage) for physical inventory and stored
|
||||
vehicle instances.
|
||||
|
||||
## Owned Locker Model
|
||||
|
||||
```json
|
||||
{
|
||||
"items": ["FirstAidKit"],
|
||||
"weapons": ["arifle_MX_F"],
|
||||
"magazines": ["30Rnd_65x39_caseless_black_mag"],
|
||||
"backpacks": ["B_AssaultPack_rgr"]
|
||||
}
|
||||
```
|
||||
|
||||
Supported owned locker categories:
|
||||
|
||||
- `items`
|
||||
- `weapons`
|
||||
- `magazines`
|
||||
- `backpacks`
|
||||
|
||||
New owned lockers are created with default unlocks from the Rust model.
|
||||
|
||||
## Owned Garage Model
|
||||
|
||||
```json
|
||||
{
|
||||
"cars": ["B_Quadbike_01_F"],
|
||||
"armor": [],
|
||||
"helis": [],
|
||||
"planes": [],
|
||||
"naval": [],
|
||||
"other": []
|
||||
}
|
||||
```
|
||||
|
||||
Supported owned garage categories:
|
||||
|
||||
- `cars`
|
||||
- `armor`
|
||||
- `helis`
|
||||
- `planes`
|
||||
- `naval`
|
||||
- `other`
|
||||
|
||||
The durable `owned:garage:remove` command currently accepts `heli` for the
|
||||
helicopter category. Add, get, and hot remove accept `helis`.
|
||||
|
||||
New owned garages are created with default unlocks from the Rust model.
|
||||
|
||||
## Owned Locker Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `owned:locker:create` | `uid` | Full owned locker JSON. |
|
||||
| `owned:locker:fetch` | `uid` | Full owned locker JSON. |
|
||||
| `owned:locker:get` | `uid`, `category` | Category classname array JSON. |
|
||||
| `owned:locker:add` | `uid`, `category`, `classnames_json` | Updated category array JSON. |
|
||||
| `owned:locker:remove` | `uid`, `category`, `classname` | Updated category array JSON. |
|
||||
| `owned:locker:delete` | `uid` | `OK`. |
|
||||
| `owned:locker:exists` | `uid` | `true` or `false`. |
|
||||
|
||||
## Owned Garage Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `owned:garage:create` | `uid` | Full owned garage JSON. |
|
||||
| `owned:garage:fetch` | `uid` | Full owned garage JSON. |
|
||||
| `owned:garage:get` | `uid`, `category` | Category classname array JSON. |
|
||||
| `owned:garage:add` | `uid`, `category`, `classnames_json` | Updated category array JSON. |
|
||||
| `owned:garage:remove` | `uid`, `category`, `classname` | Updated category array JSON. |
|
||||
| `owned:garage:delete` | `uid` | `OK`. |
|
||||
| `owned:garage:exists` | `uid` | `true` or `false`. |
|
||||
|
||||
## Add Virtual Arsenal Unlocks
|
||||
|
||||
```sqf
|
||||
private _classes = ["arifle_MX_F", "hgun_P07_F"];
|
||||
|
||||
private _result = "forge_server" callExtension ["owned:locker:add", [
|
||||
getPlayerUID player,
|
||||
"weapons",
|
||||
toJSON _classes
|
||||
]];
|
||||
```
|
||||
|
||||
## Add Virtual Garage Unlocks
|
||||
|
||||
```sqf
|
||||
private _classes = ["B_Quadbike_01_F", "B_MRAP_01_F"];
|
||||
|
||||
private _result = "forge_server" callExtension ["owned:garage:add", [
|
||||
getPlayerUID player,
|
||||
"cars",
|
||||
toJSON _classes
|
||||
]];
|
||||
```
|
||||
|
||||
## Remove an Unlock
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["owned:locker:remove", [
|
||||
getPlayerUID player,
|
||||
"weapons",
|
||||
"arifle_MX_F"
|
||||
]];
|
||||
|
||||
"forge_server" callExtension ["owned:garage:remove", [
|
||||
getPlayerUID player,
|
||||
"cars",
|
||||
"B_Quadbike_01_F"
|
||||
]];
|
||||
```
|
||||
|
||||
## Hot-State Commands
|
||||
|
||||
Both owned storage modules support hot state.
|
||||
|
||||
Owned locker:
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `owned:locker:hot:init` | `uid` | Full owned locker JSON. |
|
||||
| `owned:locker:hot:fetch` | `uid` | Full owned locker JSON. |
|
||||
| `owned:locker:hot:get` | `uid`, `category` | Category array JSON. |
|
||||
| `owned:locker:hot:override` | `uid`, `locker_json` | Full owned locker JSON. |
|
||||
| `owned:locker:hot:save` | `uid` | Current hot owned locker JSON and async durable save. |
|
||||
| `owned:locker:hot:remove` | `uid` | `OK`. |
|
||||
|
||||
Owned garage:
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `owned:garage:hot:init` | `uid` | Full owned garage JSON. |
|
||||
| `owned:garage:hot:fetch` | `uid` | Full owned garage JSON. |
|
||||
| `owned:garage:hot:get` | `uid`, `category` | Category array JSON. |
|
||||
| `owned:garage:hot:override` | `uid`, `garage_json` | Full owned garage JSON. |
|
||||
| `owned:garage:hot:add` | `uid`, `category`, `classnames_json` | Updated category array JSON. |
|
||||
| `owned:garage:hot:remove_item` | `uid`, `category`, `classname` | Updated category array JSON. |
|
||||
| `owned:garage:hot:save` | `uid` | Current hot owned garage JSON and async durable save. |
|
||||
| `owned:garage:hot:remove` | `uid` | `OK`. |
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _payload = _result select 0;
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Owned storage error: %1", _payload];
|
||||
};
|
||||
```
|
||||
136
docus/content/3.server-modules/9.phone.md
Normal file
136
docus/content/3.server-modules/9.phone.md
Normal file
@ -0,0 +1,136 @@
|
||||
# Phone Usage Guide
|
||||
|
||||
The phone module stores contacts, messages, and emails for each UID. It is a
|
||||
server-extension state module backed by SurrealDB.
|
||||
|
||||
## Storage Model
|
||||
|
||||
```json
|
||||
{
|
||||
"contacts": ["76561198000000000", "field_commander"],
|
||||
"messages": [
|
||||
{
|
||||
"id": "phone-message:sender:receiver:1",
|
||||
"from": "sender",
|
||||
"to": "receiver",
|
||||
"message": "Text body",
|
||||
"timestamp": 123.45,
|
||||
"read": false
|
||||
}
|
||||
],
|
||||
"emails": [
|
||||
{
|
||||
"id": "phone-email:sender:receiver:2",
|
||||
"from": "sender",
|
||||
"to": "receiver",
|
||||
"subject": "Subject",
|
||||
"body": "Email body",
|
||||
"timestamp": 123.45,
|
||||
"read": false
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Rules validated by the Rust service:
|
||||
|
||||
- UID arguments cannot be empty.
|
||||
- Message and email bodies cannot be empty.
|
||||
- Empty email subjects become `No subject`.
|
||||
- Player messages and emails cannot target `field_commander`.
|
||||
- `field_commander` can send messages or emails to players.
|
||||
- Deleting a message or email removes it only from the requesting UID's index.
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Arguments | Returns |
|
||||
| --- | --- | --- |
|
||||
| `phone:init` | `uid` | Full phone payload. |
|
||||
| `phone:contacts:list` | `uid` | Contact UID array. |
|
||||
| `phone:contacts:add` | `uid`, `contact_uid` | `true` or `false`. |
|
||||
| `phone:contacts:remove` | `uid`, `contact_uid` | `true` or `false`. |
|
||||
| `phone:messages:list` | `uid` | Message array. |
|
||||
| `phone:messages:thread` | `uid`, `other_uid` | Message array for both participants. |
|
||||
| `phone:messages:send` | `from_uid`, `to_uid`, `message`, `timestamp` | Message JSON. |
|
||||
| `phone:messages:mark_read` | `uid`, `message_id` | `true` or `false`. |
|
||||
| `phone:messages:delete` | `uid`, `message_id` | `true` or `false`. |
|
||||
| `phone:emails:list` | `uid` | Email array. |
|
||||
| `phone:emails:send` | `from_uid`, `to_uid`, `subject`, `body`, `timestamp` | Email JSON. |
|
||||
| `phone:emails:mark_read` | `uid`, `email_id` | `true` or `false`. |
|
||||
| `phone:emails:delete` | `uid`, `email_id` | `true` or `false`. |
|
||||
| `phone:remove` | `uid` | `OK`. |
|
||||
|
||||
## Initialize Phone State
|
||||
|
||||
`phone:init` creates phone state if needed and seeds self-contact plus
|
||||
`field_commander`.
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["phone:init", [getPlayerUID player]];
|
||||
private _payload = _result select 0;
|
||||
|
||||
if (_payload find "Error:" == 0) exitWith {
|
||||
systemChat format ["Phone init failed: %1", _payload];
|
||||
};
|
||||
|
||||
private _phone = fromJSON _payload;
|
||||
```
|
||||
|
||||
## Send a Message
|
||||
|
||||
```sqf
|
||||
private _timestamp = str diag_tickTime;
|
||||
|
||||
private _result = "forge_server" callExtension ["phone:messages:send", [
|
||||
getPlayerUID player,
|
||||
_targetUid,
|
||||
"Move to checkpoint Alpha.",
|
||||
_timestamp
|
||||
]];
|
||||
```
|
||||
|
||||
## Read a Conversation
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["phone:messages:thread", [
|
||||
getPlayerUID player,
|
||||
_otherUid
|
||||
]];
|
||||
|
||||
private _messages = fromJSON (_result select 0);
|
||||
```
|
||||
|
||||
## Send an Email
|
||||
|
||||
```sqf
|
||||
private _result = "forge_server" callExtension ["phone:emails:send", [
|
||||
getPlayerUID player,
|
||||
_targetUid,
|
||||
"Supply Request",
|
||||
"Requesting resupply at grid 123456.",
|
||||
str diag_tickTime
|
||||
]];
|
||||
```
|
||||
|
||||
## Mark and Delete Records
|
||||
|
||||
```sqf
|
||||
"forge_server" callExtension ["phone:messages:mark_read", [
|
||||
getPlayerUID player,
|
||||
_messageId
|
||||
]];
|
||||
|
||||
"forge_server" callExtension ["phone:emails:delete", [
|
||||
getPlayerUID player,
|
||||
_emailId
|
||||
]];
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
```sqf
|
||||
private _payload = (_result select 0);
|
||||
if (_payload find "Error:" == 0) then {
|
||||
systemChat format ["Phone error: %1", _payload];
|
||||
};
|
||||
```
|
||||
2
docus/content/4.client-addons/.navigation.yml
Normal file
2
docus/content/4.client-addons/.navigation.yml
Normal file
@ -0,0 +1,2 @@
|
||||
title: Client Addons
|
||||
icon: i-lucide-monitor-smartphone
|
||||
125
docus/content/4.client-addons/0.index.md
Normal file
125
docus/content/4.client-addons/0.index.md
Normal file
@ -0,0 +1,125 @@
|
||||
# Client Usage Guide
|
||||
|
||||
Forge Client contains the Arma client-side addons that open player interfaces,
|
||||
handle browser events, cache client-visible state, and forward authoritative
|
||||
requests to the server addons.
|
||||
|
||||
Use this guide as the entry point for client-side integration. Domain data,
|
||||
validation, persistence, rewards, ownership, and checkout behavior remain
|
||||
server-side responsibilities.
|
||||
|
||||
## Client Responsibilities
|
||||
|
||||
- Open Arma displays and `CT_WEBBROWSER` controls.
|
||||
- Load browser UI assets from each addon's `ui/_site` folder.
|
||||
- Receive browser alerts through `JSDialog` handlers.
|
||||
- Translate browser events into local actions or CBA server events.
|
||||
- Cache display state in client repositories.
|
||||
- Push server responses back into browser UIs with `ExecJS`.
|
||||
- Provide local-only utility state where the feature is intentionally local.
|
||||
|
||||
## Authoritative Boundaries
|
||||
|
||||
Client repositories are view state. They are useful for rendering, local UI
|
||||
decisions, and short-lived session behavior, but they should not be treated as
|
||||
durable state.
|
||||
|
||||
Authoritative state lives in:
|
||||
|
||||
- server SQF addons for mission and player workflow ownership
|
||||
- the `forge_server` extension for durable and hot-state domain logic
|
||||
- SurrealDB where the extension persists durable domain records
|
||||
|
||||
## Common Runtime Flow
|
||||
|
||||
Most browser-backed client addons follow this shape:
|
||||
|
||||
1. The addon creates a display, finds a browser control, and registers a
|
||||
`JSDialog` event handler.
|
||||
2. The browser loads an HTML entrypoint from `ui/_site`.
|
||||
3. The browser sends JSON alerts with an `event` name and `data` payload.
|
||||
4. `fnc_handleUIEvents.sqf` parses the alert and routes the event.
|
||||
5. A bridge object or repository sends a CBA server event when server data is
|
||||
needed.
|
||||
6. Server responses are caught in `XEH_postInitClient.sqf`.
|
||||
7. The bridge sends browser update events back through `ExecJS`.
|
||||
|
||||
Browser alert payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "module::action",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Open UI Entry Points
|
||||
|
||||
| UI | Entry point |
|
||||
| --- | --- |
|
||||
| Actor menu | `call forge_client_actor_fnc_openUI;` |
|
||||
| Bank | `call forge_client_bank_fnc_openUI;` |
|
||||
| ATM | `[true] call forge_client_bank_fnc_openUI;` |
|
||||
| CAD | `call forge_client_cad_fnc_openUI;` |
|
||||
| Garage | `call forge_client_garage_fnc_openUI;` |
|
||||
| Virtual garage | `call forge_client_garage_fnc_openVG;` |
|
||||
| Organization portal | `call forge_client_org_fnc_openUI;` |
|
||||
| Phone | `call forge_client_phone_fnc_openUI;` |
|
||||
| Store | `call forge_client_store_fnc_openUI;` |
|
||||
|
||||
Notifications are normally opened during client initialization and then updated
|
||||
through the notification event/service.
|
||||
|
||||
## Addon Guides
|
||||
|
||||
- [Client Main Usage Guide](/client-addons/main)
|
||||
- [Client Common Usage Guide](/client-addons/common)
|
||||
- [Client Actor Usage Guide](/client-addons/actor)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client CAD Usage Guide](/client-addons/cad)
|
||||
- [Client Garage Usage Guide](/client-addons/garage)
|
||||
- [Client Locker Usage Guide](/client-addons/locker)
|
||||
- [Client Notifications Usage Guide](/client-addons/notifications)
|
||||
- [Client Organization Usage Guide](/client-addons/organization)
|
||||
- [Client Phone Usage Guide](/client-addons/phone)
|
||||
- [Client Store Usage Guide](/client-addons/store)
|
||||
|
||||
## Extension Calls
|
||||
|
||||
Client addons should usually call server SQF events, not the `forge_server`
|
||||
extension directly. The server addon owns validation context and converts the
|
||||
request into extension commands.
|
||||
|
||||
Example:
|
||||
|
||||
```sqf
|
||||
[SRPC(bank,requestDeposit), [getPlayerUID player, 100]] call CFUNC(serverEvent);
|
||||
```
|
||||
|
||||
Direct extension calls from client code bypass server authorization boundaries
|
||||
and should be avoided.
|
||||
|
||||
## Browser Bridge Notes
|
||||
|
||||
`forge_client_common_fnc_initWebUIBridge` provides reusable bridge and screen
|
||||
objects for newer browser UIs. It queues outbound events until a browser screen
|
||||
is ready, then delivers payloads through:
|
||||
|
||||
```sqf
|
||||
_control ctrlWebBrowserAction ["ExecJS", format ["ForgeBridge.receive(%1)", _json]];
|
||||
```
|
||||
|
||||
Feature addons still own their event names, request payloads, and response
|
||||
mapping.
|
||||
|
||||
## Development Checklist
|
||||
|
||||
- Keep feature-specific behavior in the owning addon.
|
||||
- Send authoritative changes to the server addon.
|
||||
- Use namespaced browser events such as `bank::deposit::request`.
|
||||
- Treat `profileNamespace` as local player preference or utility state only.
|
||||
- Make browser-ready events request the current server state before rendering
|
||||
stale data.
|
||||
- Queue or ignore bridge responses when the display is closed.
|
||||
- Keep mission object setup on the mission/server side and client display logic
|
||||
on the client side.
|
||||
48
docus/content/4.client-addons/1.main.md
Normal file
48
docus/content/4.client-addons/1.main.md
Normal file
@ -0,0 +1,48 @@
|
||||
# Client Main Usage Guide
|
||||
|
||||
The client `main` addon provides the shared mod identity, version metadata,
|
||||
CBA settings, and macro foundation used by the Forge client addons.
|
||||
|
||||
## Purpose
|
||||
|
||||
Use `forge_client_main` as the foundation dependency for client addons that
|
||||
need Forge macros, function naming, settings, or mod-level configuration.
|
||||
|
||||
Feature logic should stay in the owning addon. `main` should remain limited to
|
||||
shared client configuration and compile infrastructure.
|
||||
|
||||
## Key Files
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `script_mod.hpp` | Client mod identity. |
|
||||
| `script_version.hpp` | Client mod version values. |
|
||||
| `script_macros.hpp` | Shared client macros. |
|
||||
| `CfgSettings.hpp` | Client CBA settings. |
|
||||
| `config.cpp` | Addon config and mod wiring. |
|
||||
|
||||
## Dependency Pattern
|
||||
|
||||
Feature addons normally depend on `forge_client_main` in their `config.cpp`.
|
||||
|
||||
```cpp
|
||||
class forge_client_example {
|
||||
requiredAddons[] = {
|
||||
"forge_client_main"
|
||||
};
|
||||
};
|
||||
```
|
||||
|
||||
## Usage Notes
|
||||
|
||||
- Put domain UI, repositories, and event handling in feature addons.
|
||||
- Put reusable browser bridge behavior in `forge_client_common`.
|
||||
- Put server-only behavior in `arma/server/addons`.
|
||||
- Keep settings in `CfgSettings.hpp` when they apply to the client mod as a
|
||||
whole or to a client feature toggle.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Client Usage Guide](/client-addons)
|
||||
- [Client Common Usage Guide](/client-addons/common)
|
||||
- [Development Guide](/getting-started/development)
|
||||
107
docus/content/4.client-addons/10.phone.md
Normal file
107
docus/content/4.client-addons/10.phone.md
Normal file
@ -0,0 +1,107 @@
|
||||
# Client Phone Usage Guide
|
||||
|
||||
The client phone addon provides the in-game phone UI for contacts, SMS
|
||||
messages, email, and local utility apps such as notes, calendar events, world
|
||||
clocks, and alarms.
|
||||
|
||||
## Open Phone UI
|
||||
|
||||
```sqf
|
||||
call forge_client_phone_fnc_openUI;
|
||||
```
|
||||
|
||||
The phone UI creates `RscPhone`, loads `ui/_site/index.html`, and routes
|
||||
browser alerts through `forge_client_phone_fnc_handleUIEvents`.
|
||||
|
||||
## State Ownership
|
||||
|
||||
Contacts, messages, and emails are server-owned and requested through the
|
||||
server phone addon.
|
||||
|
||||
Local utility app state is stored in `profileNamespace`:
|
||||
|
||||
- notes
|
||||
- calendar events
|
||||
- world clocks
|
||||
- alarms
|
||||
- theme/preferences
|
||||
|
||||
## Phone Class
|
||||
|
||||
`forge_client_phone_fnc_initClass` creates `GVAR(PhoneClass)`.
|
||||
|
||||
The phone class currently owns local notes, events, and settings helpers.
|
||||
Contacts, messages, and emails continue to use server-backed request/response
|
||||
events.
|
||||
|
||||
## Browser Events
|
||||
|
||||
### Session and Preferences
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `phone::get::player` | Send player UID to browser with `setPlayerUid`. |
|
||||
| `phone::get::theme` | Send saved light/dark theme to browser. |
|
||||
| `phone::set::theme` | Save theme preference to `profileNamespace`. |
|
||||
|
||||
### Contacts
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `phone::get::contacts` | Load cached contacts and request server refresh. |
|
||||
| `phone::refresh::contacts` | Request contacts from server. |
|
||||
| `phone::add::contact` | Add contact by phone number. |
|
||||
| `phone::add::contact::by::phone` | Add contact by phone number. |
|
||||
| `phone::add::contact::by::email` | Add contact by email. |
|
||||
| `phone::remove::contact` | Remove contact by UID. |
|
||||
|
||||
### Messages
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `phone::get::messages` | Request messages from server. |
|
||||
| `phone::get::message::thread` | Request thread with another UID. |
|
||||
| `phone::send::message` | Send SMS through server. |
|
||||
| `phone::mark::message::read` | Mark message read on server. |
|
||||
| `phone::delete::message` | Delete message on server. |
|
||||
|
||||
### Email
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `phone::get::emails` | Request emails from server. |
|
||||
| `phone::send::email` | Send email through server. |
|
||||
| `phone::mark::email::read` | Mark email read on server. |
|
||||
| `phone::delete::email` | Delete email on server. |
|
||||
|
||||
### Local Utility Apps
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `phone::get::notes` | Load local notes. |
|
||||
| `phone::save::note` | Save local note. |
|
||||
| `phone::delete::note` | Delete local note. |
|
||||
| `phone::get::events` | Load local calendar events. |
|
||||
| `phone::save::event` | Save local calendar event. |
|
||||
| `phone::delete::event` | Delete local calendar event. |
|
||||
| `phone::get::clocks` | Load local world clocks. |
|
||||
| `phone::save::clock` | Save local world clock. |
|
||||
| `phone::delete::clock` | Delete local world clock. |
|
||||
| `phone::get::alarms` | Load local alarms. |
|
||||
| `phone::save::alarm` | Save local alarm. |
|
||||
| `phone::delete::alarm` | Delete local alarm. |
|
||||
| `phone::toggle::alarm` | Toggle local alarm enabled state. |
|
||||
|
||||
## Usage Rules
|
||||
|
||||
- Send contact, message, and email mutations to the server phone addon.
|
||||
- Keep local-only utility apps in `profileNamespace` until they are migrated to
|
||||
server-backed storage.
|
||||
- Do not treat local phone utility state as shared multiplayer state.
|
||||
- Validate required UID, phone, email, subject, and message fields before
|
||||
sending server requests.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Phone Usage Guide](/server-modules/phone)
|
||||
- [Client Notifications Usage Guide](/client-addons/notifications)
|
||||
92
docus/content/4.client-addons/11.store.md
Normal file
92
docus/content/4.client-addons/11.store.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Client Store Usage Guide
|
||||
|
||||
The client store addon provides the storefront browser UI for catalog browsing,
|
||||
category hydration, payment source display, cart handling, and checkout
|
||||
requests.
|
||||
|
||||
## Open Store UI
|
||||
|
||||
```sqf
|
||||
call forge_client_store_fnc_openUI;
|
||||
```
|
||||
|
||||
The UI opens `RscStore`, loads `ui/_site/index.html`, and routes browser alerts
|
||||
through `forge_client_store_fnc_handleUIEvents`.
|
||||
|
||||
## Bridge
|
||||
|
||||
`forge_client_store_fnc_initUIBridge` owns:
|
||||
|
||||
- browser control lookup
|
||||
- store hydrate requests
|
||||
- category requests
|
||||
- checkout requests
|
||||
- category hydrate/failure responses
|
||||
- checkout success/failure responses
|
||||
- store config refresh after successful checkout
|
||||
|
||||
Store currently uses its own `StoreUIBridge.receive(...)` browser bridge rather
|
||||
than the shared `ForgeBridge.receive(...)` delivery used by newer bridges.
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `store::ready` | Request store hydrate from the server. |
|
||||
| `store::category::request` | Request catalog items for a category. |
|
||||
| `store::checkout::request` | Forward checkout JSON to the server. |
|
||||
| `store::close` | Close the display. |
|
||||
|
||||
## Browser Response Events
|
||||
|
||||
| Event | Purpose |
|
||||
| --- | --- |
|
||||
| `store::hydrate` | Initial storefront/session/config payload. |
|
||||
| `store::config::hydrate` | Refreshed payment/source config. |
|
||||
| `store::category::hydrate` | Category catalog payload. |
|
||||
| `store::category::failure` | Category request failure. |
|
||||
| `store::checkout::success` | Checkout success payload. |
|
||||
| `store::checkout::failure` | Checkout failure payload. |
|
||||
|
||||
## Category Requests
|
||||
|
||||
Category requests require a non-empty category value.
|
||||
|
||||
```json
|
||||
{
|
||||
"category": "weapons"
|
||||
}
|
||||
```
|
||||
|
||||
The client lowercases the category before forwarding it to the server store
|
||||
addon.
|
||||
|
||||
## Checkout Requests
|
||||
|
||||
Checkout requests send a serialized checkout payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"checkoutJson": "{\"items\":[],\"paymentSource\":\"cash\"}"
|
||||
}
|
||||
```
|
||||
|
||||
The client only forwards the checkout data. The server store addon and
|
||||
extension validate prices, inventory grants, payment source authorization, and
|
||||
integration with bank, organization, locker, and garage state.
|
||||
|
||||
After a successful checkout, the client asks the server for a fresh store config
|
||||
payload so payment-source balances and permissions stay current.
|
||||
|
||||
## Authoritative State
|
||||
|
||||
Catalog data, prices, checkout validation, money movement, organization funds,
|
||||
credit lines, locker grants, garage grants, and persistence are server-owned.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Store Usage Guide](/server-modules/store)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client Organization Usage Guide](/client-addons/organization)
|
||||
- [Client Locker Usage Guide](/client-addons/locker)
|
||||
- [Client Garage Usage Guide](/client-addons/garage)
|
||||
92
docus/content/4.client-addons/2.common.md
Normal file
92
docus/content/4.client-addons/2.common.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Client Common Usage Guide
|
||||
|
||||
The client `common` addon contains shared browser UI bridge declarations and
|
||||
common client-side browser integration patterns.
|
||||
|
||||
## Purpose
|
||||
|
||||
Use `forge_client_common` when a browser-backed feature UI needs reusable
|
||||
screen lifecycle behavior:
|
||||
|
||||
- active browser control tracking
|
||||
- browser ready state
|
||||
- pending event queues
|
||||
- `ExecJS` payload delivery
|
||||
- shared bridge object inheritance through `createHashMapObject`
|
||||
|
||||
Feature addons still own their app-specific events and server RPC mapping.
|
||||
|
||||
## Shared Bridge
|
||||
|
||||
Initialize the bridge declarations with:
|
||||
|
||||
```sqf
|
||||
private _webUIDeclarations = call forge_client_common_fnc_initWebUIBridge;
|
||||
private _bridgeDeclaration = _webUIDeclarations get "bridgeDeclaration";
|
||||
```
|
||||
|
||||
Feature bridges can inherit from the shared declaration:
|
||||
|
||||
```sqf
|
||||
GVAR(MyUIBridgeBaseClass) = compileFinal createHashMapFromArray [
|
||||
["#base", _bridgeDeclaration],
|
||||
["#type", "MyUIBridgeBaseClass"],
|
||||
["handleReady", compileFinal {
|
||||
params [["_control", controlNull, [controlNull]]];
|
||||
|
||||
_self call ["setActiveBrowserControl", [_control]];
|
||||
_self call ["sendEvent", ["myAddon::hydrate", createHashMap, _control]];
|
||||
}]
|
||||
];
|
||||
```
|
||||
|
||||
## Event Delivery
|
||||
|
||||
`sendEvent` builds this payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "myAddon::event",
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
If the browser control is missing or not ready, the payload is queued on the
|
||||
screen object. When the screen marks ready, `flushPendingEvents` delivers the
|
||||
queue.
|
||||
|
||||
## Screen Lifecycle
|
||||
|
||||
The shared screen object tracks:
|
||||
|
||||
| Field | Purpose |
|
||||
| --- | --- |
|
||||
| `control` | Active browser control. |
|
||||
| `readyState` | Whether the browser app has sent its ready event. |
|
||||
| `pendingEvents` | Outbound events waiting for a ready browser. |
|
||||
|
||||
Call `handleClose` or `dispose` when a display closes so stale controls and
|
||||
queued events are cleared.
|
||||
|
||||
## Current Consumers
|
||||
|
||||
The common bridge pattern is used by the newer bank, CAD, garage, and
|
||||
organization client bridges. Store currently keeps its own bridge object and
|
||||
browser bridge function names.
|
||||
|
||||
## Usage Rules
|
||||
|
||||
- Keep bridge inheritance in feature addons thin and explicit.
|
||||
- Keep shared code generic; do not add bank, CAD, org, or store-specific logic
|
||||
to `common`.
|
||||
- Prefer namespaced events such as `garage::sync`.
|
||||
- Send hash maps or arrays that can be safely serialized with `toJSON`.
|
||||
- Avoid direct extension calls from the client bridge; send CBA server events.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Client Usage Guide](/client-addons)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client CAD Usage Guide](/client-addons/cad)
|
||||
- [Client Garage Usage Guide](/client-addons/garage)
|
||||
- [Client Organization Usage Guide](/client-addons/organization)
|
||||
98
docus/content/4.client-addons/3.actor.md
Normal file
98
docus/content/4.client-addons/3.actor.md
Normal file
@ -0,0 +1,98 @@
|
||||
# Client Actor Usage Guide
|
||||
|
||||
The client actor addon owns the player interaction menu and client-side actor
|
||||
repository. It is the main launcher for nearby player actions and other Forge
|
||||
client UIs.
|
||||
|
||||
## Open the Actor Menu
|
||||
|
||||
```sqf
|
||||
call forge_client_actor_fnc_openUI;
|
||||
```
|
||||
|
||||
The actor menu opens `RscActorMenu`, loads `ui/_site/index.html`, and routes
|
||||
browser alerts through `forge_client_actor_fnc_handleUIEvents`.
|
||||
|
||||
## Repository
|
||||
|
||||
`forge_client_actor_fnc_initRepository` creates `GVAR(ActorRepository)`.
|
||||
|
||||
The repository:
|
||||
|
||||
- requests actor initialization from the server
|
||||
- saves actor state through the server actor addon
|
||||
- caches client-visible actor fields
|
||||
- applies position, direction, stance, rank, and loadout on JIP sync when the
|
||||
relevant settings allow it
|
||||
- provides nearby interaction actions to the browser UI
|
||||
|
||||
Initialize actor state through the repository:
|
||||
|
||||
```sqf
|
||||
GVAR(ActorRepository) call ["init", []];
|
||||
```
|
||||
|
||||
Save actor state through the server:
|
||||
|
||||
```sqf
|
||||
GVAR(ActorRepository) call ["save", [true]];
|
||||
```
|
||||
|
||||
## Nearby Actions
|
||||
|
||||
The menu asks for nearby actions with:
|
||||
|
||||
```text
|
||||
actor::get::actions
|
||||
```
|
||||
|
||||
The repository scans objects within 5 meters and returns actions based on
|
||||
mission object variables:
|
||||
|
||||
| Variable | Action |
|
||||
| --- | --- |
|
||||
| `storeType` | store |
|
||||
| `isAtm` | ATM |
|
||||
| `isBank` | bank |
|
||||
| `isGarage` | garage |
|
||||
| `garageType` | garage subtype |
|
||||
| `isLocker` | virtual arsenal action when VA is enabled |
|
||||
| `deviceType` | device action placeholder |
|
||||
| nearby player unit | player interaction placeholder |
|
||||
|
||||
The response is pushed into the browser with `updateAvailableActions(...)`.
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `actor::get::actions` | Refresh nearby actions. |
|
||||
| `actor::close::menu` | Close actor menu. |
|
||||
| `actor::open::atm` | Open bank UI in ATM mode. |
|
||||
| `actor::open::bank` | Open bank UI in bank mode. |
|
||||
| `actor::open::cad` | Open CAD UI. |
|
||||
| `actor::open::garage` | Open garage UI. |
|
||||
| `actor::open::vgarage` | Open virtual garage. |
|
||||
| `actor::open::org` | Open organization UI. |
|
||||
| `actor::open::vlocker` | Open ACE arsenal on `FORGE_Locker_Box`. |
|
||||
| `actor::open::phone` | Open phone UI. |
|
||||
| `actor::open::store` | Open store UI. |
|
||||
|
||||
Device and player interaction events currently display placeholder feedback.
|
||||
|
||||
## Authoritative State
|
||||
|
||||
Actor persistence is server-owned. The client repository requests and displays
|
||||
actor data, but actor creation, durable updates, and hot-state behavior are
|
||||
handled by the server actor addon and extension.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Actor Usage Guide](/server-modules/actor)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client CAD Usage Guide](/client-addons/cad)
|
||||
- [Client Garage Usage Guide](/client-addons/garage)
|
||||
- [Client Locker Usage Guide](/client-addons/locker)
|
||||
- [Client Organization Usage Guide](/client-addons/organization)
|
||||
- [Client Phone Usage Guide](/client-addons/phone)
|
||||
- [Client Store Usage Guide](/client-addons/store)
|
||||
84
docus/content/4.client-addons/4.bank.md
Normal file
84
docus/content/4.client-addons/4.bank.md
Normal file
@ -0,0 +1,84 @@
|
||||
# Client Bank Usage Guide
|
||||
|
||||
The client bank addon opens the bank and ATM browser UI, forwards banking
|
||||
requests to the server bank addon, and pushes account updates back into the
|
||||
browser.
|
||||
|
||||
## Open Bank UI
|
||||
|
||||
Open full bank mode:
|
||||
|
||||
```sqf
|
||||
call forge_client_bank_fnc_openUI;
|
||||
```
|
||||
|
||||
Open ATM mode:
|
||||
|
||||
```sqf
|
||||
[true] call forge_client_bank_fnc_openUI;
|
||||
```
|
||||
|
||||
The open function creates `RscBank`, sets the bridge mode to `bank` or `atm`,
|
||||
loads `ui/_site/index.html`, and routes browser events through
|
||||
`forge_client_bank_fnc_handleUIEvents`.
|
||||
|
||||
## Bridge and Repository
|
||||
|
||||
`forge_client_bank_fnc_initRepository` tracks account load and cached account
|
||||
state.
|
||||
|
||||
`forge_client_bank_fnc_initUIBridge` owns:
|
||||
|
||||
- active browser control tracking
|
||||
- bank/ATM mode
|
||||
- browser ready handling
|
||||
- account hydrate and sync responses
|
||||
- deposit, withdrawal, transfer, earnings deposit, credit repayment, and PIN
|
||||
requests
|
||||
- browser notice delivery
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `bank::ready` | Mark browser ready and request hydrate from the server. |
|
||||
| `bank::refresh` | Request fresh bank hydrate data. |
|
||||
| `bank::deposit::request` | Forward deposit amount to the server. |
|
||||
| `bank::withdraw::request` | Forward withdrawal amount to the server. |
|
||||
| `bank::transfer::request` | Forward target, source field, and amount. |
|
||||
| `bank::depositEarnings::request` | Request earnings deposit. |
|
||||
| `bank::repayCreditLine::request` | Request credit-line repayment. |
|
||||
| `bank::pin::request` | Forward PIN validation request. |
|
||||
| `bank::close` | Dispose bridge screen state and close the display. |
|
||||
|
||||
## Browser Response Events
|
||||
|
||||
The bridge sends:
|
||||
|
||||
| Event | Purpose |
|
||||
| --- | --- |
|
||||
| `bank::hydrate` | Full session/account payload. |
|
||||
| `bank::sync` | Account patch or sync data. |
|
||||
| `bank::notice` | UI-visible notice payload. |
|
||||
|
||||
## Request Flow
|
||||
|
||||
Example deposit flow:
|
||||
|
||||
1. Browser sends `bank::deposit::request` with an `amount`.
|
||||
2. Client bridge calls the server bank request event.
|
||||
3. Server bank addon validates the request and calls bank hot-state logic.
|
||||
4. Server response is caught by the client post-init event handlers.
|
||||
5. Client bridge sends `bank::sync` or `bank::notice` back to the browser.
|
||||
|
||||
## Authoritative State
|
||||
|
||||
Balances, PIN authorization, transfers, checkout charges, credit lines, and
|
||||
persistence are server-owned. The client should only display account data and
|
||||
request mutations through server events.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Bank Usage Guide](/server-modules/bank)
|
||||
- [Client Common Usage Guide](/client-addons/common)
|
||||
- [Client Store Usage Guide](/client-addons/store)
|
||||
100
docus/content/4.client-addons/5.cad.md
Normal file
100
docus/content/4.client-addons/5.cad.md
Normal file
@ -0,0 +1,100 @@
|
||||
# Client CAD Usage Guide
|
||||
|
||||
The client CAD addon provides the map and dispatch UI for groups, active
|
||||
tasks, task assignment, dispatch orders, support requests, and task
|
||||
acknowledge/decline workflows.
|
||||
|
||||
## Open CAD UI
|
||||
|
||||
```sqf
|
||||
call forge_client_cad_fnc_openUI;
|
||||
```
|
||||
|
||||
The CAD UI opens `RscMapUI` and loads separate browser controls for:
|
||||
|
||||
- top bar
|
||||
- bottom bar
|
||||
- side panel
|
||||
- dispatcher board
|
||||
|
||||
The native Arma map remains part of the same display.
|
||||
|
||||
## Repository and Bridge
|
||||
|
||||
`forge_client_cad_fnc_initRepository` caches the hydrated CAD payload,
|
||||
selected mode, dispatch view, session data, groups, tasks, requests, and
|
||||
assignments.
|
||||
|
||||
`forge_client_cad_fnc_initUIBridge` owns:
|
||||
|
||||
- ready state for side panel, top bar, and dispatcher board
|
||||
- operations vs dispatch mode
|
||||
- board vs map dispatch view
|
||||
- hydrate requests
|
||||
- task assignment, acknowledge, and decline requests
|
||||
- dispatch order create/close requests
|
||||
- support request submit/close requests
|
||||
- group status, role, and profile requests
|
||||
- map focus actions
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `cad::topbar::ready` | Mark top bar ready and push top bar state. |
|
||||
| `cad::ready` | Mark side panel ready and request hydrate. |
|
||||
| `cad::dispatcher::ready` | Mark dispatcher board ready and push hydrate data. |
|
||||
| `cad::mode::set` | Switch between operations and dispatch mode. |
|
||||
| `cad::dispatchView::set` | Switch dispatch board/map view. |
|
||||
| `cad::refresh` | Request fresh CAD hydrate data. |
|
||||
| `cad::tasks::assign` | Assign a task to a group. |
|
||||
| `cad::tasks::acknowledge` | Acknowledge assigned task. |
|
||||
| `cad::tasks::decline` | Decline assigned task. |
|
||||
| `cad::dispatchOrder::create` | Create dispatch order. |
|
||||
| `cad::dispatchOrder::close` | Close dispatch order. |
|
||||
| `cad::supportRequest::submit` | Submit support request. |
|
||||
| `cad::supportRequest::close` | Close support request. |
|
||||
| `cad::groups::status` | Update group status. |
|
||||
| `cad::groups::role` | Update group role. |
|
||||
| `cad::groups::profile` | Update status and role together. |
|
||||
| `cad::groups::focus` | Center map on a group. |
|
||||
| `cad::tasks::focus` | Center map on a task. |
|
||||
| `cad::requests::focus` | Center map on a support request. |
|
||||
| `map::zoomIn` | Zoom native map in. |
|
||||
| `map::zoomOut` | Zoom native map out. |
|
||||
| `map::search` | Placeholder status update. |
|
||||
| `map::close` | Dispose bridge state and close the display. |
|
||||
|
||||
## Response Events
|
||||
|
||||
The bridge pushes:
|
||||
|
||||
| Event | Purpose |
|
||||
| --- | --- |
|
||||
| `cad::hydrate` | Full hydrated CAD payload to the side panel. |
|
||||
| `cad::assignment::response` | Task assignment/acknowledge/decline result. |
|
||||
| `cad::group::response` | Group status/role/profile result. |
|
||||
| `cad::request::response` | Support request result. |
|
||||
|
||||
Dispatcher board controls also receive direct `ExecJS` status and hydrate
|
||||
calls.
|
||||
|
||||
## Task Compatibility
|
||||
|
||||
CAD task visibility depends on server-side task catalog entries. Tasks created
|
||||
through Eden Forge task modules or `forge_server_task_fnc_startTask` are the
|
||||
normal CAD-compatible task sources because they register task catalog data.
|
||||
|
||||
Direct handler or task-function calls only work with CAD when the task catalog
|
||||
entry already exists.
|
||||
|
||||
## Authorization Notes
|
||||
|
||||
Only dispatcher sessions can enter dispatch mode. If the hydrated session is
|
||||
not a dispatcher, the bridge forces the UI back to operations mode.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [CAD Usage Guide](/server-modules/cad)
|
||||
- [Task Usage Guide](/server-modules/task)
|
||||
- [Client Common Usage Guide](/client-addons/common)
|
||||
95
docus/content/4.client-addons/6.garage.md
Normal file
95
docus/content/4.client-addons/6.garage.md
Normal file
@ -0,0 +1,95 @@
|
||||
# Client Garage Usage Guide
|
||||
|
||||
The client garage addon provides player vehicle storage UI, vehicle
|
||||
store/retrieve actions, selected nearby vehicle service requests, vehicle
|
||||
context building, and the virtual garage view.
|
||||
|
||||
## Open Garage UI
|
||||
|
||||
```sqf
|
||||
call forge_client_garage_fnc_openUI;
|
||||
```
|
||||
|
||||
The garage UI opens `RscGarage`, loads `ui/_site/index.html`, and routes
|
||||
browser events through `forge_client_garage_fnc_handleUIEvents`.
|
||||
|
||||
## Open Virtual Garage
|
||||
|
||||
```sqf
|
||||
call forge_client_garage_fnc_openVG;
|
||||
```
|
||||
|
||||
The virtual garage uses mission-configured `FORGE_CfgGarages` locations to set
|
||||
the spawn/preview position, opens the BIS garage interface, and restricts the
|
||||
available vehicle lists from the virtual garage repository.
|
||||
|
||||
## Client Services
|
||||
|
||||
| Service | Purpose |
|
||||
| --- | --- |
|
||||
| `GarageRepository` | Player garage view state. |
|
||||
| `VGRepository` | Virtual garage unlock view state. |
|
||||
| `GarageHelperService` | Vehicle names, hit points, and payload helpers. |
|
||||
| `GarageContextService` | Nearby/current vehicle context. |
|
||||
| `GaragePayloadService` | Browser hydrate payload construction. |
|
||||
| `GarageActionService` | Store/retrieve request handling and selected nearby vehicle refuel/repair request forwarding. |
|
||||
| `GarageUIBridge` | Browser ready, hydrate, and sync delivery. |
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `garage::ready` | Mark browser ready and send `garage::hydrate`. |
|
||||
| `garage::refresh` | Send current garage payload as `garage::sync`. |
|
||||
| `garage::vehicle::retrieve::request` | Forward retrieve request through the action service. |
|
||||
| `garage::vehicle::store::request` | Forward store request through the action service. |
|
||||
| `garage::vehicle::refuel::request` | Forward selected nearby vehicle refuel request to the server economy service. |
|
||||
| `garage::vehicle::repair::request` | Forward selected nearby vehicle repair request to the server economy service. |
|
||||
| `garage::close` | Dispose bridge screen state and close the display. |
|
||||
|
||||
## Browser Response Events
|
||||
|
||||
| Event | Purpose |
|
||||
| --- | --- |
|
||||
| `garage::hydrate` | Initial vehicle and session payload. |
|
||||
| `garage::sync` | Refreshed vehicle payload. |
|
||||
| `garage::service::success` | Browser notice for accepted refuel/repair requests. |
|
||||
| `garage::service::failure` | Browser notice for rejected refuel/repair requests. |
|
||||
|
||||
Server action responses are handled by the action service and notification
|
||||
flow.
|
||||
|
||||
## Vehicle Service
|
||||
|
||||
The selected vehicle detail panel includes refuel and repair actions for nearby
|
||||
world vehicles. Stored records must be retrieved first because server economy
|
||||
services operate on live vehicle objects, not stored garage records.
|
||||
|
||||
Refuel requests use the server economy `RefuelService` event. Repair requests
|
||||
use the server economy `RepairService` event. Both services are billed by the
|
||||
server economy addon through organization funds.
|
||||
|
||||
## Mission Setup
|
||||
|
||||
Garage interactions are normally surfaced through the actor menu when nearby
|
||||
objects have garage variables such as:
|
||||
|
||||
```sqf
|
||||
_object setVariable ["isGarage", true, true];
|
||||
_object setVariable ["garageType", "cars", true];
|
||||
```
|
||||
|
||||
Virtual garage access also requires configured garage locations in mission
|
||||
config so the preview/spawn position can be resolved.
|
||||
|
||||
## Authoritative State
|
||||
|
||||
The client gathers vehicle context and sends store/retrieve requests. Stored
|
||||
vehicle state, validation, spawning, removal, and persistence are owned by the
|
||||
server garage addon and extension.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Garage Usage Guide](/server-modules/garage)
|
||||
- [Client Actor Usage Guide](/client-addons/actor)
|
||||
- [Client Notifications Usage Guide](/client-addons/notifications)
|
||||
87
docus/content/4.client-addons/7.locker.md
Normal file
87
docus/content/4.client-addons/7.locker.md
Normal file
@ -0,0 +1,87 @@
|
||||
# Client Locker Usage Guide
|
||||
|
||||
The client locker addon manages personal locker display state, local locker
|
||||
container behavior, and virtual arsenal unlock state.
|
||||
|
||||
## Repositories
|
||||
|
||||
`forge_client_locker_fnc_initRepository` creates `GVAR(LockerRepository)`.
|
||||
|
||||
`forge_client_locker_fnc_initVARepository` creates `GVAR(VARepository)`.
|
||||
|
||||
Initialize locker state:
|
||||
|
||||
```sqf
|
||||
GVAR(LockerRepository) call ["init", []];
|
||||
GVAR(VARepository) call ["init", []];
|
||||
```
|
||||
|
||||
## Locker Container Flow
|
||||
|
||||
The repository searches mission namespace variables whose names contain
|
||||
`locker` and refer to objects. For each server/mission locker object, it creates
|
||||
a local `Box_NATO_Equip_F` at the same position and attaches container event
|
||||
handlers.
|
||||
|
||||
On container open:
|
||||
|
||||
- the local container is cleared
|
||||
- cached locker items are inserted into the container
|
||||
- over-capacity warnings are emitted when the item count is above 25
|
||||
|
||||
On container close:
|
||||
|
||||
- cargo, nested container items, and weapon attachments are read back
|
||||
- the new locker map is sent to the server with the override request
|
||||
- the local repository cache is updated
|
||||
|
||||
## Virtual Arsenal Flow
|
||||
|
||||
The virtual arsenal repository creates a local `FORGE_Locker_Box` and requests
|
||||
virtual arsenal unlocks from the server.
|
||||
|
||||
As sync data arrives, it applies unlocks through ACE Arsenal:
|
||||
|
||||
| Data key | Client behavior |
|
||||
| --- | --- |
|
||||
| `items` | Add virtual items. |
|
||||
| `weapons` | Add virtual weapons. |
|
||||
| `magazines` | Add virtual magazines. |
|
||||
| `backpacks` | Add virtual backpacks. |
|
||||
|
||||
The actor menu opens the virtual locker with:
|
||||
|
||||
```sqf
|
||||
[FORGE_Locker_Box, player, false] spawn ace_arsenal_fnc_openBox;
|
||||
```
|
||||
|
||||
## Server Events
|
||||
|
||||
The client repository sends requests for:
|
||||
|
||||
- locker initialization
|
||||
- locker save
|
||||
- locker override after container close
|
||||
- virtual arsenal initialization
|
||||
- virtual arsenal save
|
||||
|
||||
The server locker addon and extension own the saved locker and virtual arsenal
|
||||
state.
|
||||
|
||||
## Mission Setup
|
||||
|
||||
Mission locker objects must be placed into `missionNamespace` with a variable
|
||||
name containing `locker`. The client creates local interactive containers from
|
||||
those authoritative mission objects.
|
||||
|
||||
Example:
|
||||
|
||||
```sqf
|
||||
missionNamespace setVariable ["forge_locker_alpha", _lockerObject, true];
|
||||
```
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Locker Usage Guide](/server-modules/locker)
|
||||
- [Owned Storage Usage Guide](/server-modules/owned-storage)
|
||||
- [Client Actor Usage Guide](/client-addons/actor)
|
||||
74
docus/content/4.client-addons/8.notifications.md
Normal file
74
docus/content/4.client-addons/8.notifications.md
Normal file
@ -0,0 +1,74 @@
|
||||
# Client Notifications Usage Guide
|
||||
|
||||
The client notifications addon owns the notification HUD, notification sound,
|
||||
and local notification service used by Forge client and server modules.
|
||||
|
||||
## Runtime Behavior
|
||||
|
||||
The notification display is created during client initialization. The browser
|
||||
HUD sends:
|
||||
|
||||
```text
|
||||
notifications::ready
|
||||
```
|
||||
|
||||
When that event is received, `NotificationService` initializes and sends a
|
||||
startup notification.
|
||||
|
||||
## Create a Notification
|
||||
|
||||
Use the notification service when available:
|
||||
|
||||
```sqf
|
||||
GVAR(NotificationService) call ["create", [
|
||||
"success",
|
||||
"Title",
|
||||
"Notification text.",
|
||||
4000
|
||||
]];
|
||||
```
|
||||
|
||||
Arguments:
|
||||
|
||||
| Argument | Purpose |
|
||||
| --- | --- |
|
||||
| `_type` | Notification type, such as `success`, `info`, `warning`, or `error`. |
|
||||
| `_title` | Notification title. |
|
||||
| `_content` | Notification body text. |
|
||||
| `_duration` | Display duration in milliseconds. |
|
||||
|
||||
The service dispatches a browser `forge:notify` custom event.
|
||||
|
||||
## CBA Event Surface
|
||||
|
||||
Other addons can use the client notification event:
|
||||
|
||||
```sqf
|
||||
["forge_client_notifications_recieveNotification", [
|
||||
"warning",
|
||||
"Garage",
|
||||
"Vehicle spawn position is blocked.",
|
||||
3000
|
||||
]] call CBA_fnc_localEvent;
|
||||
```
|
||||
|
||||
The event payload is:
|
||||
|
||||
```sqf
|
||||
[_type, _title, _content, _duration]
|
||||
```
|
||||
|
||||
## Usage Rules
|
||||
|
||||
- Use the shared notification service instead of opening separate transient
|
||||
browser UIs.
|
||||
- Keep server-driven player feedback short and actionable.
|
||||
- Treat notification state as transient client UI state.
|
||||
- Do not use notifications as the only record of durable domain changes.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Client Usage Guide](/client-addons)
|
||||
- [Client Garage Usage Guide](/client-addons/garage)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client Store Usage Guide](/client-addons/store)
|
||||
106
docus/content/4.client-addons/9.organization.md
Normal file
106
docus/content/4.client-addons/9.organization.md
Normal file
@ -0,0 +1,106 @@
|
||||
# Client Organization Usage Guide
|
||||
|
||||
The client organization addon provides the organization portal UI and browser
|
||||
bridge for login, registration, membership, invites, credit lines, leave and
|
||||
disband flows, assets, fleet, and treasury display.
|
||||
|
||||
## Open Organization UI
|
||||
|
||||
```sqf
|
||||
call forge_client_org_fnc_openUI;
|
||||
```
|
||||
|
||||
The UI opens `RscOrg`, loads `ui/_site/index.html`, and routes browser alerts
|
||||
through `forge_client_org_fnc_handleUIEvents`.
|
||||
|
||||
## Repository and Bridge
|
||||
|
||||
`forge_client_org_fnc_initRepository` caches organization portal state.
|
||||
|
||||
`forge_client_org_fnc_initUIBridge` owns:
|
||||
|
||||
- active browser control tracking
|
||||
- portal hydrate requests
|
||||
- create/login response routing
|
||||
- leave and disband requests
|
||||
- credit-line assignment requests
|
||||
- invite, accept invite, and decline invite requests
|
||||
- targeted browser response events
|
||||
|
||||
## Browser Events
|
||||
|
||||
| Event | Client behavior |
|
||||
| --- | --- |
|
||||
| `org::ready` | Mark browser ready and request `org::sync`. |
|
||||
| `org::login::request` | Request portal hydrate as `org::login::success`. |
|
||||
| `org::create::request` | Validate org name and request creation on server. |
|
||||
| `org::disband::request` | Request disband on server. |
|
||||
| `org::leave::request` | Request leave on server. |
|
||||
| `org::credit::request` | Request credit-line assignment. |
|
||||
| `org::invite::request` | Request member invite. |
|
||||
| `org::invite::accept` | Accept invite by org ID. |
|
||||
| `org::invite::decline` | Decline invite by org ID. |
|
||||
| `org::close` | Close the display. |
|
||||
|
||||
## Browser Response Events
|
||||
|
||||
| Event | Purpose |
|
||||
| --- | --- |
|
||||
| `org::sync` | Full portal sync payload. |
|
||||
| `org::login::success` | Login hydrate payload. |
|
||||
| `org::create::success` | Creation hydrate payload. |
|
||||
| `org::create::failure` | Creation validation or server failure. |
|
||||
| `org::disband::success` | Requester disband success. |
|
||||
| `org::disband::failure` | Disband failure. |
|
||||
| `org::portal::revoked` | Portal state revoked by someone else's disband action. |
|
||||
| `org::leave::success` | Leave success. |
|
||||
| `org::leave::failure` | Leave failure. |
|
||||
| `org::credit::success` | Credit-line request success. |
|
||||
| `org::credit::failure` | Credit-line request failure. |
|
||||
| `org::member::creditUpdated` | Targeted member credit-line patch. |
|
||||
| `org::invite::success` | Invite success. |
|
||||
| `org::invite::failure` | Invite failure. |
|
||||
| `org::invite::decision::success` | Invite accept/decline success. |
|
||||
| `org::invite::decision::failure` | Invite accept/decline failure. |
|
||||
|
||||
## Request Examples
|
||||
|
||||
Create organization request payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"orgName": "Example Logistics"
|
||||
}
|
||||
```
|
||||
|
||||
Credit-line request payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"memberUid": "76561198000000000",
|
||||
"memberName": "Player Name",
|
||||
"amount": 2500
|
||||
}
|
||||
```
|
||||
|
||||
Invite request payload:
|
||||
|
||||
```json
|
||||
{
|
||||
"targetUid": "76561198000000000",
|
||||
"targetName": "Player Name"
|
||||
}
|
||||
```
|
||||
|
||||
## Authoritative State
|
||||
|
||||
Organization funds, reputation, membership, invites, credit lines, assets,
|
||||
fleet, and persistence are server-owned. The client portal only displays and
|
||||
requests changes.
|
||||
|
||||
## Related Guides
|
||||
|
||||
- [Organization Usage Guide](/server-modules/organization)
|
||||
- [Client Common Usage Guide](/client-addons/common)
|
||||
- [Client Bank Usage Guide](/client-addons/bank)
|
||||
- [Client Store Usage Guide](/client-addons/store)
|
||||
172
docus/content/index.md
Normal file
172
docus/content/index.md
Normal file
@ -0,0 +1,172 @@
|
||||
---
|
||||
seo:
|
||||
title: Forge Framework Documentation
|
||||
description: Documentation for the Forge Arma 3 framework, covering architecture, persistence, extension APIs, gameplay modules, and client UIs.
|
||||
---
|
||||
|
||||
::u-page-hero
|
||||
#title
|
||||
Forge Framework Documentation
|
||||
|
||||
#description
|
||||
Forge is a persistent Arma 3 framework that combines SQF addons, a Rust
|
||||
`arma-rs` extension, SurrealDB persistence, shared domain crates, and
|
||||
browser-backed player interfaces.
|
||||
|
||||
Use these docs to understand the runtime architecture, extension API surface,
|
||||
server gameplay modules, and client addon integration patterns.
|
||||
|
||||
#links
|
||||
:::u-button
|
||||
---
|
||||
color: primary
|
||||
size: xl
|
||||
to: /getting-started
|
||||
trailing-icon: i-lucide-arrow-right
|
||||
---
|
||||
Start here
|
||||
:::
|
||||
|
||||
:::u-button
|
||||
---
|
||||
color: neutral
|
||||
icon: simple-icons-github
|
||||
size: xl
|
||||
to: https://github.com/InnovativeDevSolutions/forge
|
||||
variant: outline
|
||||
---
|
||||
View source
|
||||
:::
|
||||
::
|
||||
|
||||
::u-page-section
|
||||
#title
|
||||
What Forge Covers
|
||||
|
||||
#features
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-boxes
|
||||
---
|
||||
#title
|
||||
Domain [Modules]{.text-primary}
|
||||
|
||||
#description
|
||||
Actor, bank, CAD, garage, locker, organization, phone, store, task, and
|
||||
owned-storage workflows share a consistent service and extension model.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-server
|
||||
---
|
||||
#title
|
||||
Rust [Extension]{.text-primary}
|
||||
|
||||
#description
|
||||
The server extension keeps command parsing thin, routes domain requests into
|
||||
services, and persists durable state through SurrealDB.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-database-zap
|
||||
---
|
||||
#title
|
||||
Durable [Persistence]{.text-primary}
|
||||
|
||||
#description
|
||||
Repository traits stay storage-agnostic while concrete adapters in the
|
||||
extension handle schema and database mapping.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-monitor-smartphone
|
||||
---
|
||||
#title
|
||||
Browser [UIs]{.text-primary}
|
||||
|
||||
#description
|
||||
Client addons host web-based interfaces inside Arma displays and synchronize
|
||||
state through namespaced browser bridge events.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-arrow-left-right
|
||||
---
|
||||
#title
|
||||
Transport [Layer]{.text-primary}
|
||||
|
||||
#description
|
||||
Large payloads move through chunked request and response transport while
|
||||
smaller commands still use direct `callExtension` paths.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-wrench
|
||||
---
|
||||
#title
|
||||
Development [Workflow]{.text-primary}
|
||||
|
||||
#description
|
||||
The docs cover module boundaries, local validation checks, and where new
|
||||
domain logic belongs across Rust, SQF, and web UI layers.
|
||||
:::
|
||||
::
|
||||
|
||||
::u-page-section
|
||||
#title
|
||||
Documentation Areas
|
||||
|
||||
#features
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-rocket
|
||||
to: /getting-started
|
||||
---
|
||||
#title
|
||||
[Getting Started]{.text-primary}
|
||||
|
||||
#description
|
||||
Framework overview, architecture, module reference, and development rules.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-server-cog
|
||||
to: /server-extension
|
||||
---
|
||||
#title
|
||||
Server [Extension]{.text-primary}
|
||||
|
||||
#description
|
||||
Extension architecture, command surface, and SQF usage examples.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-layers-3
|
||||
to: /server-modules
|
||||
---
|
||||
#title
|
||||
Server [Modules]{.text-primary}
|
||||
|
||||
#description
|
||||
Gameplay-domain usage guides for persistence, hot state, and command flows.
|
||||
:::
|
||||
|
||||
:::u-page-feature
|
||||
---
|
||||
icon: i-lucide-monitor-smartphone
|
||||
to: /client-addons
|
||||
---
|
||||
#title
|
||||
Client [Addons]{.text-primary}
|
||||
|
||||
#description
|
||||
Browser bridge, client UX entry points, and addon-specific event contracts.
|
||||
:::
|
||||
::
|
||||
1
docus/dist/200.html
vendored
Normal file
1
docus/dist/200.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/forge/_nuxt/entry.B0IIbxeE.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/forge/_nuxt/B3fabVUf.js"><script type="module" src="/forge/_nuxt/B3fabVUf.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script><meta name="robots" content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1" /></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__=(function(a){return {_priority:{env:-15,url:a,name:a},env:"production",name:"forge-docus",url:"https:\u002F\u002Finnovativedevsolutions.github.io"}}(-3))</script><script>window.__NUXT__={};window.__NUXT__.config={public:{assistant:{enabled:false,apiPath:"/__docus__/assistant"},mdc:{components:{prose:true,map:{accordion:"ProseAccordion","accordion-item":"ProseAccordionItem",badge:"ProseBadge",callout:"ProseCallout",card:"ProseCard","card-group":"ProseCardGroup",caution:"ProseCaution","code-collapse":"ProseCodeCollapse","code-group":"ProseCodeGroup","code-icon":"ProseCodeIcon","code-preview":"ProseCodePreview","code-tree":"ProseCodeTree",collapsible:"ProseCollapsible",field:"ProseField","field-group":"ProseFieldGroup",icon:"ProseIcon",kbd:"ProseKbd",note:"ProseNote",steps:"ProseSteps",tabs:"ProseTabs","tabs-item":"ProseTabsItem",tip:"ProseTip",warning:"ProseWarning"},customElements:[]},headings:{anchorLinks:{h1:false,h2:true,h3:true,h4:true,h5:false,h6:false}},highlight:{noApiRoute:true,theme:{light:"material-theme-lighter",default:"material-theme",dark:"material-theme-palenight"},shikiEngine:"javascript",langs:["bash","diff","json","js","ts","html","css","vue","shell","mdc","md","yaml"],highlighter:"shiki"}},content:{wsUrl:""},"nuxt-robots":{version:"6.0.7",isNuxtContentV2:false,debug:false,credits:true,groups:[{userAgent:["*"],allow:["/"],disallow:[],contentUsage:[],contentSignal:[],_indexable:true,_rules:[{pattern:"/",allow:true}],_normalized:true}],sitemap:["/sitemap.xml"],header:true,robotsEnabledValue:"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1",robotsDisabledValue:"noindex, nofollow",cacheControl:"max-age=14400, must-revalidate",botDetection:true,pageMetaRobots:{}},"nuxt-og-image":{defaults:{emojis:"noto",extension:"png",width:1200,height:600,cacheMaxAgeSeconds:259200},hasServerRuntime:true}},app:{baseURL:"/forge/",buildId:"cd17b1fd-ee30-4fca-a256-bc98b65244b8",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776805513424,false]</script></body></html>
|
||||
1
docus/dist/404.html
vendored
Normal file
1
docus/dist/404.html
vendored
Normal file
@ -0,0 +1 @@
|
||||
<!DOCTYPE html><html><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><link rel="stylesheet" href="/forge/_nuxt/entry.B0IIbxeE.css" crossorigin><link rel="modulepreload" as="script" crossorigin href="/forge/_nuxt/B3fabVUf.js"><script type="module" src="/forge/_nuxt/B3fabVUf.js" crossorigin></script><script>"use strict";(()=>{const t=window,e=document.documentElement,c=["dark","light"],n=getStorageValue("localStorage","nuxt-color-mode")||"system";let i=n==="system"?u():n;const r=e.getAttribute("data-color-mode-forced");r&&(i=r),l(i),t["__NUXT_COLOR_MODE__"]={preference:n,value:i,getColorScheme:u,addColorScheme:l,removeColorScheme:d};function l(o){const s=""+o+"",a="";e.classList?e.classList.add(s):e.className+=" "+s,a&&e.setAttribute("data-"+a,o)}function d(o){const s=""+o+"",a="";e.classList?e.classList.remove(s):e.className=e.className.replace(new RegExp(s,"g"),""),a&&e.removeAttribute("data-"+a)}function f(o){return t.matchMedia("(prefers-color-scheme"+o+")")}function u(){if(t.matchMedia&&f("").media!=="not all"){for(const o of c)if(f(":"+o).matches)return o}return"light"}})();function getStorageValue(t,e){switch(t){case"localStorage":return window.localStorage.getItem(e);case"sessionStorage":return window.sessionStorage.getItem(e);case"cookie":return getCookie(e);default:return null}}function getCookie(t){const c=("; "+window.document.cookie).split("; "+t+"=");if(c.length===2)return c.pop()?.split(";").shift()}</script><meta name="robots" content="noindex, nofollow" /></head><body><div id="__nuxt" class="isolate"></div><div id="teleports"></div><script>window.__NUXT_SITE_CONFIG__=(function(a){return {_priority:{env:-15,url:a,name:a},env:"production",name:"forge-docus",url:"https:\u002F\u002Finnovativedevsolutions.github.io"}}(-3))</script><script>window.__NUXT__={};window.__NUXT__.config={public:{assistant:{enabled:false,apiPath:"/__docus__/assistant"},mdc:{components:{prose:true,map:{accordion:"ProseAccordion","accordion-item":"ProseAccordionItem",badge:"ProseBadge",callout:"ProseCallout",card:"ProseCard","card-group":"ProseCardGroup",caution:"ProseCaution","code-collapse":"ProseCodeCollapse","code-group":"ProseCodeGroup","code-icon":"ProseCodeIcon","code-preview":"ProseCodePreview","code-tree":"ProseCodeTree",collapsible:"ProseCollapsible",field:"ProseField","field-group":"ProseFieldGroup",icon:"ProseIcon",kbd:"ProseKbd",note:"ProseNote",steps:"ProseSteps",tabs:"ProseTabs","tabs-item":"ProseTabsItem",tip:"ProseTip",warning:"ProseWarning"},customElements:[]},headings:{anchorLinks:{h1:false,h2:true,h3:true,h4:true,h5:false,h6:false}},highlight:{noApiRoute:true,theme:{light:"material-theme-lighter",default:"material-theme",dark:"material-theme-palenight"},shikiEngine:"javascript",langs:["bash","diff","json","js","ts","html","css","vue","shell","mdc","md","yaml"],highlighter:"shiki"}},content:{wsUrl:""},"nuxt-robots":{version:"6.0.7",isNuxtContentV2:false,debug:false,credits:true,groups:[{userAgent:["*"],allow:["/"],disallow:[],contentUsage:[],contentSignal:[],_indexable:true,_rules:[{pattern:"/",allow:true}],_normalized:true}],sitemap:["/sitemap.xml"],header:true,robotsEnabledValue:"index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1",robotsDisabledValue:"noindex, nofollow",cacheControl:"max-age=14400, must-revalidate",botDetection:true,pageMetaRobots:{}},"nuxt-og-image":{defaults:{emojis:"noto",extension:"png",width:1200,height:600,cacheMaxAgeSeconds:259200},hasServerRuntime:true}},app:{baseURL:"/forge/",buildId:"cd17b1fd-ee30-4fca-a256-bc98b65244b8",buildAssetsDir:"/_nuxt/",cdnURL:""}}</script><script type="application/json" data-nuxt-data="nuxt-app" data-ssr="false" id="__NUXT_DATA__">[{"prerenderedAt":1,"serverRendered":2},1776805513425,false]</script></body></html>
|
||||
1
docus/dist/__nuxt_content/docs/sql_dump.txt
vendored
Normal file
1
docus/dist/__nuxt_content/docs/sql_dump.txt
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docus/dist/__nuxt_content/landing/sql_dump.txt
vendored
Normal file
1
docus/dist/__nuxt_content/landing/sql_dump.txt
vendored
Normal file
@ -0,0 +1 @@
|
||||
H4sIAAAAAAAACr1YbVPbSBL+K136wl6VBLuXDZfjaj8YbBYHxxC/BBJEUe1RW5rzaEY7M7Ixqfz3q5ZkG5OXw9zefkgFS5qZnqe7n366b4KTQac16sCoddzrQPcU+hcj6Fx3h6Mh3AmjPWl/J/XUwE8ygVHnegSXg+671uAjnHc+hhAHljBZxgEcX1z0Oq0+P3LelsKXlj6QddLoOIAPrcHJWWvAb+ffenh3l6HL7u7ioD5k3O++H3f+9i+IIlhvF4RBtz/sDEbQ7Y8untj3odUbd4bw057ISMxcmd8p1InU6V4IU1SOQtjzn5YX5xN/fPXLx8N/pxfpUr2yeZRm5dvxWzfpnZx10+PLh+51ezrbC2Fv/mr/9f7PUfRpaHtvzLte2psdXvbzs8NPr39dLNu9s2lL9VT7df/98vpcpq/Kf3R52dvzC3E6uHr96eTyn73zq4t0cP5+ev9pLlU0zs9Gv/b6H/rY7xz2H7p79Q1z8hiEQXtwcbnxxFMvNNf5CpLneLBZ+10neukVbXtkYiq38tf8MyEnrCz8V56je0/6a4fylR4t1ziXKTarKwvandPWuDcCb0viLwr02fYWjszTj/c+f9mrI4zyPzGAVvCsY6h5cCB1Qvf7ecJu5X+f48AvC4qDozjIpZY52lkchHEwR1Xy45ubOCijAlOKMrImDsLPX8KbOPCUFwo98YM4mEdOGX/UoH4UB3HwJYyDU2NTglOLOS2MnUHbiDIn7Rvcbr+70ZZzmu1u4qCoj19tLB0gFJx9jm8NLZsjvILp+jyfoQdh8onU5GD4/hQwSYx2ISAMSufjWMcBbyxMQqu90eYYWcfmxQGsoyGEYWktoWofb84UFILL0FICiclRahAWPfEBOoljPbFm4chGExQzSqBQuCQLUnuyUxTk9hsQ1hcbOwKfkSNIjHDgDZQ6Ies86oRfgC21lzkBWpFJT1UUhBsroXXZBVdWu4dxrB3ZOVlIMSc+HHKTlKqxD4SSDFuFSWVUaivPQIHek9WVed93kpJ65h67p4wmpffsMv5KGGVs9bqwHFfLKq6cfKgj5F5Vv72pfh2k5L3UaeQ8Wk9J/c6iVPxQiiYOZKRKIROK0FqziKxMM1+H2pDXQUaWGki/Z4ym0ltUPzIm875wRwcHqfRZOdkXJj/oam3m6OWc2jQfGlUyTu5gynFYLV2b6GReKKpMdlG9Q5NQVqL21Tem9Epqqi3/IGkBzpRWsOm3jfFVxjkSdQrsknRXHPR1gpwYrk0/SrQpIUfQEzdWhzev6s+/9sDE3POy59vVrhOkTjhX4MozCp3rY16RDW9176N1wNzywnd1zP4wFr9FGHHQEt7YECaoZyGctNohpGgxpRCUETOyIRibopYPVdiHUGRGc0Z7w0nl0c1WiWwWmpKIX2BKwOQyVWbh6uQHBPZ2w0KcclJQlWGbvMxNQmqdT8+DuE7eXTBmUnsBwp1NydsZ41FG0LDM5rYzosIx8+aMQoHWcTXymdQhWFN6ciu+tPRHSc475h/TEJYUK4ZqiNZBUlqcKALn0TNDWlOm2YaRdwQ2QY8TdBQ9YLFTCDdW7I7w5aZivADjARXGSW/sEpgTvWMcltCEY4SpNs5LAYtMKuJYFJY8ASZYeLKMLdeOONYbD2WoE8ZTZJRjhfUKFMixKKROd8Q0N5otjFyO1leJtAuyx3WhfAGy4+5LmOHkUelzkBnnYUGTiO+fPKrPILWTCdXKIpGOa6ir0HJLLTJrNFeQWG+HpcacXIGCEmgEAEysTFICmpP2bkdk61qnaOo3Be+5uI4salcY+xJW6LFYeQG2PeTaU+BSGUwc5Ga+gUZkpWYl1KR9BaUlVxjNwmdtbBXIjGuOSpFdMQnHvVQKStZH0pJYXeuxfhOo1GNCYxXHWnxX2BeWtMh2ogeakzIFK9wXwH3VFJUXcnClFwUX/EbkwcSUOkErmUyVEahgjkomtcSrm8qaZRcsm0DTIo51Q8vKpFLAhJTRqQMU1jhXSeaQZXSzjCYw7kKlaWtw/3fxstUhQMsS/p/ki2UB4H8sQZ9r9o6O/r0+B4arc3b296ajYn/PJS3CJw1BEwKWpmTrNqWi+EcBallTvUiQRMKkW8A1jx81zs93+LCWDn+tZlmvfYLaSrCsGqgKNG4bS8eyj+6Rtf2uqNUJEr36Fmb5Stv+BYi9XEf/3rSPUcMONRxpKRNyMDV2uyHOjK91WtNlNqBWgvlPEBWPQKwb2Kiu4rtg2JT/3TFsNUftDOHxlhAIV733+BpIe7uEwkjtGz6u7hO5goScSlGLBlZ13qLwK6a9rXAQcXD0eT3uqk7jvpY4rttU8ATq6O8hT7s2f6/69pvbL19Wc6B6IlQNo/Z4gMX/H6wGROvN/8s8J3wyVTt6yuccKjzEqPd5Oq4J6/LF1LidllvRtTXqcOGPRxvjLsNV3aqafPEfD68PtR2/+UUbOr/MBvnicNjKxtc66x4+3E8uMCqjN5cPXTz7WTTTzB1WBGEwvmzz+HJ7mDvsjKAaLMNv1YAQrs46gw7IBH6Drwe8mxnq7X8A6Goc29EWAAA=
|
||||
1
docus/dist/_nuxt/-nV3O3Hd.js
vendored
Normal file
1
docus/dist/_nuxt/-nV3O3Hd.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"या",error:{title:"पृष्ठ नहीं मिला",description:"हमें खुशी है, लेकिन आप जो पृष्ठ खोज रहे हैं वह मौजूद नहीं है।"}},a={copy:{page:"पृष्ठ कॉपी करें",link:"Markdown पृष्ठ कॉपी करें",view:"Markdown के रूप में देखें",gpt:"ChatGPT में खोलें",claude:"Claude में खोलें"},links:"समुदाय",toc:"इस पृष्ठ पर",menu:"मेनू",report:"समस्या की रिपोर्ट करें",edit:"इस पृष्ठ को संपादित करें"},e={copyLogo:"लोगो कॉपी करें",copyWordmark:"वर्डमार्क कॉपी करें",downloadLogo:"लोगो डाउनलोड करें",downloadWordmark:"वर्डमार्क डाउनलोड करें",brandAssets:"ब्रांड एसेट्स",logoCopied:"लोगो कॉपी हो गया",wordmarkCopied:"वर्डमार्क कॉपी हो गया",logoDownloaded:"लोगो डाउनलोड हो गया",wordmarkDownloaded:"वर्डमार्क डाउनलोड हो गया",copyLogoFailed:"लोगो कॉपी नहीं हो सका",copyWordmarkFailed:"वर्डमार्क कॉपी नहीं हो सका"},n={title:"AI से पूछें",placeholder:"सवाल पूछें...",tooltip:"AI से सवाल पूछें",tryAsking:"सवाल पूछने की कोशिश करें",askAnything:"कुछ भी पूछें...",clearChat:"चैट हटाएँ",close:"बंद करें",expand:"विस्तार करें",collapse:"छोटा करें",thinking:"सोच रहे हैं...",askMeAnything:"कुछ भी पूछें",askMeAnythingDescription:"डॉक्यूमेंट नेविगेट करने, अवधारणाओं को समझने और जवाब खोजने में मदद पाएँ।",faq:"अक्सर पूछे जाने वाले सवाल",chatCleared:"रीफ़्रेश करने पर चैट साफ़ हो जाती है",lineBreak:"लाइन ब्रेक",explainWithAi:"AI से समझाएँ",toolListPages:"लिस्ट किए गए डॉक्यूमेंट पेज",toolReadPage:"पढ़ें",loading:{searching:"डॉक्यूमेंट खोजना",reading:"दस्तावेज़ों के माध्यम से पढ़ना",analyzing:"कंटेंट का विश्लेषण करना",finding:"सबसे अच्छा जवाब ढूँढना",finished:"इस्तेमाल किए गए स्रोत"}},i={common:o,docs:a,logo:e,assistant:n};export{n as assistant,o as common,i as default,a as docs,e as logo};
|
||||
1
docus/dist/_nuxt/2h0Q0JkN.js
vendored
Normal file
1
docus/dist/_nuxt/2h0Q0JkN.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as g,a1 as f,aQ as b,e as r,ac as t,s as e,af as v,B as y,ab as x,q as d,c as u,am as p,o as h}from"./B3fabVUf.js";const k={slots:{base:["relative text-xl text-highlighted font-bold mt-8 mb-3 scroll-mt-[calc(32px+45px+var(--ui-header-height))] lg:scroll-mt-[calc(32px+var(--ui-header-height))] [&>a]:focus-visible:outline-primary [&>a>code]:border-dashed hover:[&>a>code]:border-primary hover:[&>a>code]:text-primary [&>a>code]:text-lg/6 [&>a>code]:font-bold","[&>a>code]:transition-colors"],leading:["absolute -ms-8 top-0.5 opacity-0 group-hover:opacity-100 group-focus:opacity-100 p-1 bg-elevated hover:text-primary rounded-md hidden lg:flex text-muted","transition"],leadingIcon:"size-4 shrink-0",link:"group lg:ps-2 lg:-ms-2"}},C=["id"],q=["href"],_={__name:"ProseH3",props:{id:{type:String,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const l=a,i=g(),s=f("prose.h3",l),{headings:n}=b().public?.mdc||{},o=u(()=>p({extend:p(k),...i.ui?.prose?.h3||{}})()),m=u(()=>l.id&&typeof n?.anchorLinks=="object"&&n.anchorLinks.h3);return(c,B)=>(h(),r("h3",{id:a.id,class:t(o.value.base({class:[e(s)?.base,l.class]}))},[a.id&&m.value?(h(),r("a",{key:0,href:`#${a.id}`,class:t(o.value.link({class:e(s)?.link}))},[v("span",{class:t(o.value.leading({class:e(s)?.leading}))},[y(x,{name:e(i).ui.icons.hash,class:t(o.value.leadingIcon({class:e(s)?.leadingIcon}))},null,8,["name","class"])],2),d(c.$slots,"default")],10,q)):d(c.$slots,"default",{key:1})],10,C))}};export{_ as default};
|
||||
1
docus/dist/_nuxt/4YIHfatS.js
vendored
Normal file
1
docus/dist/_nuxt/4YIHfatS.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as n,a1 as i,e as p,q as c,ac as u,s as d,c as m,am as t,o as f}from"./B3fabVUf.js";const _={base:"py-3 px-4 text-sm align-top border-e border-b first:border-s border-muted [&_code]:text-xs/5 [&_p]:my-0 [&_p]:leading-6 [&_ul]:my-0 [&_ol]:my-0 [&_ul]:ps-4.5 [&_ol]:ps-4.5 [&_li]:leading-6 [&_li]:my-0.5",variants:{align:{left:"text-left",center:"text-center",right:"text-right"}},defaultVariants:{align:"left"}},b={__name:"ProseTd",props:{align:{type:String,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(s){const e=s,a=n(),r=i("prose.td",e),l=m(()=>t({extend:t(_),...a.ui?.prose?.td||{}}));return(o,g)=>(f(),p("td",{class:u(l.value({align:e.align,class:[d(r)?.base,e.class]}))},[c(o.$slots,"default")],2))}};export{b as default};
|
||||
1
docus/dist/_nuxt/AFG10c_a.js
vendored
Normal file
1
docus/dist/_nuxt/AFG10c_a.js
vendored
Normal file
File diff suppressed because one or more lines are too long
9
docus/dist/_nuxt/B2gqyXxK.js
vendored
Normal file
9
docus/dist/_nuxt/B2gqyXxK.js
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
import{bK as q,bL as M,bM as R,G as U,H as B,bN as E,bO as _,d as D,o as g,a as x,p as m,q as W,m as P,s as a,W as I,bP as N,bQ as O,c as C,a0 as j,a1 as V,a4 as z,ar as G,e as A,B as f,bR as H,F as X,v as F,bS as $,bT as Q,bU as S,bV as Z,ac as k,f as T,bW as K,aQ as J,am as L,bX as Y}from"./B3fabVUf.js";var ee=0;function te(s){const i=new WeakMap,r=q();function c(n){if(s.mode!=="popLayout")return;const d=n.offsetParent,y=d instanceof HTMLElement&&d.offsetWidth||0,l={height:n.offsetHeight||0,width:n.offsetWidth||0,top:n.offsetTop,left:n.offsetLeft,right:0};l.right=y-l.width-l.left;const p=s.anchorX==="left"?`left: ${l.left}px`:`right: ${l.right}px`,h=`pop-${ee++}`;n.dataset.motionPopId=h;const e=document.createElement("style");r.value.nonce&&(e.nonce=r.value.nonce),i.set(n,e),document.head.appendChild(e),e.sheet&&e.sheet.insertRule(`
|
||||
[data-motion-pop-id="${h}"] {
|
||||
position: absolute !important;
|
||||
width: ${l.width}px !important;
|
||||
height: ${l.height}px !important;
|
||||
top: ${l.top}px !important;
|
||||
${p} !important;
|
||||
}
|
||||
`)}function v(n){const d=i.get(n);d&&(i.delete(n),M.render(()=>{document.head.removeChild(d)}))}return{addPopStyle:c,removePopStyle:v}}var oe=0;function ae(s){const i=String(oe++),r=new Map,{addPopStyle:c,removePopStyle:v}=te(s);function n(e){const t=[],o=E.get(e);o&&e.getAttribute(_.motionAttribute)===i&&t.push(o);const u=Array.from(e.querySelectorAll(`[${_.motionAttribute}="${i}"]`));for(const w of u){const b=E.get(w);b&&t.push(b)}return t}function d(e,t){const o=r.get(e);o&&(o.remaining.delete(t),o.remaining.size===0&&l(o))}const y={initial:s.initial,custom:s.custom,presenceId:i,onMotionExitComplete:d};R(y),U(()=>{y.initial=void 0});function l(e){v(e.el),e.states.forEach(t=>{t.getSnapshot(t.options,!1)}),e.done(),r.delete(e.el),e.el?.isConnected?e.states[0]?.didUpdate():e.states.forEach(t=>{t.unmount()}),s.onExitComplete?.()}function p(e,t){n(e).forEach(o=>{o.setActive("exit",!1),o.getSnapshot(o.options,!0)}),t()}function h(e,t){y.custom=s.custom;const o=e,u=n(o);if(u.length===0){t(),s.onExitComplete?.();return}const w={remaining:new Set(u),states:u,done:t,el:o};r.set(o,w),c(o),u.forEach(b=>{b.presenceContainer=o,b.setActive("exit",!0),b.getSnapshot(b.options,!1)}),u[0]?.didUpdate()}return B(()=>{r.forEach(e=>{e.states.forEach(t=>{t.unmount()})}),r.clear()}),{enter:p,exit:h}}var ne=D({name:"AnimatePresence",inheritAttrs:!0,__name:"AnimatePresence",props:{mode:{default:"sync"},initial:{type:Boolean,default:!0},as:{},custom:{},onExitComplete:{},anchorX:{default:"left"}},setup(s){const i=s,{enter:r,exit:c}=ae(i),v=C(()=>i.mode!=="wait"?{tag:i.as}:{mode:i.mode==="wait"?"out-in":void 0});return(n,d)=>(g(),x(I(n.mode==="wait"?N:O),P(v.value,{appear:"",css:!1,onLeave:a(c),onEnter:a(r)}),{default:m(()=>[W(n.$slots,"default")]),_:3},16,["onLeave","onEnter"]))}}),se=ne;const ie={slots:{base:"rounded-md w-full",overlay:"fixed inset-0 bg-default/75 backdrop-blur-sm will-change-opacity",content:"fixed inset-0 flex items-center justify-center cursor-zoom-out focus:outline-none",zoomedImage:"w-full h-auto max-w-[95vw] max-h-[95vh] object-contain rounded-md"},variants:{zoom:{true:"will-change-transform"},open:{true:""}},compoundVariants:[{zoom:!0,open:!1,class:"cursor-zoom-in"}]},le=["onClick"],ue=Object.assign({inheritAttrs:!1},{__name:"ProseImg",props:{src:{type:String,required:!0},alt:{type:String,required:!0},width:{type:[String,Number],required:!1},height:{type:[String,Number],required:!1},class:{type:null,required:!1},zoom:{type:Boolean,required:!1,default:!0},ui:{type:Object,required:!1}},setup(s){const i=s,r=j(),c=V("prose.img",i),[v,n]=z(),[d,y]=z(),l=F(!1),p=C(()=>L({extend:L(ie),...r.ui?.prose?.img||{}})({zoom:i.zoom,open:l.value})),h=C(()=>K(i.src,J().app.baseURL)),e=C(()=>`${h.value}::${Y()}`);return i.zoom&&G(window,"scroll",()=>{l.value=!1}),(t,o)=>(g(),A(X,null,[f(a(v),null,{default:m(()=>[(g(),x(I(a($)),P({src:h.value,alt:s.alt,width:s.width,height:s.height},t.$attrs,{class:p.value.base({class:[a(c)?.base,i.class]})}),null,16,["src","alt","width","height","class"]))]),_:1}),f(a(d),null,{default:m(()=>[(g(),x(I(a($)),P({src:h.value,alt:s.alt},t.$attrs,{class:p.value.zoomedImage({class:[a(c)?.zoomedImage]})}),null,16,["src","alt","class"]))]),_:1}),s.zoom?(g(),x(a(H),{key:0,open:l.value,"onUpdate:open":o[0]||(o[0]=u=>l.value=u),modal:!1},{default:m(({close:u})=>[f(a(Q),{"as-child":""},{default:m(()=>[f(a(S),{"layout-id":e.value,"as-child":"",transition:{type:"spring",bounce:.15,duration:.5,ease:"easeInOut"}},{default:m(()=>[f(a(n))]),_:1},8,["layout-id"])]),_:1}),f(a(Z),null,{default:m(()=>[f(a(se),null,{default:m(()=>[l.value?(g(),x(a(S),{key:0,initial:{opacity:0},animate:{opacity:1},exit:{opacity:0},class:k(p.value.overlay({class:[a(c)?.overlay]}))},null,8,["class"])):T("",!0),l.value?(g(),A("div",{key:1,class:k(p.value.content({class:[a(c)?.content]})),onClick:u},[f(a(S),{"as-child":"","layout-id":e.value,transition:{type:"spring",bounce:.15,duration:.5,ease:"easeInOut"}},{default:m(()=>[f(a(y))]),_:1},8,["layout-id"])],10,le)):T("",!0)]),_:2},1024)]),_:2},1024)]),_:1},8,["open"])):(g(),x(a(n),{key:1}))],64))}});export{ue as default};
|
||||
41
docus/dist/_nuxt/B3fabVUf.js
vendored
Normal file
41
docus/dist/_nuxt/B3fabVUf.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docus/dist/_nuxt/B4xqMpdT.js
vendored
Normal file
1
docus/dist/_nuxt/B4xqMpdT.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as r,a1 as c,e as p,q as u,ac as i,s as m,c as f,am as e,o as d}from"./B3fabVUf.js";const _={base:"my-1.5 ps-1.5 leading-7 [&>ul]:my-0"},b={__name:"ProseLi",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const s=a,o=r(),t=c("prose.li",s),l=f(()=>e({extend:e(_),...o.ui?.prose?.li||{}}));return(n,y)=>(d(),p("li",{class:i(l.value({class:[m(t)?.base,s.class]}))},[u(n.$slots,"default")],2))}};export{b as default};
|
||||
1
docus/dist/_nuxt/B6bUjJBd.js
vendored
Normal file
1
docus/dist/_nuxt/B6bUjJBd.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{d as m,bH as I,I as y,h as g,G as F,H as h,o as K,a as S,p as l,B as C,s as n,P as q,q as w,c as u,y as B,bI as T,n as R,bJ as x}from"./B3fabVUf.js";var _=m({__name:"RovingFocusItem",props:{tabStopId:{type:String,required:!1},focusable:{type:Boolean,required:!1,default:!0},active:{type:Boolean,required:!1},allowShiftKey:{type:Boolean,required:!1},asChild:{type:Boolean,required:!1},as:{type:null,required:!1,default:"span"}},setup(d){const r=d,a=I(),f=y(),i=u(()=>r.tabStopId||f),c=u(()=>a.currentTabStopId.value===i.value),{getItems:p,CollectionItem:v}=g();F(()=>{r.focusable&&a.onFocusableItemAdd()}),h(()=>{r.focusable&&a.onFocusableItemRemove()});function b(e){if(e.key==="Tab"&&e.shiftKey){a.onItemShiftTab();return}if(e.target!==e.currentTarget)return;const t=B(e,a.orientation.value,a.dir.value);if(t!==void 0){if(e.metaKey||e.ctrlKey||e.altKey||!r.allowShiftKey&&e.shiftKey)return;e.preventDefault();let o=[...p().map(s=>s.ref).filter(s=>s.dataset.disabled!=="")];if(t==="last")o.reverse();else if(t==="prev"||t==="next"){t==="prev"&&o.reverse();const s=o.indexOf(e.currentTarget);o=a.loop.value?T(o,s+1):o.slice(s+1)}R(()=>x(o))}}return(e,t)=>(K(),S(n(v),null,{default:l(()=>[C(n(q),{tabindex:c.value?0:-1,"data-orientation":n(a).orientation.value,"data-active":e.active?"":void 0,"data-disabled":e.focusable?void 0:"",as:e.as,"as-child":e.asChild,onMousedown:t[0]||(t[0]=o=>{e.focusable?n(a).onItemFocus(i.value):o.preventDefault()}),onFocus:t[1]||(t[1]=o=>n(a).onItemFocus(i.value)),onKeydown:b},{default:l(()=>[w(e.$slots,"default")]),_:3},8,["tabindex","data-orientation","data-active","data-disabled","as","as-child"])]),_:3}))}}),A=_;export{A as R};
|
||||
1
docus/dist/_nuxt/B7V4_Oof.js
vendored
Normal file
1
docus/dist/_nuxt/B7V4_Oof.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const a={or:"veya",error:{title:"Sayfa bulunamadı",description:"Üzgünüz, bu sayfa bulunamadı."}},o={copy:{page:"Sayfayı kopyala",link:"Markdown bağlantısını kopyala",view:"Markdown olarak görüntüle",gpt:"ChatGPT'de aç",claude:"Claude'da aç"},links:"Topluluk",toc:"Bu sayfada",menu:"Menü",report:"Sorun bildir",edit:"Bu sayfayı düzenle"},n={copyLogo:"Logoyu kopyala",copyWordmark:"Wordmark'ı kopyala",downloadLogo:"Logoyu indir",downloadWordmark:"Wordmark'ı indir",brandAssets:"Marka materyalleri",logoCopied:"Logo kopyalandı",wordmarkCopied:"Wordmark kopyalandı",logoDownloaded:"Logo indirildi",wordmarkDownloaded:"Wordmark indirildi",copyLogoFailed:"Logo kopyalanamadı",copyWordmarkFailed:"Wordmark kopyalanamadı"},e={title:"Yapay zekaya sor",placeholder:"Bir soru sorun...",tooltip:"Yapay zekaya bir soru sorun",tryAsking:"Bir soru sormayı deneyin",askAnything:"Her şeyi sor...",clearChat:"Sohbeti temizle",close:"Kapat",expand:"Genişlet",collapse:"Daralt",thinking:"Düşünüyorum...",askMeAnything:"Her şeyi sor",askMeAnythingDescription:"Belgelerde gezinme, kavramları anlama ve yanıt bulma konusunda yardım alın.",faq:"SSS",chatCleared:"Sohbet yenilendiğinde temizlenir",lineBreak:"Satır sonu",explainWithAi:"Yapay zeka ile açıklayın",toolListPages:"Listelenen dokümantasyon sayfaları",toolReadPage:"Oku",loading:{searching:"Dokümantasyonda arama",reading:"Dokümanlar okunuyor",analyzing:"İçerik analiz ediliyor",finding:"En iyi yanıt bulunuyor",finished:"Kullanılan kaynaklar"}},r={common:a,docs:o,logo:n,assistant:e};export{e as assistant,a as common,r as default,o as docs,n as logo};
|
||||
1
docus/dist/_nuxt/B91fwRQf.js
vendored
Normal file
1
docus/dist/_nuxt/B91fwRQf.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{$ as h,aR as b,a0 as v,a1 as x,e as c,ac as r,s as e,B as u,af as d,ag as q,f as k,ak as C,q as S,m as $,c as B,am as p,o as m}from"./B3fabVUf.js";import w from"./C-sCiTCq.js";const P={slots:{root:"relative my-5 group",header:"flex items-center gap-1.5 border border-muted bg-default border-b-0 relative rounded-t-md px-4 py-3",filename:"text-default text-sm/6",icon:"size-4 shrink-0",copy:"absolute top-[11px] right-[11px] lg:opacity-0 lg:group-hover:opacity-100 transition",base:"group font-mono text-sm/6 border border-muted bg-muted rounded-md px-4 py-3 whitespace-pre-wrap break-words overflow-x-auto focus:outline-none **:[.line]:block **:[.line.highlight]:-mx-4 **:[.line.highlight]:px-4 **:[.line.highlight]:bg-accented/50!"},variants:{filename:{true:{root:"[&>pre]:rounded-t-none [&>pre]:my-0 my-5"}}}},A={__name:"ProsePre",props:{icon:{type:null,required:!1},code:{type:String,required:!1},language:{type:String,required:!1},filename:{type:String,required:!1},highlights:{type:Array,required:!1},hideHeader:{type:Boolean,required:!1},meta:{type:String,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const t=a,{t:f}=h(),{copy:g,copied:y}=b(),l=v(),s=x("prose.pre",t),o=B(()=>p({extend:p(P),...l.ui?.prose?.pre||{}})());return(i,n)=>(m(),c("div",{class:r(o.value.root({class:[e(s)?.root],filename:!!a.filename}))},[a.filename&&!a.hideHeader?(m(),c("div",{key:0,class:r(o.value.header({class:e(s)?.header}))},[u(w,{icon:a.icon,filename:a.filename,class:r(o.value.icon({class:e(s)?.icon}))},null,8,["icon","filename","class"]),d("span",{class:r(o.value.filename({class:e(s)?.filename}))},q(a.filename),3)],2)):k("",!0),u(C,{icon:e(y)?e(l).ui.icons.copyCheck:e(l).ui.icons.copy,color:"neutral",variant:"outline",size:"sm","aria-label":e(f)("prose.pre.copy"),class:r(o.value.copy({class:e(s)?.copy})),tabindex:"-1",onClick:n[0]||(n[0]=z=>e(g)(t.code||""))},null,8,["icon","aria-label","class"]),d("pre",$({class:o.value.base({class:[e(s)?.base,t.class]})},i.$attrs),[S(i.$slots,"default")],16)],2))}};export{A as default};
|
||||
1
docus/dist/_nuxt/BKwruWNQ.js
vendored
Normal file
1
docus/dist/_nuxt/BKwruWNQ.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as p,a1 as u,e as d,af as i,q as f,ac as o,s as t,c as m,am as r,o as b}from"./B3fabVUf.js";const v={slots:{root:"relative my-5 overflow-x-auto",base:"w-full border-separate border-spacing-0 rounded-md"}},g={__name:"ProseTable",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(l){const e=l,c=p(),s=u("prose.table",e),a=m(()=>r({extend:r(v),...c.ui?.prose?.table||{}})());return(n,_)=>(b(),d("div",{class:o(a.value.root({class:[t(s)?.root,e.class]}))},[i("table",{class:o(a.value.base({class:t(s)?.base}))},[f(n.$slots,"default")],2)],2))}};export{g as default};
|
||||
1
docus/dist/_nuxt/BLnzyn6S.js
vendored
Normal file
1
docus/dist/_nuxt/BLnzyn6S.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{r as a}from"./Cf5i2Hk_.js";import t from"./Jc8Ntx_l.js";import{a0 as s,a as e,p as n,s as p,o as c}from"./B3fabVUf.js";import"./BN_7HF1G.js";const d={__name:"ProseCaution",setup(i){const o=s();return(r,f)=>(c(),e(t,{color:"error",icon:p(o).ui.icons.caution},{default:n(()=>[a(r.$slots,"default",{mdcUnwrap:"p"})]),_:3},8,["icon"]))}};export{d as default};
|
||||
1
docus/dist/_nuxt/BN_7HF1G.js
vendored
Normal file
1
docus/dist/_nuxt/BN_7HF1G.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const y=["p","h1","h2","h3","h4","h5","h6","li"];function f(r,i){return r.type===i||typeof r.type=="object"&&r.type.tag===i||r.tag===i}function u(r){return f(r,"text")||f(r,Symbol.for("v-txt"))}function l(r){return Array.isArray(r.children)||typeof r.children=="string"?r.children:typeof r.children?.default=="function"?r.children.default():[]}function n(r){if(!r)return"";if(Array.isArray(r))return r.map(n).join("");if(u(r))return r.value||r.children||"";const i=l(r);return Array.isArray(i)?i.map(n).filter(Boolean).join(""):""}function h(r,i=[]){if(Array.isArray(r))return r.flatMap(e=>h(e,i));let t=r;return i.some(e=>e==="*"||f(r,e))&&(t=l(r)||r,!Array.isArray(t)&&y.some(e=>f(r,e))&&(t=[t])),t}function p(r,i=[]){return r=Array.isArray(r)?r:[r],i.length?r.flatMap(t=>p(h(t,[i[0]]),i.slice(1))).filter(t=>!(u(t)&&n(t).trim()==="")):r}function a(r,i=[]){return typeof i=="string"&&(i=i.split(/[,\s]/).map(t=>t.trim()).filter(Boolean)),i.length?p(r,i).reduce((t,e)=>(u(e)?typeof t[t.length-1]=="string"?t[t.length-1]+=e.children:t.push(e.children):t.push(e),t),[]):r}export{a as f,n};
|
||||
1
docus/dist/_nuxt/BOynLmEf.js
vendored
Normal file
1
docus/dist/_nuxt/BOynLmEf.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const a={or:"ou",error:{title:"Página não encontrada",description:"Desculpe, mas esta página não pôde ser encontrada."}},o={copy:{page:"Copiar página",link:"Copiar página em Markdown",view:"Visualizar como Markdown",gpt:"Abrir no ChatGPT",claude:"Abrir no Claude"},links:"Comunidade",toc:"Nesta página",menu:"Menu",report:"Reportar um erro",edit:"Editar esta página"},e={copyLogo:"Copiar logo",copyWordmark:"Copiar wordmark",downloadLogo:"Baixar logo",downloadWordmark:"Baixar wordmark",brandAssets:"Recursos da marca",logoCopied:"Logo copiado",wordmarkCopied:"Wordmark copiado",logoDownloaded:"Logo baixado",wordmarkDownloaded:"Wordmark baixado",copyLogoFailed:"Falha ao copiar o logo",copyWordmarkFailed:"Falha ao copiar o wordmark"},r={title:"Pergunte à IA",placeholder:"Faça uma pergunta...",tooltip:"Faça uma pergunta à IA",tryAsking:"Tente fazer uma pergunta",askAnything:"Pergunte qualquer coisa...",clearChat:"Limpar chat",close:"Fechar",expand:"Expandir",collapse:"Recolher",thinking:"Pensando...",askMeAnything:"Perguntar qualquer coisa",askMeAnythingDescription:"Obtenha ajuda para navegar pela documentação, entender conceitos e encontrar respostas.",faq:"Perguntas frequentes",chatCleared:"O chat é apagado ao atualizar",lineBreak:"Quebra de linha",explainWithAi:"Explicar com IA",toolListPages:"Páginas de documentação listadas",toolReadPage:"Ler",loading:{searching:"Pesquisar a documentação",reading:"Ler os documentos",analyzing:"Analisar o conteúdo",finding:"Encontrar a melhor resposta",finished:"Fontes utilizadas"}},n={common:a,docs:o,logo:e,assistant:r};export{r as assistant,a as common,n as default,o as docs,e as logo};
|
||||
1
docus/dist/_nuxt/BRj7a3jo.js
vendored
Normal file
1
docus/dist/_nuxt/BRj7a3jo.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const e={or:"oder",error:{title:"Säit net fonnt",description:"Et deet ons leed, awer d'Säit déi Dir sicht gëtt et net."}},n={copy:{page:"Säit kopéieren",link:"Markdown Säit kopéieren",view:"Als Markdown kucken",gpt:"An ChatGPT opmaachen",claude:"An Claude opmaachen"},links:"Gemeinschaft",toc:"Op dëser Säit",menu:"Menü",report:"Problem mellen",edit:"Dës Säit änneren"},o={copyLogo:"Logo kopéieren",copyWordmark:"Wortmark kopéieren",downloadLogo:"Logo eroflueden",downloadWordmark:"Wortmark eroflueden",brandAssets:"Marken-Materialien",logoCopied:"Logo kopéiert",wordmarkCopied:"Wortmark kopéiert",logoDownloaded:"Logo erofgelueden",wordmarkDownloaded:"Wortmark erofgelueden",copyLogoFailed:"Logo konnt net kopéiert ginn",copyWordmarkFailed:"Wortmark konnt net kopéiert ginn"},t={title:"Frot d'AI",placeholder:"Stell eng Fro...",tooltip:"Stell der AI eng Fro",tryAsking:"Probéieren eng Fro ze stellen",askAnything:"Frot iergendeppes...",clearChat:"Chat läschen",close:"Zoumaachen",expand:"Erweideren",collapse:"Zesummeklappen",thinking:"Denken...",askMeAnything:"Frot alles",askMeAnythingDescription:"Kritt Hëllef fir an der Dokumentatioun ze navigéieren, Konzepter ze verstoen an Äntwerten ze fannen.",faq:"FAQ",chatCleared:"Chat gëtt op Erfrëschung gereinegt",lineBreak:"Zeilepaus",explainWithAi:"Erklär mat AI",toolListPages:"Opgelëschte Dokumentatiounssäiten",toolReadPage:"Liesen",loading:{searching:"D'Dokumentatioun gëtt duerchsicht",reading:"D'Dokumenter ginn duerchgelies",analyzing:"Den Inhalt gëtt analyséiert",finding:"Déi bescht Äntwert gëtt fonnt",finished:"Benotzte Quellen"}},r={common:e,docs:n,logo:o,assistant:t};export{t as assistant,e as common,r as default,n as docs,o as logo};
|
||||
1
docus/dist/_nuxt/BXokzvm8.js
vendored
Normal file
1
docus/dist/_nuxt/BXokzvm8.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as c,a1 as i,e as l,q as p,ac as u,s as m,c as d,am as a,o as f,ah as b,ag as g}from"./B3fabVUf.js";const y={base:"*:first:mt-0 *:last:mb-0 *:my-1.5"},I={__name:"ProseTabsItem",props:{label:{type:String,required:!0},description:{type:String,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(e){const s=e,t=c(),r=i("prose.tabsItem",s),o=d(()=>a({extend:a(y),...t.ui?.prose?.tabsItem||{}}));return(n,q)=>(f(),l("div",{class:u(o.value({class:[m(r)?.base,s.class]}))},[p(n.$slots,"default",{},()=>[b(g(e.description),1)])],2))}};export{I as default};
|
||||
1
docus/dist/_nuxt/BeOsfPZ5.js
vendored
Normal file
1
docus/dist/_nuxt/BeOsfPZ5.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const a={or:"o",error:{title:"Pagina non trovata",description:"Ci scusiamo, ma sembra che questa pagina non sia disponibile."}},o={copy:{page:"Copia pagina",link:"Copia pagina Markdown",view:"Visualizza come Markdown",gpt:"Apri in ChatGPT",claude:"Apri in Claude"},links:"Comunità",toc:"In questa pagina",menu:"Menu",report:"Segnala un problema",edit:"Modifica questa pagina"},i={copyLogo:"Copia logo",copyWordmark:"Copia wordmark",downloadLogo:"Scarica logo",downloadWordmark:"Scarica wordmark",brandAssets:"Risorse del brand",logoCopied:"Logo copiato",wordmarkCopied:"Wordmark copiato",logoDownloaded:"Logo scaricato",wordmarkDownloaded:"Wordmark scaricato",copyLogoFailed:"Impossibile copiare il logo",copyWordmarkFailed:"Impossibile copiare il wordmark"},e={title:"Chiedi all'AI",placeholder:"Fai una domanda...",tooltip:"Fai una domanda all'IA",tryAsking:"Prova a fare una domanda",askAnything:"Chiedi qualsiasi cosa...",clearChat:"Cancella chat",close:"Chiudi",expand:"Espandi",collapse:"Comprimi",thinking:"Sto pensando...",askMeAnything:"Chiedi qualsiasi cosa",askMeAnythingDescription:"Ricevi assistenza per esplorare la documentazione, comprendere i concetti e trovare risposte.",faq:"Domande frequenti",chatCleared:"La chat viene cancellata all'aggiornamento",lineBreak:"Interruzione di riga",explainWithAi:"Spiega con l'IA",toolListPages:"Pagine di documentazione elencate",toolReadPage:"Leggi",loading:{searching:"Ricerca della documentazione",reading:"Leggere i documenti",analyzing:"Analizzare il contenuto",finding:"Trovare la risposta migliore",finished:"Fonti utilizzate"}},n={common:a,docs:o,logo:i,assistant:e};export{e as assistant,a as common,n as default,o as docs,i as logo};
|
||||
1
docus/dist/_nuxt/BePYYsBE.js
vendored
Normal file
1
docus/dist/_nuxt/BePYYsBE.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"හෝ",error:{title:"පිටුව හමු නොවීය",description:"අපට කණගාටුයි, නමුත් මෙම පිටුව සොයාගත නොහැකි විය."}},a={copy:{page:"පිටුව පිටපත් කරන්න",link:"Markdown පිටුව පිටපත් කරන්න",view:"Markdown ලෙස බලන්න",gpt:"ChatGPT හි විවෘත කරන්න",claude:"Claude හි විවෘත කරන්න"},links:"Community",toc:"මෙම පිටුවේ",menu:"මෙනුව",report:"ගැටලුවක් වාර්තා කරන්න",edit:"මෙම පිටුව සංස්කරණය කරන්න"},e={copyLogo:"ලාංඡනය පිටපත් කරන්න",copyWordmark:"වචන ලකුණ පිටපත් කරන්න",downloadLogo:"ලාංඡනය බාගන්න",downloadWordmark:"වචන ලකුණ බාගන්න",brandAssets:"වෙළඳ නාම සම්පත්",logoCopied:"ලාංඡනය පිටපත් කරන ලදී",wordmarkCopied:"වචන ලකුණ පිටපත් කරන ලදී",logoDownloaded:"ලාංඡනය බාගත කරන ලදී",wordmarkDownloaded:"වචන ලකුණ බාගත කරන ලදී",copyLogoFailed:"ලාංඡනය පිටපත් කිරීමට අසමත් විය",copyWordmarkFailed:"වචන ලකුණ පිටපත් කිරීමට අසමත් විය"},n={title:"AI අහන්න",placeholder:"ප්රශ්නයක් අහන්න...",tooltip:"AI ගෙන් ප්රශ්නයක් අසන්න",tryAsking:"ප්රශ්නයක් ඇසීමට උත්සාහ කරන්න",askAnything:"ඕන දෙයක් අහන්න...",clearChat:"කතාබස් පැහැදිලි කරන්න",close:"වසන්න",expand:"පුළුල් කරන්න",collapse:"හකුළන්න",thinking:"සිතමින්...",askMeAnything:"ඕන දෙයක් අහන්න",askMeAnythingDescription:"ලේඛන සැරිසැරීමට, සංකල්ප තේරුම් ගැනීමට සහ පිළිතුරු සෙවීමට උදවු ලබා ගන්න.",faq:"නිතර අසන පැන",chatCleared:"නැවුම් කිරීමේදී කතාබස් හිස් වේ",lineBreak:"රේඛා බිඳීම",explainWithAi:"AI සමඟ පැහැදිලි කරන්න",toolListPages:"ලැයිස්තුගත ලේඛන පිටු",toolReadPage:"කියවන්න",loading:{searching:"ලේඛන සෙවීම",reading:"ලේඛන හරහා කියවීම",analyzing:"අන්තර්ගතය විශ්ලේෂණය කිරීම",finding:"හොඳම පිළිතුර සොයා ගැනීම",finished:"භාවිතා කරන ලද මූලාශ්ර"}},i={common:o,docs:a,logo:e,assistant:n};export{n as assistant,o as common,i as default,a as docs,e as logo};
|
||||
1
docus/dist/_nuxt/BecbEIqG.js
vendored
Normal file
1
docus/dist/_nuxt/BecbEIqG.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{Q as C,a0 as q,a1 as S,o as r,a as d,p,e as g,ac as o,s as i,q as u,ab as $,f as c,af as m,a7 as w,m as F,ah as y,ag as v,P as I,c as k,am as b,c4 as P}from"./B3fabVUf.js";const j={slots:{root:"relative rounded-sm",wrapper:"",leading:"inline-flex items-center justify-center",leadingIcon:"size-5 shrink-0 text-primary",title:"text-base text-pretty font-semibold text-highlighted",description:"text-[15px] text-pretty text-muted"},variants:{orientation:{horizontal:{root:"flex items-start gap-2.5",leading:"p-0.5"},vertical:{leading:"mb-2.5"}},to:{true:{root:["has-focus-visible:ring-2 has-focus-visible:ring-primary","transition"]}},title:{true:{description:"mt-1"}}}},B=Object.assign({inheritAttrs:!1},{__name:"UPageFeature",props:{as:{type:null,required:!1},icon:{type:null,required:!1},title:{type:String,required:!1},description:{type:String,required:!1},orientation:{type:null,required:!1,default:"horizontal"},to:{type:null,required:!1},target:{type:[String,Object,null],required:!1},onClick:{type:Function,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(e){const t=e,s=C(),h=q(),l=S("pageFeature",t),a=k(()=>b({extend:b(j),...h.ui?.pageFeature||{}})({orientation:t.orientation,title:!!t.title||!!s.title,to:!!t.to||!!t.onClick})),x=k(()=>(s.title&&P(s.title())||t.title||"Feature link").trim());return(n,f)=>(r(),d(i(I),{as:e.as,"data-orientation":e.orientation,"data-slot":"root",class:o(a.value.root({class:[i(l)?.root,t.class]})),onClick:e.onClick},{default:p(()=>[e.icon||s.leading?(r(),g("div",{key:0,"data-slot":"leading",class:o(a.value.leading({class:i(l)?.leading}))},[u(n.$slots,"leading",{ui:a.value},()=>[e.icon?(r(),d($,{key:0,name:e.icon,"data-slot":"leadingIcon",class:o(a.value.leadingIcon({class:i(l)?.leadingIcon}))},null,8,["name","class"])):c("",!0)])],2)):c("",!0),m("div",{"data-slot":"wrapper",class:o(a.value.wrapper({class:i(l)?.wrapper}))},[e.to?(r(),d(w,F({key:0,"aria-label":x.value},{to:e.to,target:e.target,...n.$attrs},{class:"focus:outline-none peer",raw:""}),{default:p(()=>[...f[0]||(f[0]=[m("span",{class:"absolute inset-0","aria-hidden":"true"},null,-1)])]),_:1},16,["aria-label"])):c("",!0),u(n.$slots,"default",{},()=>[e.title||s.title?(r(),g("div",{key:0,"data-slot":"title",class:o(a.value.title({class:i(l)?.title}))},[u(n.$slots,"title",{},()=>[y(v(e.title),1)])],2)):c("",!0),e.description||s.description?(r(),g("div",{key:1,"data-slot":"description",class:o(a.value.description({class:i(l)?.description}))},[u(n.$slots,"description",{},()=>[y(v(e.description),1)])],2)):c("",!0)])],2)]),_:3},8,["as","data-orientation","class","onClick"]))}});export{B as default};
|
||||
1
docus/dist/_nuxt/BfXwXGCj.js
vendored
Normal file
1
docus/dist/_nuxt/BfXwXGCj.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{aQ as c,e as t,s as d,q as n,c as p,o as r}from"./B3fabVUf.js";const u=["id"],f=["href"],k={__name:"ProseH5",props:{id:{type:String,required:!1}},setup(a){const e=a,{headings:o}=c().public.mdc,i=p(()=>e.id&&(typeof o?.anchorLinks=="boolean"&&o?.anchorLinks===!0||typeof o?.anchorLinks=="object"&&o?.anchorLinks?.h5));return(s,h)=>(r(),t("h5",{id:e.id},[e.id&&d(i)?(r(),t("a",{key:0,href:`#${e.id}`},[n(s.$slots,"default")],8,f)):n(s.$slots,"default",{key:1})],8,u))}};export{k as default};
|
||||
1
docus/dist/_nuxt/Bkurqz2d.js
vendored
Normal file
1
docus/dist/_nuxt/Bkurqz2d.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"ή",error:{title:"Η σελίδα δεν βρέθηκε",description:"Λυπούμαστε, αλλά η σελίδα που αναζητάτε δεν υπάρχει."}},a={copy:{page:"Αντιγραφή σελίδας",link:"Αντιγραφή σελίδας Markdown",view:"Προβολή ως Markdown",gpt:"Άνοιγμα στο ChatGPT",claude:"Άνοιγμα στο Claude"},links:"Κοινότητα",toc:"Σε αυτή τη σελίδα",menu:"Μενού",report:"Αναφορά προβλήματος",edit:"Επεξεργασία αυτής της σελίδας"},e={copyLogo:"Αντιγραφή λογοτύπου",copyWordmark:"Αντιγραφή wordmark",downloadLogo:"Λήψη λογοτύπου",downloadWordmark:"Λήψη wordmark",brandAssets:"Υλικά επωνυμίας",logoCopied:"Το λογότυπο αντιγράφηκε",wordmarkCopied:"Το wordmark αντιγράφηκε",logoDownloaded:"Το λογότυπο κατέβηκε",wordmarkDownloaded:"Το wordmark κατέβηκε",copyLogoFailed:"Αποτυχία αντιγραφής λογοτύπου",copyWordmarkFailed:"Αποτυχία αντιγραφής wordmark"},d={title:"Ερώτηση στην τεχνητή νοημοσύνη",placeholder:"Κάντε μια ερώτηση...",tooltip:"Κάντε μια ερώτηση στην τεχνητή νοημοσύνη",tryAsking:"Δοκιμάστε να κάνετε μια ερώτηση",askAnything:"Ρωτήστε οτιδήποτε...",clearChat:"Εκκαθάριση συνομιλίας",close:"Κλείσιμο",expand:"Ανάπτυξη",collapse:"Σύμπτυξη",thinking:"Σκέψη...",askMeAnything:"Ρωτήστε οτιδήποτε",askMeAnythingDescription:"Λάβετε βοήθεια για την πλοήγηση στην τεκμηρίωση, την κατανόηση των εννοιών και την εύρεση απαντήσεων.",faq:"Συχνές Ερωτήσεις",chatCleared:"Η συνομιλία εκκαθαρίζεται κατά την ανανέωση",lineBreak:"Διακοπή γραμμής",explainWithAi:"Εξηγήστε με τεχνητή νοημοσύνη",toolListPages:"Καταχωρημένες σελίδες τεκμηρίωσης",toolReadPage:"Ανάγνωση",loading:{searching:"Αναζήτηση στην τεκμηρίωση",reading:"Ανάγνωση των εγγράφων",analyzing:"Ανάλυση του περιεχομένου",finding:"Βρίσκοντας την καλύτερη απάντηση",finished:"Πηγές που χρησιμοποιήθηκαν"}},n={common:o,docs:a,logo:e,assistant:d};export{d as assistant,o as common,n as default,a as docs,e as logo};
|
||||
1
docus/dist/_nuxt/BlIgAVvl.js
vendored
Normal file
1
docus/dist/_nuxt/BlIgAVvl.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as c,a1 as p,e as l,q as u,ac as m,s as i,c as f,am as s,o as d}from"./B3fabVUf.js";const _={base:""},g={__name:"ProseEm",props:{class:{type:String,required:!1},ui:{type:Object,required:!1}},setup(a){const e=a,o=c(),t=p("prose.em",e),r=f(()=>s({extend:s(_),...o.ui?.prose?.em||{}}));return(n,C)=>(d(),l("em",{class:m(r.value({class:[i(t)?.base,e.class]}))},[u(n.$slots,"default")],2))}};export{g as default};
|
||||
1
docus/dist/_nuxt/BlxJeUnt.js
vendored
Normal file
1
docus/dist/_nuxt/BlxJeUnt.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"або",error:{title:"Сторінку не знайдено",description:"Вибачте, але сторінку, яку ви шукаєте, не знайдено."}},a={copy:{page:"Скопіювати сторінку",link:"Скопіювати Markdown сторінку",view:"Переглянути як Markdown",gpt:"Відкрити в ChatGPT",claude:"Відкрити в Claude"},links:"Спільнота",toc:"На цій сторінці",menu:"Меню",report:"Повідомити про проблему",edit:"Редагувати цю сторінку"},e={copyLogo:"Копіювати логотип",copyWordmark:"Копіювати словесний знак",downloadLogo:"Завантажити логотип",downloadWordmark:"Завантажити словесний знак",brandAssets:"Матеріали бренду",logoCopied:"Логотип скопійовано",wordmarkCopied:"Словесний знак скопійовано",logoDownloaded:"Логотип завантажено",wordmarkDownloaded:"Словесний знак завантажено",copyLogoFailed:"Не вдалося скопіювати логотип",copyWordmarkFailed:"Не вдалося скопіювати словесний знак"},n={title:"Запитайте ШІ",placeholder:"Задайте питання...",tooltip:"Задайте питання ШІ",tryAsking:"Спробуйте задати питання",askAnything:"Запитайте будь-що...",clearChat:"Очистити чат",close:"Закрити",expand:"Розгорнути",collapse:"Згорнути",thinking:"Думаючи...",askMeAnything:"Запитайте будь-що",askMeAnythingDescription:"Отримайте допомогу в навігації документацією, розумінні понять і пошуку відповідей.",faq:"Поширені запитання",chatCleared:"Чат очищається під час оновлення",lineBreak:"Розрив рядка",explainWithAi:"Поясніть за допомогою ШІ",toolListPages:"Перелічені сторінки документації",toolReadPage:"Читати",loading:{searching:"Пошук документації",reading:"Читання документів",analyzing:"Аналіз змісту",finding:"Пошук найкращої відповіді",finished:"Використані джерела"}},i={common:o,docs:a,logo:e,assistant:n};export{n as assistant,o as common,i as default,a as docs,e as logo};
|
||||
1
docus/dist/_nuxt/BnHu5O7o.js
vendored
Normal file
1
docus/dist/_nuxt/BnHu5O7o.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{_ as o}from"./DXQrCARz.js";import{bY as a,a as n,p as s,o as r,q as c}from"./B3fabVUf.js";const _={};function f(t,l){const e=o;return r(),n(e,null,{default:s(()=>[c(t.$slots,"default")]),_:3})}const m=a(_,[["render",f]]);export{m as default};
|
||||
1
docus/dist/_nuxt/BnW3dU0n.js
vendored
Normal file
1
docus/dist/_nuxt/BnW3dU0n.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docus/dist/_nuxt/Bnc4fItI.js
vendored
Normal file
1
docus/dist/_nuxt/Bnc4fItI.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{r as f}from"./Cf5i2Hk_.js";import{Q as y,a0 as x,a1 as q,a as v,p as g,ac as r,s as t,P as k,c as C,am as u,o as l,af as S,e as i,ag as o,f as n,ah as w}from"./B3fabVUf.js";import"./BN_7HF1G.js";const B={slots:{root:"my-5",container:"flex items-center gap-3 font-mono text-sm",name:"font-semibold text-primary",wrapper:"flex-1 flex items-center gap-1.5 text-xs",required:"rounded-sm bg-error/10 text-error px-1.5 py-0.5",type:"rounded-sm bg-elevated text-toned px-1.5 py-0.5",description:"mt-3 text-muted text-sm [&_code]:text-xs/4"}},V={__name:"ProseField",props:{as:{type:null,required:!1},name:{type:String,required:!1},type:{type:String,required:!1},description:{type:String,required:!1},required:{type:Boolean,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(e){const c=e,d=y(),p=x(),s=q("prose.field",c),a=C(()=>u({extend:u(B),...p.ui?.prose?.field||{}})());return(m,b)=>(l(),v(t(k),{as:e.as,class:r(a.value.root({class:[t(s)?.root,c.class]}))},{default:g(()=>[S("div",{class:r(a.value.container({class:t(s)?.container}))},[e.name?(l(),i("span",{key:0,class:r(a.value.name({class:t(s)?.name}))},o(e.name),3)):n("",!0),e.type||e.required?(l(),i("div",{key:1,class:r(a.value.wrapper({class:t(s)?.wrapper}))},[e.type?(l(),i("span",{key:0,class:r(a.value.type({class:t(s)?.type}))},o(e.type),3)):n("",!0),e.required?(l(),i("span",{key:1,class:r(a.value.required({class:t(s)?.required}))}," required ",2)):n("",!0)],2)):n("",!0)],2),d.default||e.description?(l(),i("div",{key:0,class:r(a.value.description({class:t(s)?.description}))},[f(m.$slots,"default",{mdcUnwrap:"p"},()=>[w(o(e.description),1)])],2)):n("",!0)]),_:3},8,["as","class"]))}};export{V as default};
|
||||
1
docus/dist/_nuxt/BosS-MS4.js
vendored
Normal file
1
docus/dist/_nuxt/BosS-MS4.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const e={or:"oder",error:{title:"Seite nicht gefunden",description:"Es tut uns leid, aber diese Seite konnte nicht gefunden werden."}},n={copy:{page:"Seite kopieren",link:"Markdown-Seite kopieren",view:"Als Markdown anzeigen",gpt:"In ChatGPT öffnen",claude:"In Claude öffnen"},links:"Community",toc:"Auf dieser Seite",menu:"Menü",report:"Problem melden",edit:"Diese Seite bearbeiten"},o={copyLogo:"Logo kopieren",copyWordmark:"Wortmarke kopieren",downloadLogo:"Logo herunterladen",downloadWordmark:"Wortmarke herunterladen",brandAssets:"Markenmaterialien",logoCopied:"Logo kopiert",wordmarkCopied:"Wortmarke kopiert",logoDownloaded:"Logo heruntergeladen",wordmarkDownloaded:"Wortmarke heruntergeladen",copyLogoFailed:"Logo konnte nicht kopiert werden",copyWordmarkFailed:"Wortmarke konnte nicht kopiert werden"},i={title:"AI fragen",placeholder:"Stellen Sie eine Frage...",tooltip:"KI eine Frage stellen",tryAsking:"Versuchen Sie, eine Frage zu stellen",askAnything:"Fragen Sie alles...",clearChat:"Chat löschen",close:"Schließen",expand:"Erweitern",collapse:"Einklappen",thinking:"Denken...",askMeAnything:"Fragen Sie alles",askMeAnythingDescription:"Erhalten Sie Hilfe beim Navigieren durch die Dokumentation, beim Verstehen von Konzepten und beim Finden von Antworten.",faq:"FAQ",chatCleared:"Chat wird beim Aktualisieren gelöscht",lineBreak:"Zeilenumbruch",explainWithAi:"Mit KI erklären",toolListPages:"Aufgelistete Dokumentationsseiten",toolReadPage:"Lesen",loading:{searching:"Durchsuchen der Dokumentation",reading:"Lesen der Dokumente",analyzing:"Analysieren des Inhalts",finding:"Die beste Antwort finden",finished:"Verwendete Quellen"}},t={common:e,docs:n,logo:o,assistant:i};export{i as assistant,e as common,t as default,n as docs,o as logo};
|
||||
1
docus/dist/_nuxt/Bqoc_FpL.js
vendored
Normal file
1
docus/dist/_nuxt/Bqoc_FpL.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"או",error:{title:"העמוד לא נמצא",description:"אנו מתנצלים, אך העמוד שאתה מחפש לא קיים."}},a={copy:{page:"העתק עמוד",link:"העתק עמוד Markdown",view:"הצג כ-Markdown",gpt:"פתח ב-ChatGPT",claude:"פתח ב-Claude"},links:"קהילה",toc:"בעמוד זה",menu:"תפריט",report:"דווח על בעיה",edit:"ערוך עמוד זה"},e={copyLogo:"העתק לוגו",copyWordmark:"העתק סימן מילולי",downloadLogo:"הורד לוגו",downloadWordmark:"הורד סימן מילולי",brandAssets:"נכסי מותג",logoCopied:"הלוגו הועתק",wordmarkCopied:"הסימן המילולי הועתק",logoDownloaded:"הלוגו הורד",wordmarkDownloaded:"הסימן המילולי הורד",copyLogoFailed:"העתקת הלוגו נכשלה",copyWordmarkFailed:"העתקת הסימן המילולי נכשלה"},n={title:"שאל את הבינה המלאכותית",placeholder:"שאל שאלה...",tooltip:"שאל את הבינה המלאכותית שאלה",tryAsking:"נסה לשאול שאלה",askAnything:"שאל כל דבר...",clearChat:"נקה צ'אט",close:"סגירה",expand:"הרחבה",collapse:"כיווץ התצוגה",thinking:"חושב...",askMeAnything:"שאלו כל דבר",askMeAnythingDescription:"קבלת עזרה בניווט בתיעוד, הבנת מושגים ומציאת תשובות.",faq:"שאלות נפוצות",chatCleared:"הצ'אט נוקה בעת הרענון",lineBreak:"מעבר שורה",explainWithAi:"הסבר באמצעות בינה מלאכותית",toolListPages:"דפי תיעוד רשומים",toolReadPage:"קריאה",loading:{searching:"חיפוש בתיעוד",reading:"קריאה דרך המסמכים",analyzing:"ניתוח התוכן",finding:"מציאת התשובה הטובה ביותר",finished:"מקורות בשימוש"}},i={common:o,docs:a,logo:e,assistant:n};export{n as assistant,o as common,i as default,a as docs,e as logo};
|
||||
1
docus/dist/_nuxt/BscxgZ9w.js
vendored
Normal file
1
docus/dist/_nuxt/BscxgZ9w.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"lub",error:{title:"Nie znaleziono strony",description:"Przepraszamy, ale nie znaleziono tej strony."}},a={copy:{page:"Skopiuj stronę",link:"Skopiuj stronę Markdown",view:"Wyświetl jako Markdown",gpt:"Otwórz w ChatGPT",claude:"Otwórz w Claude"},links:"Społeczność",toc:"Na tej stronie",menu:"Menu",report:"Zgłoś problem",edit:"Edytuj tę stronę"},i={copyLogo:"Kopiuj logo",copyWordmark:"Kopiuj wordmark",downloadLogo:"Pobierz logo",downloadWordmark:"Pobierz wordmark",brandAssets:"Materiały marki",logoCopied:"Logo skopiowane",wordmarkCopied:"Wordmark skopiowany",logoDownloaded:"Logo pobrane",wordmarkDownloaded:"Wordmark pobrany",copyLogoFailed:"Nie udało się skopiować logo",copyWordmarkFailed:"Nie udało się skopiować wordmarku"},e={title:"Zapytaj SI",placeholder:"Zadaj pytanie...",tooltip:"Zadaj AI pytanie",tryAsking:"Spróbuj zadać pytanie",askAnything:"Zapytaj o cokolwiek...",clearChat:"Wyczyść czat",close:"Zamknij",expand:"Rozwiń",collapse:"Zwiń",thinking:"Zastanawiam się...",askMeAnything:"Zapytaj o cokolwiek",askMeAnythingDescription:"Uzyskaj pomoc w poruszaniu się po dokumentacji, zrozumieniu pojęć i znalezieniu odpowiedzi.",faq:"Często zadawane pytania",chatCleared:"Czat został wyczyszczony po odświeżeniu",lineBreak:"Podział wiersza",explainWithAi:"Wyjaśnij za pomocą sztucznej inteligencji",toolListPages:"Wymienione strony dokumentacji",toolReadPage:"Czytaj",loading:{searching:"Przeszukiwanie dokumentacji",reading:"Czytanie dokumentów",analyzing:"Analizowanie treści",finding:"Znajdowanie najlepszej odpowiedzi",finished:"Wykorzystane źródła"}},n={common:o,docs:a,logo:i,assistant:e};export{e as assistant,o as common,n as default,a as docs,i as logo};
|
||||
1
docus/dist/_nuxt/BvtDsFck.js
vendored
Normal file
1
docus/dist/_nuxt/BvtDsFck.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{s,e as o,ah as e,af as t,f as n,o as a}from"./B3fabVUf.js";const i={key:0},u={__name:"ProseScript",props:{src:{type:String,required:!0}},setup(c){return(l,r)=>s(!1)?(a(),o("div",i,[...r[0]||(r[0]=[e(" Rendering the ",-1),t("code",null,"script",-1),e(" element is dangerous and is disabled by default. Consider implementing your own ",-1),t("code",null,"ProseScript",-1),e(" element to have control over script rendering. ",-1)])])):n("",!0)}};export{u as default};
|
||||
1
docus/dist/_nuxt/Bzhn7Nlk.js
vendored
Normal file
1
docus/dist/_nuxt/Bzhn7Nlk.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as p,a1 as n,e as l,q as u,ac as i,s as d,c as m,am as e,o as f}from"./B3fabVUf.js";const g={base:"grid grid-cols-1 sm:grid-cols-2 gap-5 my-5 *:my-0"},y={__name:"ProseCardGroup",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const s=a,o=p(),r=n("prose.cardGroup",s),t=m(()=>e({extend:e(g),...o.ui?.prose?.cardGroup||{}}));return(c,C)=>(f(),l("div",{class:i(t.value({class:[d(r)?.base,s.class]}))},[u(c.$slots,"default")],2))}};export{y as default};
|
||||
1
docus/dist/_nuxt/C-sCiTCq.js
vendored
Normal file
1
docus/dist/_nuxt/C-sCiTCq.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as f,a as d,ab as y,f as v,c as t,ao as a,o as r}from"./B3fabVUf.js";const u={"package.json":"i-vscode-icons-file-type-node","tsconfig.json":"i-vscode-icons-file-type-tsconfig",".npmrc":"i-vscode-icons-file-type-npm",".editorconfig":"i-vscode-icons-file-type-editorconfig",".eslintrc":"i-vscode-icons-file-type-eslint",".eslintrc.cjs":"i-vscode-icons-file-type-eslint",".eslintignore":"i-vscode-icons-file-type-eslint","eslint.config.js":"i-vscode-icons-file-type-eslint","eslint.config.mjs":"i-vscode-icons-file-type-eslint","eslint.config.cjs":"i-vscode-icons-file-type-eslint",".gitignore":"i-vscode-icons-file-type-git","yarn.lock":"i-vscode-icons-file-type-yarn",".env":"i-vscode-icons-file-type-dotenv",".env.example":"i-vscode-icons-file-type-dotenv",".vscode/settings.json":"i-vscode-icons-file-type-vscode",nuxt:"i-vscode-icons-file-type-nuxt",".nuxtrc":"i-vscode-icons-file-type-nuxt",".nuxtignore":"i-vscode-icons-file-type-nuxt","nuxt.config.js":"i-vscode-icons-file-type-nuxt","nuxt.config.ts":"i-vscode-icons-file-type-nuxt","nuxt.schema.ts":"i-vscode-icons-file-type-nuxt","tailwind.config.js":"i-vscode-icons-file-type-tailwind","tailwind.config.ts":"i-vscode-icons-file-type-tailwind",vue:"i-vscode-icons-file-type-vue",ts:"i-vscode-icons-file-type-typescript",tsx:"i-vscode-icons-file-type-typescript",mjs:"i-vscode-icons-file-type-js",cjs:"i-vscode-icons-file-type-js",js:"i-vscode-icons-file-type-js",jsx:"i-vscode-icons-file-type-js",md:"i-vscode-icons-file-type-markdown",py:"i-vscode-icons-file-type-python",cs:"i-vscode-icons-file-type-csharp",asm:"i-vscode-icons-file-type-assembly",f:"i-vscode-icons-file-type-fortran",hs:"i-vscode-icons-file-type-haskell",fs:"i-vscode-icons-file-type-fsharp",kt:"i-vscode-icons-file-type-kotlin",rs:"i-vscode-icons-file-type-rust",rb:"i-vscode-icons-file-type-ruby",lsp:"i-vscode-icons-file-type-lisp",ps1:"i-vscode-icons-file-type-powershell",psd1:"i-vscode-icons-file-type-powershell",psm1:"i-vscode-icons-file-type-powershell",go:"i-vscode-icons-file-type-go",gleam:"i-vscode-icons-file-type-gleam",bicep:"i-vscode-icons-file-type-bicep",bicepparam:"i-vscode-icons-file-type-bicep",exs:"i-vscode-icons-file-type-elixir",erl:"i-vscode-icons-file-type-erlang",sbt:"i-vscode-icons-file-type-scala",h:"i-vscode-icons-file-type-cppheader",ino:"i-vscode-icons-file-type-arduino",pl:"i-vscode-icons-file-type-perl",jl:"i-vscode-icons-file-type-julia",dart:"i-vscode-icons-file-type-dartlang",ico:"i-vscode-icons-file-type-favicon",npm:"i-vscode-icons-file-type-npm",pnpm:"i-vscode-icons-file-type-pnpm",npx:"i-vscode-icons-file-type-npm",yarn:"i-vscode-icons-file-type-yarn",bun:"i-vscode-icons-file-type-bun",deno:"i-vscode-icons-file-type-deno",yml:"i-vscode-icons-file-type-yaml",terminal:"i-lucide-terminal"},g={__name:"ProseCodeIcon",props:{icon:{type:null,required:!1},filename:{type:String,required:!1}},setup(l){const e=l,p=f(),o=t(()=>a(p.ui?.prose?.codeIcon||{},u)),c=t(()=>{if(e.icon)return e.icon;if(!e.filename)return;const i=e.filename.replace(/\s*\(.*\)\s*$/,""),s=i.includes(".")&&i.split(".").pop(),n=i.split("/").pop();return(n&&o.value[n.toLowerCase()])??(s&&(o.value[s]??`i-vscode-icons-file-type-${s}`))});return(i,s)=>c.value?(r(),d(y,{key:0,name:c.value},null,8,["name"])):v("",!0)}};export{g as default};
|
||||
1
docus/dist/_nuxt/C0AA5UMg.js
vendored
Normal file
1
docus/dist/_nuxt/C0AA5UMg.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
docus/dist/_nuxt/C1fSKR9D.js
vendored
Normal file
1
docus/dist/_nuxt/C1fSKR9D.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const e={or:"või",error:{title:"Lehekülge ei leitud",description:"Vabandame, kuid otsitavat lehekülge ei leitud."}},a={copy:{page:"Kopeeri lehekülg",link:"Kopeeri Markdown lehekülg",view:"Vaata Markdownina",gpt:"Ava ChatGPT-s",claude:"Ava Claude'is"},links:"Kogukond",toc:"Sellel lehel",menu:"Menüü",report:"Teata probleemist",edit:"Muuda seda lehekülge"},i={copyLogo:"Kopeeri logo",copyWordmark:"Kopeeri sõnamärk",downloadLogo:"Laadi logo alla",downloadWordmark:"Laadi sõnamärk alla",brandAssets:"Brändimaterjalid",logoCopied:"Logo kopeeritud",wordmarkCopied:"Sõnamärk kopeeritud",logoDownloaded:"Logo allalaaditud",wordmarkDownloaded:"Sõnamärk allalaaditud",copyLogoFailed:"Logo kopeerimine ebaõnnestus",copyWordmarkFailed:"Sõnamärgi kopeerimine ebaõnnestus"},o={title:"Küsi tehisintellekti",placeholder:"Esita küsimus...",tooltip:"Esita tehisintellektile küsimus",tryAsking:"Proovi esitada küsimus",askAnything:"Küsi ükskõik mida...",clearChat:"Tühjenda vestlus",close:"Sulge",expand:"Laienda",collapse:"Ahenda",thinking:"Mõtlen...",askMeAnything:"Küsi ükskõik mida",askMeAnythingDescription:"Saa abi dokumentatsioonis navigeerimisel, kontseptsioonide mõistmisel ja vastuste leidmisel.",faq:"KKK",chatCleared:"Vestlus kustutatakse värskendamisel",lineBreak:"Reavahetus",explainWithAi:"Selgita tehisintellektiga",toolListPages:"Loetletud dokumentatsiooni lehed",toolReadPage:"Loe",loading:{searching:"Dokumentatsiooni otsimine",reading:"Dokumentide lugemine",analyzing:"Sisu analüüsimine",finding:"Parima vastuse leidmine",finished:"Kasutatud allikad"}},t={common:e,docs:a,logo:i,assistant:o};export{o as assistant,e as common,t as default,a as docs,i as logo};
|
||||
1
docus/dist/_nuxt/C2GbCjp9.js
vendored
Normal file
1
docus/dist/_nuxt/C2GbCjp9.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const o={or:"یان",error:{title:"لاپەڕە نەدۆزرایەوە",description:"ببورن بەڵام ئەم پەیجە نەدۆزرایەوە."}},a={copy:{page:"کۆپیکردنی پەڕە",link:"کۆپیکردنی پەڕەی Markdown",view:"بینین وەک Markdown",gpt:"کردنەوە لە ChatGPT",claude:"کردنەوە لە Claude"},links:"کۆمەڵگا",toc:"لەم پەڕەدا",menu:"مینیو",report:"ڕاپۆرتکردنی کێشە",edit:"دەستکاریکردنی ئەم پەڕەیە"},e={copyLogo:"کۆپیکردنی لۆگۆ",copyWordmark:"کۆپیکردنی وشەنیشان",downloadLogo:"داگرتنی لۆگۆ",downloadWordmark:"داگرتنی وشەنیشان",brandAssets:"سامانەکانی براند",logoCopied:"لۆگۆ کۆپی کرا",wordmarkCopied:"وشەنیشان کۆپی کرا",logoDownloaded:"لۆگۆ دابەزێنرا",wordmarkDownloaded:"وشەنیشان دابەزێنرا",copyLogoFailed:"کۆپیکردنی لۆگۆ سەرکەوتوو نەبوو",copyWordmarkFailed:"کۆپیکردنی وشەنیشان سەرکەوتوو نەبوو"},n={title:"پرسیار لە AI بکە",placeholder:"پرسیارێک بکە...",tooltip:"پرسیارێک لە AI بکە",tryAsking:"هەوڵبدە پرسیارێک بکەیت",askAnything:"هەر شتێک بپرسە...",clearChat:"چاتی پاک بکەرەوە",close:"دابخە",expand:"فراوانتر بکە",collapse:"داڕمان",thinking:"بیرکردنەوە...",askMeAnything:"هەر شتێک بپرسە",askMeAnythingDescription:"یارمەتی وەربگرە لە گەشتکردن بە بەڵگەنامەکان، تێگەیشتن لە چەمکەکان و دۆزینەوەی وەڵامەکان.",faq:"پرسیارە بەردەوامەکان",chatCleared:"چات لە کاتی نوێکردنەوەدا پاک دەکرێتەوە",lineBreak:"هێڵ شکاندن",explainWithAi:"بە AI ڕوونی بکەرەوە",toolListPages:"لاپەڕەکانی بەڵگەنامەی ڕیزبەندی",toolReadPage:"خوێندنەوە",loading:{searching:"گەڕان بەدوای بەڵگەنامەکاندا",reading:"خوێندنەوە لە ڕێگەی دۆکیۆمێنتەکانەوە",analyzing:"شیکردنەوەی ناوەڕۆک",finding:"دۆزینەوەی باشترین وەڵام",finished:"سەرچاوە بەکارهێنراوەکان"}},i={common:o,docs:a,logo:e,assistant:n};export{n as assistant,o as common,i as default,a as docs,e as logo};
|
||||
1
docus/dist/_nuxt/C3Ly8WAT.js
vendored
Normal file
1
docus/dist/_nuxt/C3Ly8WAT.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{r}from"./Cf5i2Hk_.js";import a from"./Jc8Ntx_l.js";import{a0 as t,a as e,p,s as c,o as n}from"./B3fabVUf.js";import"./BN_7HF1G.js";const d={__name:"ProseTip",setup(i){const o=t();return(s,f)=>(n(),e(a,{color:"success",icon:c(o).ui.icons.tip},{default:p(()=>[r(s.$slots,"default",{mdcUnwrap:"p"})]),_:3},8,["icon"]))}};export{d as default};
|
||||
1
docus/dist/_nuxt/C5B99YjC.js
vendored
Normal file
1
docus/dist/_nuxt/C5B99YjC.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{i}from"./B3fabVUf.js";function r(e,s,o){const t=e.findIndex(n=>i(n,s)),d=e.findIndex(n=>i(n,o));if(t===-1||d===-1)return[];const[x,f]=[t,d].sort((n,I)=>n-I);return e.slice(x,f+1)}export{r as f};
|
||||
1
docus/dist/_nuxt/C6Z5zZ8h.js
vendored
Normal file
1
docus/dist/_nuxt/C6Z5zZ8h.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as c,a1 as p,e as l,q as u,ac as d,s as i,c as m,am as e,o as f}from"./B3fabVUf.js";const b={base:""},C={__name:"ProseTbody",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const s=a,o=c(),t=p("prose.tbody",s),r=m(()=>e({extend:e(b),...o.ui?.prose?.tbody||{}}));return(n,y)=>(f(),l("tbody",{class:d(r.value({class:[i(t)?.base,s.class]}))},[u(n.$slots,"default")],2))}};export{C as default};
|
||||
1
docus/dist/_nuxt/C8-8Pk2V.js
vendored
Normal file
1
docus/dist/_nuxt/C8-8Pk2V.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const a={or:"ali",error:{title:"Stran ni bila najdena",description:"Opravičujemo se, vendar stran, ki jo iščete, ni bila najdena."}},o={copy:{page:"Kopiraj stran",link:"Kopiraj Markdown stran",view:"Prikaži kot Markdown",gpt:"Odpri v ChatGPT",claude:"Odpri v Claude"},links:"Skupnost",toc:"Na tej strani",menu:"Meni",report:"Prijavi težavo",edit:"Uredi to stran"},e={copyLogo:"Kopiraj logotip",copyWordmark:"Kopiraj besedno znamko",downloadLogo:"Prenesi logotip",downloadWordmark:"Prenesi besedno znamko",brandAssets:"Materiali blagovne znamke",logoCopied:"Logotip kopiran",wordmarkCopied:"Besedna znamka kopirana",logoDownloaded:"Logotip prenesen",wordmarkDownloaded:"Besedna znamka prenesena",copyLogoFailed:"Kopiranje logotipa ni uspelo",copyWordmarkFailed:"Kopiranje besedne znamke ni uspelo"},i={title:"Vprašajte AI",placeholder:"Postavite vprašanje...",tooltip:"Zastavite vprašanje AI",tryAsking:"Poskusite postaviti vprašanje",askAnything:"Vprašajte karkoli...",clearChat:"Počisti klepet",close:"Zapri",expand:"Razširi",collapse:"Strni",thinking:"Razmišljanje...",askMeAnything:"Vprašaj karkoli",askMeAnythingDescription:"Poiščite pomoč pri krmarjenju po dokumentaciji, razumevanju konceptov in iskanju odgovorov.",faq:"Pogosta vprašanja",chatCleared:"Klepet se ob osvežitvi izbriše",lineBreak:"Prelom vrstice",explainWithAi:"Razloži z AI",toolListPages:"Navedene strani dokumentacije",toolReadPage:"Preberi",loading:{searching:"Iskanje po dokumentaciji",reading:"Prebiranje dokumentov",analyzing:"Analiza vsebine",finding:"Iskanje najboljšega odgovora",finished:"Uporabljeni viri"}},n={common:a,docs:o,logo:e,assistant:i};export{i as assistant,a as common,n as default,o as docs,e as logo};
|
||||
1
docus/dist/_nuxt/C8-Mg-oX.js
vendored
Normal file
1
docus/dist/_nuxt/C8-Mg-oX.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{Q as x,a0 as b,a1 as $,o as a,a as y,p as g,q as n,B as q,bZ as w,ac as o,s,e as d,ah as f,ag as h,f as c,F as C,r as S,ak as B,m as P,P as z,c as j,am as m}from"./B3fabVUf.js";const H={slots:{root:"relative isolate",container:"flex flex-col lg:grid py-24 sm:py-32 lg:py-40 gap-16 sm:gap-y-24",wrapper:"",header:"",headline:"mb-4",title:"text-5xl sm:text-7xl text-pretty tracking-tight font-bold text-highlighted",description:"text-lg sm:text-xl/8 text-muted",body:"mt-10",footer:"mt-10",links:"flex flex-wrap gap-x-6 gap-y-3"},variants:{orientation:{horizontal:{container:"lg:grid-cols-2 lg:items-center",description:"text-pretty"},vertical:{container:"",headline:"justify-center",wrapper:"text-center",description:"text-balance",links:"justify-center"}},reverse:{true:{wrapper:"order-last"}},headline:{true:{headline:"font-semibold text-primary flex items-center gap-1.5"}},title:{true:{description:"mt-6"}}}},N={key:2,class:"hidden lg:block"},F={__name:"UPageHero",props:{as:{type:null,required:!1},headline:{type:String,required:!1},title:{type:String,required:!1},description:{type:String,required:!1},links:{type:Array,required:!1},orientation:{type:null,required:!1,default:"vertical"},reverse:{type:Boolean,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(t){const u=t,e=x(),p=b(),l=$("pageHero",u),i=j(()=>m({extend:m(H),...p.ui?.pageHero||{}})({orientation:u.orientation,reverse:u.reverse,title:!!u.title||!!e.title}));return(r,V)=>(a(),y(s(z),{as:t.as,"data-orientation":t.orientation,"data-slot":"root",class:o(i.value.root({class:[s(l)?.root,u.class]}))},{default:g(()=>[n(r.$slots,"top"),q(w,{"data-slot":"container",class:o(i.value.container({class:s(l)?.container}))},{default:g(()=>[e.header||t.headline||e.headline||t.title||e.title||t.description||e.description||e.body||e.footer||t.links?.length||e.links?(a(),d("div",{key:0,"data-slot":"wrapper",class:o(i.value.wrapper({class:s(l)?.wrapper}))},[e.header||t.headline||e.headline||t.title||e.title||t.description||e.description?(a(),d("div",{key:0,"data-slot":"header",class:o(i.value.header({class:s(l)?.header}))},[n(r.$slots,"header",{},()=>[t.headline||e.headline?(a(),d("div",{key:0,"data-slot":"headline",class:o(i.value.headline({class:s(l)?.headline,headline:!e.headline}))},[n(r.$slots,"headline",{},()=>[f(h(t.headline),1)])],2)):c("",!0),t.title||e.title?(a(),d("h1",{key:1,"data-slot":"title",class:o(i.value.title({class:s(l)?.title}))},[n(r.$slots,"title",{},()=>[f(h(t.title),1)])],2)):c("",!0),t.description||e.description?(a(),d("div",{key:2,"data-slot":"description",class:o(i.value.description({class:s(l)?.description}))},[n(r.$slots,"description",{},()=>[f(h(t.description),1)])],2)):c("",!0)])],2)):c("",!0),e.body?(a(),d("div",{key:1,"data-slot":"body",class:o(i.value.body({class:s(l)?.body}))},[n(r.$slots,"body")],2)):c("",!0),e.footer||t.links?.length||e.links?(a(),d("div",{key:2,"data-slot":"footer",class:o(i.value.footer({class:s(l)?.footer}))},[n(r.$slots,"footer",{},()=>[t.links?.length||e.links?(a(),d("div",{key:0,"data-slot":"links",class:o(i.value.links({class:s(l)?.links}))},[n(r.$slots,"links",{},()=>[(a(!0),d(C,null,S(t.links,(k,v)=>(a(),y(B,P({key:v,size:"xl"},{ref_for:!0},k),null,16))),128))])],2)):c("",!0)])],2)):c("",!0)],2)):c("",!0),e.default?n(r.$slots,"default",{key:1}):t.orientation==="horizontal"?(a(),d("div",N)):c("",!0)]),_:3},8,["class"]),n(r.$slots,"bottom")]),_:3},8,["as","data-orientation","class"]))}};export{F as default};
|
||||
1
docus/dist/_nuxt/C8791Vr7.js
vendored
Normal file
1
docus/dist/_nuxt/C8791Vr7.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as c,a1 as n,e as p,ac as l,s as u,c as i,am as s,o as m}from"./B3fabVUf.js";const f={base:"border-t border-default my-12"},h={__name:"ProseHr",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const e=a,r=c(),o=n("prose.hr",e),t=i(()=>s({extend:s(f),...r.ui?.prose?.hr||{}}));return(d,_)=>(m(),p("hr",{class:l(t.value({class:[u(o)?.base,e.class]}))},null,2))}};export{h as default};
|
||||
1
docus/dist/_nuxt/C919_JS_.js
vendored
Normal file
1
docus/dist/_nuxt/C919_JS_.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const e={or:"sau",error:{title:"Pagina nu a fost găsită",description:"Ne pare rău, dar această pagină nu a putut fi găsită."}},a={copy:{page:"Copiază pagina",link:"Copiază pagina în Markdown",view:"Vezi ca Markdown",gpt:"Deschide în ChatGPT",claude:"Deschide în Claude"},links:"Comunitate",toc:"Pe această pagină",menu:"Meniu",report:"Raportează o problemă",edit:"Editează această pagină"},o={copyLogo:"Copiază logo",copyWordmark:"Copiază wordmark",downloadLogo:"Descarcă logo",downloadWordmark:"Descarcă wordmark",brandAssets:"Resurse de brand",logoCopied:"Logo copiat",wordmarkCopied:"Wordmark copiat",logoDownloaded:"Logo descărcat",wordmarkDownloaded:"Wordmark descărcat",copyLogoFailed:"Nu s-a putut copia logo-ul",copyWordmarkFailed:"Nu s-a putut copia wordmark-ul"},i={title:"Întrebați AI",placeholder:"Pune o întrebare...",tooltip:"Pune o întrebare AI",tryAsking:"Încercați să puneți o întrebare",askAnything:"Întreabă orice...",clearChat:"Ștergeți chatul",close:"Închide",expand:"Extindeți",collapse:"Colaps",thinking:"Gândind...",askMeAnything:"Întreabă orice",askMeAnythingDescription:"Obțineți ajutor pentru navigarea în documentație, înțelegerea conceptelor și găsirea răspunsurilor.",faq:"Întrebări frecvente",chatCleared:"Chatul este șters la reîmprospătare",lineBreak:"Rupere de linie",explainWithAi:"Explicați cu AI",toolListPages:"Pagini de documentație listate",toolReadPage:"Citiți",loading:{searching:"Căutarea documentației",reading:"Citind documentele",analyzing:"Analizând conținutul",finding:"Găsirea celui mai bun răspuns",finished:"Surse folosite"}},n={common:e,docs:a,logo:o,assistant:i};export{i as assistant,e as common,n as default,a as docs,o as logo};
|
||||
1
docus/dist/_nuxt/CArNbJ6I.js
vendored
Normal file
1
docus/dist/_nuxt/CArNbJ6I.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
const a={or:"atau",error:{title:"Halaman tidak ditemukan",description:"Kami minta maaf, halaman ini tidak dapat ditemukan."},copied:"Berhasil disalin ke papan klip"},n={copy:{page:"Salin halaman",link:"Salin halaman Markdown",view:"Lihat sebagai Markdown",gpt:"Buka di ChatGPT",claude:"Buka di Claude",mcp_url:"Salin URL Server MCP",mcp_add:"Tambah Server MCP"},links:"Komunitas",toc:"Pada halaman ini",menu:"Menu",report:"Laporkan masalah",edit:"Ubah halaman ini"},i={copyLogo:"Salin logo",copyWordmark:"Salin wordmark",downloadLogo:"Unduh logo",downloadWordmark:"Unduh wordmark",brandAssets:"Aset merek",logoCopied:"Logo disalin",wordmarkCopied:"Wordmark disalin",logoDownloaded:"Logo diunduh",wordmarkDownloaded:"Wordmark diunduh",copyLogoFailed:"Gagal menyalin logo",copyWordmarkFailed:"Gagal menyalin wordmark"},o={title:"Tanya AI",placeholder:"Ajukan pertanyaan...",tooltip:"Ajukan pertanyaan kepada AI",tryAsking:"Coba ajukan pertanyaan",askAnything:"Tanyakan apa saja...",clearChat:"Hapus obrolan",close:"Tutup",expand:"Perluas",collapse:"Ciutkan",thinking:"Berpikir...",askMeAnything:"Tanyakan apa saja",askMeAnythingDescription:"Dapatkan bantuan untuk menavigasi dokumentasi, memahami konsep, dan menemukan jawaban.",faq:"Pertanyaan Umum",chatCleared:"Obrolan dihapus saat penyegaran",lineBreak:"Jeda baris",explainWithAi:"Jelaskan dengan AI",toolListPages:"Halaman dokumentasi yang terdaftar",toolReadPage:"Baca",loading:{searching:"Mencari dokumentasi",reading:"Membaca dokumen",analyzing:"Menganalisis konten",finding:"Menemukan jawaban terbaik",finished:"Sumber yang digunakan"}},e={common:a,docs:n,logo:i,assistant:o};export{o as assistant,a as common,e as default,n as docs,i as logo};
|
||||
1
docus/dist/_nuxt/CB6Z2s81.js
vendored
Normal file
1
docus/dist/_nuxt/CB6Z2s81.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as n,a1 as i,e as l,q as p,ac as u,s as d,c as m,am as t,o as f,ah as b,ag as g}from"./B3fabVUf.js";const y={base:"pb-4 text-muted *:first:mt-0 *:last:mb-0 *:my-1.5"},C={__name:"ProseAccordionItem",props:{label:{type:String,required:!0},description:{type:String,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(e){const s=e,a=n(),o=i("prose.accordionItem",s),r=m(()=>t({extend:t(y),...a.ui?.prose?.accordionItem||{}}));return(c,q)=>(f(),l("div",{class:u(r.value({class:[d(o)?.base,s.class]}))},[p(c.$slots,"default",{},()=>[b(g(e.description),1)])],2))}};export{C as default};
|
||||
1
docus/dist/_nuxt/CCiWmQwn.js
vendored
Normal file
1
docus/dist/_nuxt/CCiWmQwn.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{r as k}from"./Cf5i2Hk_.js";import{Q as I,a0 as w,a1 as q,e as c,a as u,p as C,m as S,a7 as $,f as l,ac as n,s as t,ab as g,q as V,c as d,am as f,o,af as B,ah as h,ag as x}from"./B3fabVUf.js";import"./BN_7HF1G.js";const j={slots:{base:["group relative block my-5 p-4 sm:p-6 border border-default rounded-md bg-default","transition-colors"],icon:"size-6 mb-2 block",title:"text-highlighted font-semibold",description:"text-[15px] text-muted *:first:mt-0 *:last:mb-0 *:my-1",externalIcon:["size-4 align-top absolute right-2 top-2 text-dimmed pointer-events-none","transition-colors"]},variants:{color:{primary:{icon:"text-primary"},secondary:{icon:"text-secondary"},success:{icon:"text-success"},info:{icon:"text-info"},warning:{icon:"text-warning"},error:{icon:"text-error"},neutral:{icon:"text-highlighted"}},to:{true:""},title:{true:{description:"mt-1"}}},compoundVariants:[{color:"primary",to:!0,class:{base:"hover:bg-primary/10 hover:border-primary has-focus-visible:border-primary",externalIcon:"group-hover:text-primary"}},{color:"secondary",to:!0,class:{base:"hover:bg-secondary/10 hover:border-secondary has-focus-visible:border-secondary",externalIcon:"group-hover:text-secondary"}},{color:"success",to:!0,class:{base:"hover:bg-success/10 hover:border-success has-focus-visible:border-success",externalIcon:"group-hover:text-success"}},{color:"info",to:!0,class:{base:"hover:bg-info/10 hover:border-info has-focus-visible:border-info",externalIcon:"group-hover:text-info"}},{color:"warning",to:!0,class:{base:"hover:bg-warning/10 hover:border-warning has-focus-visible:border-warning",externalIcon:"group-hover:text-warning"}},{color:"error",to:!0,class:{base:"hover:bg-error/10 hover:border-error has-focus-visible:border-error",externalIcon:"group-hover:text-error"}},{color:"neutral",to:!0,class:{base:"hover:bg-elevated/50 hover:border-inverted has-focus-visible:border-inverted",externalIcon:"group-hover:text-highlighted"}}],defaultVariants:{color:"primary"}},P=Object.assign({inheritAttrs:!1},{__name:"ProseCard",props:{to:{type:null,required:!1},target:{type:[String,Object,null],required:!1},icon:{type:null,required:!1},title:{type:String,required:!1},description:{type:String,required:!1},color:{type:null,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(r){const e=r,b=I(),p=w(),s=q("prose.card",e),a=d(()=>f({extend:f(j),...p.ui?.prose?.card||{}})({color:e.color,to:!!e.to,title:!!e.title})),m=d(()=>e.target||(e.to&&typeof e.to=="string"&&e.to.startsWith("http")?"_blank":void 0)),y=d(()=>(e.title||"Card link").trim());return(i,v)=>(o(),c("div",{class:n(a.value.base({class:[t(s)?.base,e.class]}))},[r.to?(o(),u($,S({key:0,"aria-label":y.value},{to:r.to,target:m.value,...i.$attrs},{class:"focus:outline-none",raw:""}),{default:C(()=>[...v[0]||(v[0]=[B("span",{class:"absolute inset-0","aria-hidden":"true"},null,-1)])]),_:1},16,["aria-label"])):l("",!0),r.icon?(o(),u(g,{key:1,name:r.icon,class:n(a.value.icon({class:t(s)?.icon}))},null,8,["name","class"])):l("",!0),r.to&&m.value==="_blank"?(o(),u(g,{key:2,name:t(p).ui.icons.external,class:n(a.value.externalIcon({class:t(s)?.externalIcon}))},null,8,["name","class"])):l("",!0),r.title||b.title?(o(),c("p",{key:3,class:n(a.value.title({class:t(s)?.title}))},[k(i.$slots,"title",{mdcUnwrap:"p"},()=>[h(x(r.title),1)])],2)):l("",!0),b.default?(o(),c("div",{key:4,class:n(a.value.description({class:t(s)?.description}))},[V(i.$slots,"default",{},()=>[h(x(r.description),1)])],2)):l("",!0)],2))}});export{P as default};
|
||||
1
docus/dist/_nuxt/CDym3zc6.js
vendored
Normal file
1
docus/dist/_nuxt/CDym3zc6.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as c,a1 as n,o as l,a as u,p as d,q as p,ac as f,s as m,a7 as b,c as h,am as s}from"./B3fabVUf.js";const y={base:["text-primary border-b border-transparent hover:border-primary font-medium focus-visible:outline-primary focus-visible:has-[>code]:outline-0 [&>code]:border-dashed hover:[&>code]:border-primary hover:[&>code]:text-primary focus-visible:[&>code]:border-primary focus-visible:[&>code]:text-primary","transition-colors [&>code]:transition-colors"]},x={__name:"ProseA",props:{href:{type:String,required:!1},target:{type:null,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(e){const r=e,a=c(),o=n("prose.a",r),t=h(()=>s({extend:s(y),...a.ui?.prose?.a||{}}));return(i,v)=>(l(),u(b,{href:e.href,target:e.target,class:f(t.value({class:[m(o)?.base,r.class]})),raw:""},{default:d(()=>[p(i.$slots,"default")]),_:3},8,["href","target","class"]))}};export{x as default};
|
||||
1
docus/dist/_nuxt/CFKlwMFM.js
vendored
Normal file
1
docus/dist/_nuxt/CFKlwMFM.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{Q as B,a0 as q,a1 as v,a2 as $,a3 as P,o as d,a as f,p as n,s as e,aZ as T,q as m,f as O,B as x,a_ as S,ac as l,m as k,a$ as w,c as C,am as p,$ as I,ay as L,af as _,ab as N,ag as y}from"./B3fabVUf.js";const U={slots:{root:"",content:"data-[state=open]:animate-[collapsible-down_200ms_ease-out] data-[state=closed]:animate-[collapsible-up_200ms_ease-out] overflow-hidden"}},V={__name:"UCollapsible",props:{as:{type:null,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1},defaultOpen:{type:Boolean,required:!1},open:{type:Boolean,required:!1},disabled:{type:Boolean,required:!1},unmountOnHide:{type:Boolean,required:!1,default:!0}},emits:["update:open"],setup(r,{emit:s}){const a=r,i=s,t=B(),o=q(),c=v("collapsible",a),g=$(P(a,"as","defaultOpen","open","disabled","unmountOnHide"),i),u=C(()=>p({extend:p(U),...o.ui?.collapsible||{}})());return(b,z)=>(d(),f(e(w),k(e(g),{"data-slot":"root",class:u.value.root({class:[e(c)?.root,a.class]})}),{default:n(({open:h})=>[t.default?(d(),f(e(T),{key:0,"as-child":""},{default:n(()=>[m(b.$slots,"default",{open:h})]),_:2},1024)):O("",!0),x(e(S),{"data-slot":"content",class:l(u.value.content({class:e(c)?.content}))},{default:n(()=>[m(b.$slots,"content")]),_:3},8,["class"])]),_:3},16,["class"]))}},j={slots:{root:"my-5",trigger:["group relative rounded-xs inline-flex items-center gap-1.5 text-muted hover:text-default text-sm focus-visible:ring-2 focus-visible:ring-primary focus:outline-none","transition-colors"],triggerIcon:"size-4 shrink-0 group-data-[state=open]:rotate-180 transition-transform duration-200",triggerLabel:"truncate",content:"*:first:mt-2.5 *:last:mb-0 *:my-1.5"}},H={__name:"ProseCollapsible",props:{icon:{type:null,required:!1},name:{type:String,required:!1},openText:{type:String,required:!1},closeText:{type:String,required:!1},class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(r){const s=r,{t:a}=I(),i=q(),t=v("prose.collapsible",s),o=C(()=>p({extend:p(j),...i.ui?.prose?.collapsible||{}})());return(c,g)=>(d(),f(V,{"unmount-on-hide":!1,class:l(s.class),ui:e(L)(o.value,e(t))},{default:n(({open:u})=>[_("button",{class:l(o.value.trigger({class:e(t)?.trigger}))},[x(N,{name:r.icon||e(i).ui.icons.chevronDown,class:l(o.value.triggerIcon({class:e(t)?.triggerIcon}))},null,8,["name","class"]),_("span",{class:l(o.value.triggerLabel({class:e(t)?.triggerLabel}))},y(u?s.closeText||e(a)("prose.collapsible.closeText"):s.openText||e(a)("prose.collapsible.openText"))+" "+y(s.name||e(a)("prose.collapsible.name")),3)],2)]),content:n(()=>[m(c.$slots,"default")]),_:3},8,["class","ui"]))}};export{H as default};
|
||||
1
docus/dist/_nuxt/CFOqH6J8.js
vendored
Normal file
1
docus/dist/_nuxt/CFOqH6J8.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as l,a1 as n,e as u,q as p,ac as i,s as m,c as d,am as s,o as f}from"./B3fabVUf.js";const _={base:"list-disc ps-6 my-5 marker:text-(--ui-border-accented)"},k={__name:"ProseUl",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const e=a,t=l(),o=n("prose.ul",e),r=d(()=>s({extend:s(_),...t.ui?.prose?.ul||{}}));return(c,b)=>(f(),u("ul",{class:i(r.value({class:[m(o)?.base,e.class]}))},[p(c.$slots,"default")],2))}};export{k as default};
|
||||
1
docus/dist/_nuxt/CG1YvYVw.js
vendored
Normal file
1
docus/dist/_nuxt/CG1YvYVw.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{r as a}from"./Cf5i2Hk_.js";import n from"./Jc8Ntx_l.js";import{a0 as s,a as t,p as e,s as i,o as p}from"./B3fabVUf.js";import"./BN_7HF1G.js";const d={__name:"ProseWarning",setup(c){const o=s();return(r,f)=>(p(),t(n,{color:"warning",icon:i(o).ui.icons.warning},{default:e(()=>[a(r.$slots,"default",{mdcUnwrap:"p"})]),_:3},8,["icon"]))}};export{d as default};
|
||||
1
docus/dist/_nuxt/CGXzuCYy.js
vendored
Normal file
1
docus/dist/_nuxt/CGXzuCYy.js
vendored
Normal file
@ -0,0 +1 @@
|
||||
import{a0 as c,a1 as n,e as p,q as u,ac as m,s as i,c as d,am as s,o as f}from"./B3fabVUf.js";const _={base:"list-decimal ps-6 my-5 marker:text-muted"},k={__name:"ProseOl",props:{class:{type:null,required:!1},ui:{type:Object,required:!1}},setup(a){const e=a,o=c(),t=n("prose.ol",e),l=d(()=>s({extend:s(_),...o.ui?.prose?.ol||{}}));return(r,C)=>(f(),p("ol",{class:m(l.value({class:[i(t)?.base,e.class]}))},[u(r.$slots,"default")],2))}};export{k as default};
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user