Compare commits
6 commits
50bc0e3441
...
2f536a814b
| Author | SHA1 | Date | |
|---|---|---|---|
| 2f536a814b | |||
| cc45ba0b61 | |||
| 452aa392bd | |||
| ae732496ed | |||
| e57f38cd8f | |||
| 79d3c0a0b3 |
14 changed files with 717 additions and 198 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
#DATABASE_URI=postgres://ptfapp:polaristaskforcedb@jmf-usrv-2404:2552/ptf_app_prd
|
DATABASE_URI=postgres://ptfapp:polaristaskforcedb@jmf-usrv-2404:2552/ptf_app_dev
|
||||||
PAYLOAD_SECRET=b5ac11acc77ea8427d10fcd0
|
PAYLOAD_SECRET=b5ac11acc77ea8427d10fcd0
|
||||||
# Added by Payload
|
# Added by Payload
|
||||||
|
|
||||||
|
|
@ -21,3 +21,5 @@ DISCORD_ANNOUNCE_CHANNEL_ID=1136692636481237173
|
||||||
DISCORD_STAFF_ROLE_IDS=851910807277404187,851910807277404188,851910807277404189,851910807289462814
|
DISCORD_STAFF_ROLE_IDS=851910807277404187,851910807277404188,851910807277404189,851910807289462814
|
||||||
DISCORD_ATTENDANCE_POLL_MS=5000
|
DISCORD_ATTENDANCE_POLL_MS=5000
|
||||||
DISCORD_NOTIFICATION_POLL_MS=5000
|
DISCORD_NOTIFICATION_POLL_MS=5000
|
||||||
|
|
||||||
|
UNSLOTH_API_KEY=sk-unsloth-12a9650c5c90a5b544bfd45786be0516
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
## What this is
|
## What this is
|
||||||
|
|
||||||
Next.js 16 + Payload CMS 3.79.1 app for an Arma 3 unit. PostgreSQL database via `@payloadcms/db-postgres` + Drizzle. Tailwind CSS v4 (no config file — CSS-based). shadcn/ui (new-york style, `lucide` icons). Dark-themed frontend.
|
Next.js 16 + Payload CMS 3.88.0 app for an Arma 3 unit. PostgreSQL database via `@payloadcms/db-postgres` + Drizzle. Tailwind CSS v4 (no config file — CSS-based). shadcn/ui (new-york style, `lucide` icons). Dark-themed frontend.
|
||||||
|
|
||||||
## Package manager
|
## Package manager
|
||||||
|
|
||||||
|
|
@ -32,7 +32,7 @@ bun run generate:importmap # regenerates payload admin importMap
|
||||||
npx tsc --noEmit 2>&1 | grep -E "error TS" | grep -v "\.next/"
|
npx tsc --noEmit 2>&1 | grep -E "error TS" | grep -v "\.next/"
|
||||||
```
|
```
|
||||||
|
|
||||||
Known pre-existing errors, don't chase them: `src/utils/access-control/hasLogisticsQualification.ts` (lines 12, 19 — `'user' is possibly 'null'`) and `src/payload-generated-schema.ts` (line 1883, generated file — moves when the schema is regenerated). Anything else is yours.
|
The typecheck should now pass clean (the two pre-existing errors in `hasLogisticsQualification.ts` and `payload-generated-schema.ts` were resolved by the Payload 3.88.0 upgrade). Any error is yours.
|
||||||
|
|
||||||
## Test details
|
## Test details
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@ Web operations and administration hub for the Arma 3 unit Polaris Task Force. Da
|
||||||
## Tech stack
|
## Tech stack
|
||||||
|
|
||||||
- **Framework**: Next.js 16 (App Router, webpack)
|
- **Framework**: Next.js 16 (App Router, webpack)
|
||||||
- **CMS / Admin**: Payload CMS 3.79.1 (`@payloadcms/db-postgres` + Drizzle)
|
- **CMS / Admin**: Payload CMS 3.88.0 (`@payloadcms/db-postgres` + Drizzle)
|
||||||
- **Database**: PostgreSQL
|
- **Database**: PostgreSQL
|
||||||
- **Styling**: Tailwind CSS v4 (CSS-based, no config file) + shadcn/ui (New York style)
|
- **Styling**: Tailwind CSS v4 (CSS-based, no config file) + shadcn/ui (New York style)
|
||||||
- **Package manager**: Bun (`bun.lock`, `bunfig.toml`). The package.json scripts use cross-env and reference pnpm — run everything via `bun run`.
|
- **Package manager**: Bun (`bun.lock`, `bunfig.toml`). The package.json scripts use cross-env and reference pnpm — run everything via `bun run`.
|
||||||
|
|
|
||||||
|
|
@ -17,13 +17,29 @@ const nextConfig = {
|
||||||
// tree under .next/standalone/WebstormProjects/polaris-task-force/, which
|
// tree under .next/standalone/WebstormProjects/polaris-task-force/, which
|
||||||
// breaks the Dockerfile's `COPY .../standalone ./` + `CMD ["node","server.js"]`).
|
// breaks the Dockerfile's `COPY .../standalone ./` + `CMD ["node","server.js"]`).
|
||||||
outputFileTracingRoot: path.resolve(path.dirname(fileURLToPath(import.meta.url))),
|
outputFileTracingRoot: path.resolve(path.dirname(fileURLToPath(import.meta.url))),
|
||||||
webpack: (webpackConfig) => {
|
webpack: (webpackConfig, { isServer }) => {
|
||||||
webpackConfig.resolve.extensionAlias = {
|
webpackConfig.resolve.extensionAlias = {
|
||||||
".cjs": [".cts", ".cjs"],
|
".cjs": [".cts", ".cjs"],
|
||||||
".js": [".ts", ".tsx", ".js", ".jsx"],
|
".js": [".ts", ".tsx", ".js", ".jsx"],
|
||||||
".mjs": [".mts", ".mjs"],
|
".mjs": [".mts", ".mjs"],
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// @ai-stack/payloadcms client components (Compose -> setSafeLexicalState ->
|
||||||
|
// lexicalToHTML) import the *server* entry of @payloadcms/richtext-lexical, which
|
||||||
|
// transitively pulls in `payload` and all Node built-ins (node:fs, node:crypto,
|
||||||
|
// child_process, worker_threads, ...). The client only needs two pure string
|
||||||
|
// constants (BLOCK_PLACEHOLDER_PREFIX/SUFFIX) from lexicalToHTML; the server-only
|
||||||
|
// convertLexicalToHTML is never invoked client-side. Stub the bare server entry in
|
||||||
|
// the client bundle so the transitive import chain breaks. The `$` makes it an
|
||||||
|
// exact-match alias, leaving the legitimate `@payloadcms/richtext-lexical/client`
|
||||||
|
// subpath and the server bundle unaffected.
|
||||||
|
if (!isServer) {
|
||||||
|
webpackConfig.resolve.alias = {
|
||||||
|
...webpackConfig.resolve.alias,
|
||||||
|
"@payloadcms/richtext-lexical$": false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return webpackConfig;
|
return webpackConfig;
|
||||||
},
|
},
|
||||||
transpilePackages: [
|
transpilePackages: [
|
||||||
|
|
|
||||||
15
package.json
15
package.json
|
|
@ -26,14 +26,15 @@
|
||||||
"postversion": "bun run generate:version-info && bash ./build/deploy.sh"
|
"postversion": "bun run generate:version-info && bash ./build/deploy.sh"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@ai-stack/payloadcms": "^3.2.30",
|
||||||
"@dnd-kit/core": "^6.3.1",
|
"@dnd-kit/core": "^6.3.1",
|
||||||
"@dnd-kit/utilities": "^3.2.2",
|
"@dnd-kit/utilities": "^3.2.2",
|
||||||
"@payloadcms/db-postgres": "3.79.1",
|
"@payloadcms/db-postgres": "3.88.0",
|
||||||
"@payloadcms/email-nodemailer": "3.79.1",
|
"@payloadcms/email-nodemailer": "3.88.0",
|
||||||
"@payloadcms/next": "3.79.1",
|
"@payloadcms/next": "3.88.0",
|
||||||
"@payloadcms/payload-cloud": "3.79.1",
|
"@payloadcms/payload-cloud": "3.88.0",
|
||||||
"@payloadcms/richtext-lexical": "3.79.1",
|
"@payloadcms/richtext-lexical": "3.88.0",
|
||||||
"@payloadcms/ui": "3.79.1",
|
"@payloadcms/ui": "3.88.0",
|
||||||
"@radix-ui/react-avatar": "^1.1.11",
|
"@radix-ui/react-avatar": "^1.1.11",
|
||||||
"@radix-ui/react-checkbox": "^1.3.11",
|
"@radix-ui/react-checkbox": "^1.3.11",
|
||||||
"@radix-ui/react-collapsible": "^1.1.12",
|
"@radix-ui/react-collapsible": "^1.1.12",
|
||||||
|
|
@ -62,7 +63,7 @@
|
||||||
"next": "16.2.11",
|
"next": "16.2.11",
|
||||||
"next-themes": "^0.4.6",
|
"next-themes": "^0.4.6",
|
||||||
"nodemailer": "^9.0.3",
|
"nodemailer": "^9.0.3",
|
||||||
"payload": "3.79.1",
|
"payload": "3.88.0",
|
||||||
"postcss": "8.5.18",
|
"postcss": "8.5.18",
|
||||||
"radix-ui": "^1.6.7",
|
"radix-ui": "^1.6.7",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
|
|
|
||||||
|
|
@ -104,7 +104,7 @@ export async function submitFlappyScore(input: {
|
||||||
if (passes > 0) {
|
if (passes > 0) {
|
||||||
await emitGameEvent(payload, {
|
await emitGameEvent(payload, {
|
||||||
type: EventTypes.MinigameFlappy,
|
type: EventTypes.MinigameFlappy,
|
||||||
message: `Completed a flight simulator run passing ${passes} obstacle${passes === 1 ? "" : "s"} (definitely not Flappy Bird) (+${runXp} XP)`,
|
message: `Completed a flight simulator run passing ${passes} obstacle${passes === 1 ? "" : "s"} (+${runXp} XP)`,
|
||||||
actor: user.id as number,
|
actor: user.id as number,
|
||||||
targetCollection: "users",
|
targetCollection: "users",
|
||||||
targetId: user.id as number,
|
targetId: user.id as number,
|
||||||
|
|
|
||||||
|
|
@ -1,71 +1,44 @@
|
||||||
|
import { ComposeField as ComposeField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc'
|
||||||
import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { LexicalEditorFeatureClient as LexicalEditorFeatureClient_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
||||||
import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client'
|
|
||||||
import { default as default_c3ea68702b5e5f8f2f98055e01c43c8d } from '@/components/admin/awards/RibbonDesigner'
|
import { default as default_c3ea68702b5e5f8f2f98055e01c43c8d } from '@/components/admin/awards/RibbonDesigner'
|
||||||
import { default as default_2f2033acccb3be245f1a85f7b4271dac } from '@/components/admin/awards/MedalDesigner'
|
import { default as default_2f2033acccb3be245f1a85f7b4271dac } from '@/components/admin/awards/MedalDesigner'
|
||||||
import { default as default_0edde40a6ccd30c893b1227360f69360 } from '@/components/static/TickDurationDescription'
|
import { default as default_0edde40a6ccd30c893b1227360f69360 } from '@/components/static/TickDurationDescription'
|
||||||
import { default as default_2d468cc5cf0b647ffca3201592c20825 } from '@/components/static/WarningMessage'
|
import { default as default_2d468cc5cf0b647ffca3201592c20825 } from '@/components/static/WarningMessage'
|
||||||
import { default as default_7fa52484aec46e988e9cfb5e86c0c759 } from '@/components/admin/narrative-flow/NarrativeFlowEditor'
|
import { default as default_7fa52484aec46e988e9cfb5e86c0c759 } from '@/components/admin/narrative-flow/NarrativeFlowEditor'
|
||||||
|
import { SelectField as SelectField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
|
import { PromptEditorField as PromptEditorField_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
import { default as default_2f6c866ab4d4d51b3789f090b84b85c7 } from '@/components/static/VersionOverlay'
|
import { default as default_2f6c866ab4d4d51b3789f090b84b85c7 } from '@/components/static/VersionOverlay'
|
||||||
import { default as default_508743824696caae2894ebf34c2e68bb } from '@/components/graphics/AppIcon'
|
import { default as default_508743824696caae2894ebf34c2e68bb } from '@/components/graphics/AppIcon'
|
||||||
import { default as default_e752fee78378294ff03f91dba0a75a04 } from '@/components/graphics/AppLogo'
|
import { default as default_e752fee78378294ff03f91dba0a75a04 } from '@/components/graphics/AppLogo'
|
||||||
import { default as default_05548cb43495e62d80b80ec4d9f0f7e9 } from '@/components/static/BeforeDashboardText'
|
import { default as default_05548cb43495e62d80b80ec4d9f0f7e9 } from '@/components/static/BeforeDashboardText'
|
||||||
import { default as default_459d6b6f5ead73c736013da63a5ace3f } from '@/components/static/GoToFrontendLink'
|
import { default as default_459d6b6f5ead73c736013da63a5ace3f } from '@/components/static/GoToFrontendLink'
|
||||||
|
import { InstructionsProvider as InstructionsProvider_4490b89d4413c1ffaecdacfe72efaf73 } from '@ai-stack/payloadcms/client'
|
||||||
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
import { CollectionCards as CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1 } from '@payloadcms/next/rsc'
|
||||||
|
|
||||||
|
/** @type import('payload').ImportMap */
|
||||||
export const importMap = {
|
export const importMap = {
|
||||||
|
"@ai-stack/payloadcms/client#ComposeField": ComposeField_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
"@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
"@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e,
|
||||||
"@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@ai-stack/payloadcms/client#LexicalEditorFeatureClient": LexicalEditorFeatureClient_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
"@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
"@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
||||||
"@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864,
|
|
||||||
"@/components/admin/awards/RibbonDesigner#default": default_c3ea68702b5e5f8f2f98055e01c43c8d,
|
"@/components/admin/awards/RibbonDesigner#default": default_c3ea68702b5e5f8f2f98055e01c43c8d,
|
||||||
"@/components/admin/awards/MedalDesigner#default": default_2f2033acccb3be245f1a85f7b4271dac,
|
"@/components/admin/awards/MedalDesigner#default": default_2f2033acccb3be245f1a85f7b4271dac,
|
||||||
"@/components/static/TickDurationDescription#default": default_0edde40a6ccd30c893b1227360f69360,
|
"@/components/static/TickDurationDescription#default": default_0edde40a6ccd30c893b1227360f69360,
|
||||||
"@/components/static/WarningMessage#default": default_2d468cc5cf0b647ffca3201592c20825,
|
"@/components/static/WarningMessage#default": default_2d468cc5cf0b647ffca3201592c20825,
|
||||||
"@/components/admin/narrative-flow/NarrativeFlowEditor#default": default_7fa52484aec46e988e9cfb5e86c0c759,
|
"@/components/admin/narrative-flow/NarrativeFlowEditor#default": default_7fa52484aec46e988e9cfb5e86c0c759,
|
||||||
|
"@ai-stack/payloadcms/client#SelectField": SelectField_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
|
"@ai-stack/payloadcms/client#PromptEditorField": PromptEditorField_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
"@/components/static/VersionOverlay#default": default_2f6c866ab4d4d51b3789f090b84b85c7,
|
"@/components/static/VersionOverlay#default": default_2f6c866ab4d4d51b3789f090b84b85c7,
|
||||||
"@/components/graphics/AppIcon#default": default_508743824696caae2894ebf34c2e68bb,
|
"@/components/graphics/AppIcon#default": default_508743824696caae2894ebf34c2e68bb,
|
||||||
"@/components/graphics/AppLogo#default": default_e752fee78378294ff03f91dba0a75a04,
|
"@/components/graphics/AppLogo#default": default_e752fee78378294ff03f91dba0a75a04,
|
||||||
"@/components/static/BeforeDashboardText#default": default_05548cb43495e62d80b80ec4d9f0f7e9,
|
"@/components/static/BeforeDashboardText#default": default_05548cb43495e62d80b80ec4d9f0f7e9,
|
||||||
"@/components/static/GoToFrontendLink#default": default_459d6b6f5ead73c736013da63a5ace3f,
|
"@/components/static/GoToFrontendLink#default": default_459d6b6f5ead73c736013da63a5ace3f,
|
||||||
|
"@ai-stack/payloadcms/client#InstructionsProvider": InstructionsProvider_4490b89d4413c1ffaecdacfe72efaf73,
|
||||||
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
"@payloadcms/next/rsc#CollectionCards": CollectionCards_f9c02e79a4aed9a3924487c0cd4cafb1
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { Command as CommandPrimitive } from "cmdk";
|
||||||
import { Search } from "lucide-react";
|
import { Search } from "lucide-react";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Dialog, DialogContent } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog";
|
||||||
|
|
||||||
const Command = React.forwardRef<
|
const Command = React.forwardRef<
|
||||||
React.ElementRef<typeof CommandPrimitive>,
|
React.ElementRef<typeof CommandPrimitive>,
|
||||||
|
|
@ -23,10 +23,15 @@ const Command = React.forwardRef<
|
||||||
));
|
));
|
||||||
Command.displayName = CommandPrimitive.displayName;
|
Command.displayName = CommandPrimitive.displayName;
|
||||||
|
|
||||||
const CommandDialog = ({ children, ...props }: DialogProps) => {
|
const CommandDialog = ({
|
||||||
|
children,
|
||||||
|
title,
|
||||||
|
...props
|
||||||
|
}: DialogProps & { title?: React.ReactNode }) => {
|
||||||
return (
|
return (
|
||||||
<Dialog {...props}>
|
<Dialog {...props}>
|
||||||
<DialogContent className="overflow-hidden p-0">
|
<DialogContent className="overflow-hidden p-0">
|
||||||
|
{title && <DialogTitle className="sr-only">{title}</DialogTitle>}
|
||||||
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
<Command className="[&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group-heading]]:text-muted-foreground [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-group]]:px-2 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
|
||||||
{children}
|
{children}
|
||||||
</Command>
|
</Command>
|
||||||
|
|
|
||||||
|
|
@ -31,8 +31,10 @@ DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
||||||
|
|
||||||
const DialogContent = React.forwardRef<
|
const DialogContent = React.forwardRef<
|
||||||
React.ElementRef<typeof DialogPrimitive.Content>,
|
React.ElementRef<typeof DialogPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof DialogPrimitive.Content> & {
|
||||||
>(({ className, children, ...props }, ref) => (
|
showCloseButton?: boolean;
|
||||||
|
}
|
||||||
|
>(({ className, children, showCloseButton = true, ...props }, ref) => (
|
||||||
<DialogPortal>
|
<DialogPortal>
|
||||||
<DialogOverlay />
|
<DialogOverlay />
|
||||||
<DialogPrimitive.Content
|
<DialogPrimitive.Content
|
||||||
|
|
@ -44,10 +46,12 @@ const DialogContent = React.forwardRef<
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
{showCloseButton && (
|
||||||
<X className="h-4 w-4" />
|
<DialogPrimitive.Close className="absolute right-4 top-4 rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
|
||||||
<span className="sr-only">Close</span>
|
<X className="h-4 w-4" />
|
||||||
</DialogPrimitive.Close>
|
<span className="sr-only">Close</span>
|
||||||
|
</DialogPrimitive.Close>
|
||||||
|
)}
|
||||||
</DialogPrimitive.Content>
|
</DialogPrimitive.Content>
|
||||||
</DialogPortal>
|
</DialogPortal>
|
||||||
));
|
));
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,36 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|
||||||
const Tabs = TabsPrimitive.Root;
|
const Tabs = TabsPrimitive.Root;
|
||||||
|
|
||||||
|
const tabsListVariants = cva(
|
||||||
|
"inline-flex items-center justify-center text-muted-foreground",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "h-9 rounded-lg bg-muted p-1",
|
||||||
|
line: "h-9 w-full justify-start gap-1 border-b border-border bg-transparent p-0 group",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
const TabsList = React.forwardRef<
|
const TabsList = React.forwardRef<
|
||||||
React.ElementRef<typeof TabsPrimitive.List>,
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List> &
|
||||||
>(({ className, ...props }, ref) => (
|
VariantProps<typeof tabsListVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => (
|
||||||
<TabsPrimitive.List
|
<TabsPrimitive.List
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
data-variant={variant ?? "default"}
|
||||||
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
className={cn(tabsListVariants({ variant, className }))}
|
||||||
className,
|
|
||||||
)}
|
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
));
|
));
|
||||||
|
|
@ -30,6 +45,9 @@ const TabsTrigger = React.forwardRef<
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||||
|
// "line" variant: underline-style triggers (applies only when the parent
|
||||||
|
// TabsList has data-variant="line" + the `group` class from the cva above).
|
||||||
|
"group-data-[variant=line]:rounded-none group-data-[variant=line]:border-b-2 group-data-[variant=line]:border-transparent group-data-[variant=line]:bg-transparent group-data-[variant=line]:px-4 group-data-[variant=line]:py-2 group-data-[variant=line]:shadow-none group-data-[variant=line]:data-[state=active]:border-primary group-data-[variant=line]:data-[state=active]:bg-transparent group-data-[variant=line]:data-[state=active]:text-foreground group-data-[variant=line]:data-[state=active]:shadow-none",
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,10 @@ const TooltipTrigger = TooltipPrimitive.Trigger;
|
||||||
|
|
||||||
const TooltipContent = React.forwardRef<
|
const TooltipContent = React.forwardRef<
|
||||||
React.ElementRef<typeof TooltipPrimitive.Content>,
|
React.ElementRef<typeof TooltipPrimitive.Content>,
|
||||||
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content>
|
React.ComponentPropsWithoutRef<typeof TooltipPrimitive.Content> & {
|
||||||
>(({ className, sideOffset = 4, ...props }, ref) => (
|
arrowClassName?: string;
|
||||||
|
}
|
||||||
|
>(({ className, sideOffset = 4, arrowClassName, children, ...props }, ref) => (
|
||||||
<TooltipPrimitive.Portal>
|
<TooltipPrimitive.Portal>
|
||||||
<TooltipPrimitive.Content
|
<TooltipPrimitive.Content
|
||||||
ref={ref}
|
ref={ref}
|
||||||
|
|
@ -24,7 +26,10 @@ const TooltipContent = React.forwardRef<
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
>
|
||||||
|
{arrowClassName && <TooltipPrimitive.Arrow className={arrowClassName} />}
|
||||||
|
{children}
|
||||||
|
</TooltipPrimitive.Content>
|
||||||
</TooltipPrimitive.Portal>
|
</TooltipPrimitive.Portal>
|
||||||
));
|
));
|
||||||
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
TooltipContent.displayName = TooltipPrimitive.Content.displayName;
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,7 @@ export interface Config {
|
||||||
'narrative-events': NarrativeEvent;
|
'narrative-events': NarrativeEvent;
|
||||||
'mission-files': MissionFile;
|
'mission-files': MissionFile;
|
||||||
'mod-lists': ModList;
|
'mod-lists': ModList;
|
||||||
|
'plugin-ai-instructions': PluginAiInstruction;
|
||||||
'payload-kv': PayloadKv;
|
'payload-kv': PayloadKv;
|
||||||
'payload-locked-documents': PayloadLockedDocument;
|
'payload-locked-documents': PayloadLockedDocument;
|
||||||
'payload-preferences': PayloadPreference;
|
'payload-preferences': PayloadPreference;
|
||||||
|
|
@ -153,6 +154,7 @@ export interface Config {
|
||||||
'narrative-events': NarrativeEventsSelect<false> | NarrativeEventsSelect<true>;
|
'narrative-events': NarrativeEventsSelect<false> | NarrativeEventsSelect<true>;
|
||||||
'mission-files': MissionFilesSelect<false> | MissionFilesSelect<true>;
|
'mission-files': MissionFilesSelect<false> | MissionFilesSelect<true>;
|
||||||
'mod-lists': ModListsSelect<false> | ModListsSelect<true>;
|
'mod-lists': ModListsSelect<false> | ModListsSelect<true>;
|
||||||
|
'plugin-ai-instructions': PluginAiInstructionsSelect<false> | PluginAiInstructionsSelect<true>;
|
||||||
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
'payload-kv': PayloadKvSelect<false> | PayloadKvSelect<true>;
|
||||||
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
'payload-locked-documents': PayloadLockedDocumentsSelect<false> | PayloadLockedDocumentsSelect<true>;
|
||||||
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
'payload-preferences': PayloadPreferencesSelect<false> | PayloadPreferencesSelect<true>;
|
||||||
|
|
@ -2342,6 +2344,47 @@ export interface NarrativeEvent {
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "plugin-ai-instructions".
|
||||||
|
*/
|
||||||
|
export interface PluginAiInstruction {
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* Please don't change this unless you're sure of what you're doing
|
||||||
|
*/
|
||||||
|
'schema-path'?: string | null;
|
||||||
|
/**
|
||||||
|
* Please don't change this unless you're sure of what you're doing
|
||||||
|
*/
|
||||||
|
'field-type'?: ('text' | 'textarea' | 'upload' | 'richText') | null;
|
||||||
|
'relation-to'?: string | null;
|
||||||
|
'model-id'?: 'unsloth-text' | null;
|
||||||
|
/**
|
||||||
|
* Please reload your collection after applying the changes
|
||||||
|
*/
|
||||||
|
disabled?: boolean | null;
|
||||||
|
/**
|
||||||
|
* Click 'Compose' to run this custom prompt and generate content
|
||||||
|
*/
|
||||||
|
prompt?: string | null;
|
||||||
|
images?:
|
||||||
|
| {
|
||||||
|
/**
|
||||||
|
* Please make sure the image is publicly accessible.
|
||||||
|
*/
|
||||||
|
image?: (number | null) | Media;
|
||||||
|
id?: string | null;
|
||||||
|
}[]
|
||||||
|
| null;
|
||||||
|
system?: string | null;
|
||||||
|
layout?: string | null;
|
||||||
|
'unsloth-text-settings'?: {
|
||||||
|
model?: string | null;
|
||||||
|
};
|
||||||
|
updatedAt: string;
|
||||||
|
createdAt: string;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-kv".
|
* via the `definition` "payload-kv".
|
||||||
|
|
@ -2509,6 +2552,10 @@ export interface PayloadLockedDocument {
|
||||||
| ({
|
| ({
|
||||||
relationTo: 'mod-lists';
|
relationTo: 'mod-lists';
|
||||||
value: number | ModList;
|
value: number | ModList;
|
||||||
|
} | null)
|
||||||
|
| ({
|
||||||
|
relationTo: 'plugin-ai-instructions';
|
||||||
|
value: number | PluginAiInstruction;
|
||||||
} | null);
|
} | null);
|
||||||
globalSlug?: string | null;
|
globalSlug?: string | null;
|
||||||
user: {
|
user: {
|
||||||
|
|
@ -3634,6 +3681,33 @@ export interface ModListsSelect<T extends boolean = true> {
|
||||||
focalX?: T;
|
focalX?: T;
|
||||||
focalY?: T;
|
focalY?: T;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
|
* via the `definition` "plugin-ai-instructions_select".
|
||||||
|
*/
|
||||||
|
export interface PluginAiInstructionsSelect<T extends boolean = true> {
|
||||||
|
'schema-path'?: T;
|
||||||
|
'field-type'?: T;
|
||||||
|
'relation-to'?: T;
|
||||||
|
'model-id'?: T;
|
||||||
|
disabled?: T;
|
||||||
|
prompt?: T;
|
||||||
|
images?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
image?: T;
|
||||||
|
id?: T;
|
||||||
|
};
|
||||||
|
system?: T;
|
||||||
|
layout?: T;
|
||||||
|
'unsloth-text-settings'?:
|
||||||
|
| T
|
||||||
|
| {
|
||||||
|
model?: T;
|
||||||
|
};
|
||||||
|
updatedAt?: T;
|
||||||
|
createdAt?: T;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* This interface was referenced by `Config`'s JSON-Schema
|
* This interface was referenced by `Config`'s JSON-Schema
|
||||||
* via the `definition` "payload-kv_select".
|
* via the `definition` "payload-kv_select".
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
// storage-adapter-import-placeholder
|
// storage-adapter-import-placeholder
|
||||||
import { postgresAdapter } from "@payloadcms/db-postgres";
|
import { postgresAdapter } from "@payloadcms/db-postgres";
|
||||||
import { lexicalEditor } from "@payloadcms/richtext-lexical";
|
import { HeadingFeature, lexicalEditor } from "@payloadcms/richtext-lexical";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
import { buildConfig } from "payload";
|
import { buildConfig } from "payload";
|
||||||
import { fileURLToPath } from "url";
|
import { fileURLToPath } from "url";
|
||||||
|
|
@ -47,9 +47,89 @@ import { migrations } from "./migrations";
|
||||||
import { nodemailerAdapter } from "@payloadcms/email-nodemailer";
|
import { nodemailerAdapter } from "@payloadcms/email-nodemailer";
|
||||||
import nodemailer from "nodemailer";
|
import nodemailer from "nodemailer";
|
||||||
import { Shims } from "@/collections/Shims";
|
import { Shims } from "@/collections/Shims";
|
||||||
|
import { payloadAiPlugin, PayloadAiPluginLexicalEditorFeature } from "@ai-stack/payloadcms";
|
||||||
|
import { createOpenAI } from "@ai-sdk/openai";
|
||||||
|
import { createTextStreamResponse, jsonSchema, streamObject } from "ai";
|
||||||
|
|
||||||
|
const unslothProvider = createOpenAI({
|
||||||
|
apiKey: process.env.UNSLOTH_API_KEY,
|
||||||
|
baseURL: process.env.UNSLOTH_URL || "https://uns.onyxsimple.com/v1",
|
||||||
|
});
|
||||||
|
|
||||||
|
const DEFAULT_MODEL = "unsloth/gemma-4-E4B-it-qat-GGUF";
|
||||||
|
|
||||||
|
function plainTextToLexicalJson(text: string): Record<string, unknown> {
|
||||||
|
const paragraphs = text.split(/\n\n+/).filter((p) => p.trim());
|
||||||
|
|
||||||
|
const children = paragraphs.map((para) => ({
|
||||||
|
type: "paragraph",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
text: para.trim(),
|
||||||
|
format: 0,
|
||||||
|
style: "",
|
||||||
|
mode: "normal",
|
||||||
|
detail: 0,
|
||||||
|
direction: "ltr",
|
||||||
|
indent: 0,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
direction: "ltr",
|
||||||
|
format: "start",
|
||||||
|
indent: 0,
|
||||||
|
textFormat: 0,
|
||||||
|
textStyle: "",
|
||||||
|
version: 1,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
root: {
|
||||||
|
type: "root",
|
||||||
|
children,
|
||||||
|
direction: "ltr",
|
||||||
|
format: "",
|
||||||
|
indent: 0,
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function generatePlainText(
|
||||||
|
prompt: string,
|
||||||
|
system: string,
|
||||||
|
model: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const baseUrl = process.env.UNSLOTH_URL || "https://uns.onyxsimple.com/v1";
|
||||||
|
const response = await fetch(`${baseUrl}/chat/completions`, {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: "Bearer " + process.env.UNSLOTH_API_KEY,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
model,
|
||||||
|
messages: [
|
||||||
|
{ role: "system", content: system || "You are a helpful assistant." },
|
||||||
|
{ role: "user", content: prompt },
|
||||||
|
],
|
||||||
|
stream: false,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`Unsloth API error: ${response.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
return result.choices[0].message.content;
|
||||||
|
}
|
||||||
|
|
||||||
const filename = fileURLToPath(import.meta.url);
|
const filename = fileURLToPath(import.meta.url);
|
||||||
const dirname = path.dirname(filename);
|
const dirname = path.dirname(filename);
|
||||||
|
const META_TITLE = "Polaris Task Force";
|
||||||
|
const META_DESCRIPTION = "A gamified platform for Polaris Task Force, an Arma 3 unit.";
|
||||||
|
|
||||||
export default buildConfig({
|
export default buildConfig({
|
||||||
// serverURL: "http://localhost:3000/",
|
// serverURL: "http://localhost:3000/",
|
||||||
|
|
@ -60,15 +140,15 @@ export default buildConfig({
|
||||||
},
|
},
|
||||||
meta: {
|
meta: {
|
||||||
titleSuffix: "- Polaris Task Force",
|
titleSuffix: "- Polaris Task Force",
|
||||||
applicationName: "Polaris Task Force",
|
applicationName: META_TITLE,
|
||||||
abstract: "The Polaris Task Force official website.",
|
abstract: META_DESCRIPTION,
|
||||||
keywords: "Polaris Task Force, Polaris, Task Force, Polaris Task Force official website",
|
keywords: "Polaris Task Force, Polaris, Task Force, Polaris Task Force official website",
|
||||||
description: "The Polaris Task Force official website.",
|
description: META_DESCRIPTION,
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Polaris Task Force",
|
title: META_TITLE,
|
||||||
description: "The Polaris Task Force official website.",
|
description: META_DESCRIPTION,
|
||||||
url: "https://polaris-task-force.com",
|
url: "https://polaris-task-force.com",
|
||||||
siteName: "Polaris Task Force",
|
siteName: META_TITLE,
|
||||||
images: [
|
images: [
|
||||||
{
|
{
|
||||||
url: "/images/icon-favicon.png",
|
url: "/images/icon-favicon.png",
|
||||||
|
|
@ -156,7 +236,11 @@ export default buildConfig({
|
||||||
MissionFiles,
|
MissionFiles,
|
||||||
ModLists,
|
ModLists,
|
||||||
],
|
],
|
||||||
editor: lexicalEditor(),
|
editor: lexicalEditor({
|
||||||
|
features: ({ rootFeatures }) => {
|
||||||
|
return [HeadingFeature(), PayloadAiPluginLexicalEditorFeature()];
|
||||||
|
},
|
||||||
|
}),
|
||||||
secret: process.env.PAYLOAD_SECRET || "",
|
secret: process.env.PAYLOAD_SECRET || "",
|
||||||
typescript: {
|
typescript: {
|
||||||
outputFile: path.resolve(dirname, "payload-types.ts"),
|
outputFile: path.resolve(dirname, "payload-types.ts"),
|
||||||
|
|
@ -186,6 +270,146 @@ export default buildConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
// payloadCloudPlugin(),
|
// payloadCloudPlugin(),
|
||||||
// storage-adapter-placeholder
|
// storage-adapter-placeholder
|
||||||
|
payloadAiPlugin({
|
||||||
|
collections: {
|
||||||
|
[GameHardResources.slug]: true,
|
||||||
|
[GameStructures.slug]: true,
|
||||||
|
[GameVehicles.slug]: true,
|
||||||
|
[GameNpcs.slug]: true,
|
||||||
|
[GameEventLogs.slug]: true,
|
||||||
|
|
||||||
|
// Users
|
||||||
|
[Users.slug]: true,
|
||||||
|
[Ranks.slug]: true,
|
||||||
|
[Profiles.slug]: true,
|
||||||
|
[Awards.slug]: true,
|
||||||
|
[Qualifications.slug]: true,
|
||||||
|
[Assignments.slug]: true,
|
||||||
|
[Experience.slug]: true,
|
||||||
|
[UserNotifications.slug]: true,
|
||||||
|
|
||||||
|
// Intel
|
||||||
|
[Missions.slug]: true,
|
||||||
|
[MissionAttendances.slug]: true,
|
||||||
|
[Campaigns.slug]: true,
|
||||||
|
[Factions.slug]: true,
|
||||||
|
[Technologies.slug]: true,
|
||||||
|
|
||||||
|
// Logistics
|
||||||
|
[Assets.slug]: true,
|
||||||
|
[Resources.slug]: true,
|
||||||
|
[Vehicles.slug]: true,
|
||||||
|
[Structures.slug]: true,
|
||||||
|
[Shipments.slug]: true,
|
||||||
|
|
||||||
|
// Banking
|
||||||
|
[BankAccounts.slug]: true,
|
||||||
|
[BankTransactions.slug]: true,
|
||||||
|
[LedgerEntries.slug]: true,
|
||||||
|
|
||||||
|
// Locker
|
||||||
|
[LockerStorages.slug]: true,
|
||||||
|
[Loadouts.slug]: true,
|
||||||
|
|
||||||
|
// Market
|
||||||
|
[MarketListings.slug]: true,
|
||||||
|
[MarketNegotiations.slug]: true,
|
||||||
|
|
||||||
|
// Helpdesk
|
||||||
|
[Tickets.slug]: true,
|
||||||
|
},
|
||||||
|
debugging: true,
|
||||||
|
generationModels: [
|
||||||
|
{
|
||||||
|
id: "unsloth-text",
|
||||||
|
name: "Unsloth",
|
||||||
|
fields: ["text", "textarea", "richText"],
|
||||||
|
handler: async (
|
||||||
|
prompt: string,
|
||||||
|
options: {
|
||||||
|
locale: string;
|
||||||
|
model: string;
|
||||||
|
system: string;
|
||||||
|
schema: Record<string, unknown>;
|
||||||
|
maxTokens?: number;
|
||||||
|
temperature?: number;
|
||||||
|
},
|
||||||
|
) => {
|
||||||
|
const model = options.model || DEFAULT_MODEL;
|
||||||
|
|
||||||
|
if (options.schema?.definitions) {
|
||||||
|
const text = await generatePlainText(prompt, options.system, model);
|
||||||
|
const lexicalJson = plainTextToLexicalJson(text);
|
||||||
|
const ts = new ReadableStream({
|
||||||
|
start(controller) {
|
||||||
|
controller.enqueue(JSON.stringify(lexicalJson));
|
||||||
|
controller.close();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return createTextStreamResponse({ textStream: ts });
|
||||||
|
}
|
||||||
|
|
||||||
|
const streamResult = streamObject({
|
||||||
|
maxOutputTokens: options.maxTokens || 5000,
|
||||||
|
model: unslothProvider(model),
|
||||||
|
onError: (error) => {
|
||||||
|
console.error("Unsloth generateObject: ", error);
|
||||||
|
},
|
||||||
|
prompt,
|
||||||
|
schema: jsonSchema(options.schema),
|
||||||
|
system: options.system,
|
||||||
|
temperature: options.temperature ?? 0.7,
|
||||||
|
providerOptions: {
|
||||||
|
openai: {
|
||||||
|
strictJsonSchema: true,
|
||||||
|
structuredOutputs: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const textStream = new ReadableStream({
|
||||||
|
async start(controller) {
|
||||||
|
try {
|
||||||
|
for await (const chunk of streamResult.textStream) {
|
||||||
|
controller.enqueue(chunk);
|
||||||
|
}
|
||||||
|
if ((await streamResult.finishReason) === "length") {
|
||||||
|
controller.enqueue("\u0000");
|
||||||
|
}
|
||||||
|
controller.close();
|
||||||
|
} catch (error) {
|
||||||
|
controller.error(error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return createTextStreamResponse({ textStream });
|
||||||
|
},
|
||||||
|
generateText: async (prompt: string, system: string) => {
|
||||||
|
return generatePlainText(prompt, system, DEFAULT_MODEL);
|
||||||
|
},
|
||||||
|
output: "text",
|
||||||
|
settings: {
|
||||||
|
name: "unsloth-text-settings",
|
||||||
|
type: "group",
|
||||||
|
admin: {
|
||||||
|
condition(data) {
|
||||||
|
return data["model-id"] === "unsloth-text";
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: "model",
|
||||||
|
type: "text",
|
||||||
|
defaultValue: DEFAULT_MODEL,
|
||||||
|
label: "Model",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
label: "Unsloth Settings",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
],
|
],
|
||||||
email: nodemailerAdapter({
|
email: nodemailerAdapter({
|
||||||
defaultFromAddress: process.env.EMAIL_FROM_ADDRESS || "",
|
defaultFromAddress: process.env.EMAIL_FROM_ADDRESS || "",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue