Change shared files naming. Rework tree structure and item management rendering.
This commit is contained in:
parent
1a71637ebb
commit
777443471c
|
|
@ -11,9 +11,9 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Content } from '#shared/content.util';
|
||||
import * as Floating from '#shared/floating.util';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Content } from '~~/shared/content';
|
||||
import * as Floating from '~~/shared/floating';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
import { init } from '#shared/i18n';
|
||||
|
||||
onBeforeMount(() => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import render, { type MDProperties } from '#shared/markdown.util'
|
||||
import render, { type MDProperties } from '~~/shared/markdown'
|
||||
const { content, filter, properties } = defineProps<{
|
||||
content?: string,
|
||||
filter?: string,
|
||||
|
|
|
|||
|
|
@ -52,11 +52,11 @@
|
|||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { TreeDOM } from '#shared/tree';
|
||||
import { Content, iconByType } from '#shared/content.util';
|
||||
import { dom, icon } from '#shared/dom.util';
|
||||
import { unifySlug } from '#shared/general.util';
|
||||
import { tooltip } from '#shared/floating.util';
|
||||
import { link, loading } from '#shared/components.util';
|
||||
import { Content, iconByType } from '~~/shared/content';
|
||||
import { dom, icon } from '~~/shared/dom';
|
||||
import { unifySlug } from '~~/shared/general';
|
||||
import { tooltip } from '~~/shared/floating';
|
||||
import { link, loading } from '~~/shared/components';
|
||||
|
||||
const open = ref(false);
|
||||
let tree: TreeDOM | undefined;
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { hasPermissions } from "#shared/auth.util";
|
||||
import { hasPermissions } from "#shared/auth";
|
||||
|
||||
export default defineNuxtRouteMiddleware(async (to, from) => {
|
||||
const { loggedIn, fetch, user } = useUserSession();
|
||||
|
|
|
|||
|
|
@ -31,10 +31,10 @@
|
|||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { format } from '#shared/general.util';
|
||||
import { iconByType } from '#shared/content.util';
|
||||
import { format } from '~~/shared/general';
|
||||
import { iconByType } from '~~/shared/content';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
interface File
|
||||
{
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ const schemaList: Record<string, z.ZodObject<any> | null> = {
|
|||
<script setup lang="ts">
|
||||
import { z } from 'zod/v4';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
rights: ['admin'],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { unifySlug } from '#shared/general.util';
|
||||
import { CampaignSheet } from '#shared/campaign.util';
|
||||
import { unifySlug } from '~~/shared/general';
|
||||
import { CampaignSheet } from '~~/shared/campaign';
|
||||
|
||||
definePageMeta({
|
||||
requiresAuth: true,
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
requiresAuth: true,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { CharacterBuilder } from '#shared/character.util';
|
||||
import { unifySlug } from '#shared/general.util';
|
||||
import { CharacterBuilder } from '~~/shared/character';
|
||||
import { unifySlug } from '~~/shared/general';
|
||||
|
||||
definePageMeta({
|
||||
requiresAuth: true,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
<script setup lang="ts">
|
||||
import characterConfig from '#shared/character-config.json';
|
||||
import { unifySlug } from '#shared/general.util';
|
||||
import { unifySlug } from '~~/shared/general';
|
||||
import type { CharacterConfig } from '~/types/character';
|
||||
import { CharacterSheet } from '#shared/character.util';
|
||||
import { CharacterSheet } from '~~/shared/character';
|
||||
|
||||
/*
|
||||
text-light-red dark:text-dark-red border-light-red dark:border-dark-red bg-light-red dark:bg-dark-red
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import characterConfig from '#shared/character-config.json';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
import type { CharacterConfig } from '~/types/character';
|
||||
|
||||
definePageMeta({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
<script setup lang="ts">
|
||||
import { HomebrewBuilder } from '#shared/feature.util';
|
||||
import { HomebrewBuilder } from '#shared/feature';
|
||||
|
||||
|
||||
definePageMeta({
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Content } from '#shared/content.util';
|
||||
import { unifySlug } from '#shared/general.util';
|
||||
import { Content } from '~~/shared/content';
|
||||
import { unifySlug } from '~~/shared/general';
|
||||
|
||||
const element = useTemplateRef('element'), overview = ref();
|
||||
const route = useRouter().currentRoute;
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { Content, Editor } from '#shared/content.util';
|
||||
import { button, loading } from '#shared/components.util';
|
||||
import { dom, icon } from '#shared/dom.util';
|
||||
import { modal, tooltip } from '#shared/floating.util';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Content, Editor } from '~~/shared/content';
|
||||
import { button, loading } from '~~/shared/components';
|
||||
import { dom, icon } from '~~/shared/dom';
|
||||
import { modal, tooltip } from '~~/shared/floating';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
import { Icon } from '@iconify/vue';
|
||||
|
||||
definePageMeta({
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'login',
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'login',
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@
|
|||
import type { ZodError } from 'zod/v4';
|
||||
import { schema, type Login } from '~/schemas/login';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'login',
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
<script setup lang="ts">
|
||||
import { hasPermissions } from "#shared/auth.util";
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { hasPermissions } from "#shared/auth";
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
requiresAuth: true,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
import { ZodError } from 'zod/v4';
|
||||
import { schema, type Registration } from '~/schemas/registration';
|
||||
import { Icon } from '@iconify/vue';
|
||||
import { Toaster } from '#shared/components.util';
|
||||
import { Toaster } from '~~/shared/components';
|
||||
|
||||
definePageMeta({
|
||||
layout: 'login',
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { MAIN_STATS, ABILITIES, LEVELS, TRAINING_LEVELS, SPELL_TYPES, CATEGORIES, SPELL_ELEMENTS, ALIGNMENTS, RESISTANCES, DAMAGE_TYPES, WEAPON_TYPES, PropertySum, ITEM_BUFFER_KEYS } from "#shared/character.util";
|
||||
import type { MAIN_STATS, ABILITIES, LEVELS, TRAINING_LEVELS, SPELL_TYPES, CATEGORIES, SPELL_ELEMENTS, ALIGNMENTS, RESISTANCES, DAMAGE_TYPES, WEAPON_TYPES, PropertySum, ITEM_BUFFER_KEYS } from "#shared/character";
|
||||
import type { Localized } from "../types/general";
|
||||
|
||||
export type MainStat = typeof MAIN_STATS[number];
|
||||
|
|
@ -57,11 +57,19 @@ export type CharacterVariables = {
|
|||
|
||||
money: number;
|
||||
};
|
||||
export enum TreeFlag {
|
||||
AUTOMATIC = 1 << 0,
|
||||
REPEATING = 1 << 1,
|
||||
};
|
||||
export type TreeLeaf = {
|
||||
id: FeatureID;
|
||||
to?: FeatureID | Array<FeatureID> | Record<string, FeatureID>;
|
||||
flags?: number; //Flags from TreeFlag
|
||||
};
|
||||
export type TreeStructure = {
|
||||
name: string;
|
||||
nodes: FeatureID[];
|
||||
|
||||
paths: Record<number, number | number[]>;
|
||||
starts: FeatureID;
|
||||
nodes: Record<FeatureID, TreeLeaf>;
|
||||
};
|
||||
type CommonState = {
|
||||
capacity?: number;
|
||||
|
|
@ -212,7 +220,7 @@ export type FeatureTree = {
|
|||
id: FeatureID;
|
||||
category: "tree";
|
||||
tree: string;
|
||||
option?: number;
|
||||
option?: string;
|
||||
};
|
||||
export type FeatureChoice = {
|
||||
id: FeatureID;
|
||||
|
|
|
|||
|
|
@ -151,8 +151,39 @@ export default defineNuxtConfig({
|
|||
passwd: '',
|
||||
}
|
||||
},
|
||||
routeRules: {
|
||||
'/api/auth/session': {
|
||||
security: {
|
||||
rateLimiter: {
|
||||
headers: true,
|
||||
interval: 1000,
|
||||
tokensPerInterval: 5
|
||||
},
|
||||
}
|
||||
},
|
||||
'/api/auth/login': {
|
||||
security: {
|
||||
rateLimiter: {
|
||||
headers: true,
|
||||
interval: 1000,
|
||||
tokensPerInterval: 1
|
||||
},
|
||||
}
|
||||
},
|
||||
'/api/auth/register': {
|
||||
security: {
|
||||
rateLimiter: {
|
||||
headers: true,
|
||||
interval: 1000,
|
||||
tokensPerInterval: 1
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
security: {
|
||||
rateLimiter: false,
|
||||
rateLimiter: {
|
||||
headers: true,
|
||||
},
|
||||
headers: {
|
||||
contentSecurityPolicy: {
|
||||
"img-src": "'self' data: blob:",
|
||||
|
|
@ -164,7 +195,7 @@ export default defineNuxtConfig({
|
|||
},
|
||||
},
|
||||
sitemap: {
|
||||
exclude: ['/admin/**', '/explore/edit', '/user/mailvalidated', '/user/changing-password', '/user/reset-password', '/character/manage', '/campaign/create'],
|
||||
exclude: ['/admin/**', '/explore/edit', '/user/**', '/character/**', '/campaign/**'],
|
||||
sources: ['/api/__sitemap__/urls']
|
||||
},
|
||||
experimental: {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { hasPermissions } from "#shared/auth.util";
|
||||
import { hasPermissions } from "#shared/auth";
|
||||
|
||||
declare module 'nitropack'
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import useDatabase from '~/composables/useDatabase';
|
||||
import { projectFilesTable } from '~/db/schema';
|
||||
import { hasPermissions } from '#shared/auth.util';
|
||||
import { hasPermissions } from '~~/shared/auth';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const session = await getUserSession(e);
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { hasPermissions } from "#shared/auth.util";
|
||||
import { hasPermissions } from "#shared/auth";
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { and, eq, notInArray } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { hasPermissions } from "#shared/auth.util";
|
||||
import { hasPermissions } from "#shared/auth";
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { and, eq, notInArray } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import useDatabase from '~/composables/useDatabase';
|
||||
import { hasPermissions } from '#shared/auth.util';
|
||||
import { hasPermissions } from '~~/shared/auth';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const session = await getUserSession(e);
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@ import { eq } from 'drizzle-orm';
|
|||
import { z } from 'zod/v4';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { campaignTable } from '~/db/schema';
|
||||
import { CampaignValidation } from '#shared/campaign.util';
|
||||
import { cryptURI } from '#shared/general.util';
|
||||
import { CampaignValidation } from '~~/shared/campaign';
|
||||
import { cryptURI } from '~~/shared/general';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const body = await readValidatedBody(e, CampaignValidation.extend({ id: z.unknown(), }).safeParse);
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { campaignTable } from '~/db/schema';
|
||||
import { CampaignValidation } from '#shared/campaign.util';
|
||||
import { CampaignValidation } from '~~/shared/campaign';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const params = getRouterParam(e, "id");
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { eq, SQL, type Operators } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable, userPermissionsTable } from '~/db/schema';
|
||||
import { hasPermissions } from '#shared/auth.util';
|
||||
import { group } from '#shared/general.util';
|
||||
import { hasPermissions } from '~~/shared/auth';
|
||||
import { group } from '~~/shared/general';
|
||||
import type { Character, MainStat, TrainingLevel } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { z } from 'zod/v4';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterAbilitiesTable, characterLevelingTable, characterTable, characterTrainingTable } from '~/db/schema';
|
||||
import { CharacterValidation } from '#shared/character.util';
|
||||
import { CharacterValidation } from '~~/shared/character';
|
||||
import { type Ability, type MainStat } from '~/types/character';
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import useDatabase from '~/composables/useDatabase';
|
||||
import { campaignCharactersTable, campaignMembersTable, campaignTable, characterAbilitiesTable, characterChoicesTable, characterLevelingTable, characterTable, characterTrainingTable, usersTable } from '~/db/schema';
|
||||
import { group } from '#shared/general.util';
|
||||
import { group } from '~~/shared/general';
|
||||
import type { Character, MainStat, TrainingLevel } from '~/types/character';
|
||||
import { and, eq, exists, getTableColumns, isNotNull, or, sql } from 'drizzle-orm';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterAbilitiesTable, characterChoicesTable, characterLevelingTable, characterTable, characterTrainingTable } from '~/db/schema';
|
||||
import { CharacterValidation } from '#shared/character.util';
|
||||
import { CharacterValidation } from '~~/shared/character';
|
||||
import { type Ability, type MainStat } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import { CharacterNotesValidation } from '#shared/character.util';
|
||||
import { CharacterNotesValidation } from '~~/shared/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const id = getRouterParam(e, "id");
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { eq } from 'drizzle-orm';
|
||||
import useDatabase from '~/composables/useDatabase';
|
||||
import { characterTable } from '~/db/schema';
|
||||
import { CharacterVariablesValidation } from '#shared/character.util';
|
||||
import { CharacterVariablesValidation } from '~~/shared/character';
|
||||
import type { CharacterVariables } from '~/types/character';
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import useDatabase from '~/composables/useDatabase';
|
||||
import { hasPermissions } from '#shared/auth.util';
|
||||
import { hasPermissions } from '~~/shared/auth';
|
||||
import { projectContentTable, projectFilesTable } from '~/db/schema';
|
||||
import { eq } from 'drizzle-orm';
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import useDatabase from '~/composables/useDatabase';
|
||||
import { hasPermissions } from "#shared/auth.util";
|
||||
import { hasPermissions } from "#shared/auth";
|
||||
import { eq, sql } from "drizzle-orm";
|
||||
import { projectFilesTable } from "~/db/schema";
|
||||
import { Project } from "~/schemas/project";
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { dom, type VirtualNode } from "#shared/dom.virtual.util";
|
||||
import { dom, type VirtualNode } from "#shared/dom.virtual";
|
||||
|
||||
export default function(content: VirtualNode[])
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { dom, text } from "#shared/dom.virtual.util";
|
||||
import { dom, text } from "#shared/dom.virtual";
|
||||
|
||||
export default function(data: any)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { dom, text } from "#shared/dom.virtual.util";
|
||||
import { format } from "#shared/general.util";
|
||||
import { dom, text } from "#shared/dom.virtual";
|
||||
import { format } from "#shared/general";
|
||||
|
||||
export default function(data: any)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { and, eq, sql } from "drizzle-orm";
|
||||
import useDatabase from "~/composables/useDatabase";
|
||||
import { campaignMembersTable, campaignTable } from "~/db/schema";
|
||||
import { decryptURI } from "#shared/general.util";
|
||||
import { decryptURI } from "#shared/general";
|
||||
|
||||
export default defineEventHandler(async (e) => {
|
||||
const link = getRouterParam(e, "link");
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import type { SocketMessage } from "#shared/websocket.util";
|
||||
import type { SocketMessage } from "#shared/websocket";
|
||||
|
||||
export default defineWebSocketHandler({
|
||||
message(peer, message) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import useDatabase from "~/composables/useDatabase";
|
||||
import { extname, basename } from 'node:path';
|
||||
import type { CanvasColor, CanvasContent } from "~/types/canvas";
|
||||
import type { FileType, ProjectContent } from "#shared/content.util";
|
||||
import { getID, parsePath } from "#shared/general.util";
|
||||
import type { FileType, ProjectContent } from "#shared/content";
|
||||
import { getID, parsePath } from "#shared/general";
|
||||
import { projectContentTable, projectFilesTable } from "~/db/schema";
|
||||
|
||||
const typeMapping: Record<string, FileType> = {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { defu } from 'defu'
|
|||
import { createHooks } from 'hookable'
|
||||
import { useRuntimeConfig } from '#imports'
|
||||
import type { UserSession, UserSessionRequired } from '~/types/auth'
|
||||
import type { CompatEvent } from '~~/shared/websocket.util'
|
||||
import type { CompatEvent } from '~~/shared/websocket'
|
||||
|
||||
export interface SessionHooks {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -2,16 +2,16 @@ import { z } from "zod/v4";
|
|||
import type { User } from "~/types/auth";
|
||||
import characterConfig from '#shared/character-config.json';
|
||||
import type { Campaign } from "~/types/campaign";
|
||||
import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { button, foldable, loading, numberpicker, tabgroup, Toaster } from "#shared/components.util";
|
||||
import { CharacterCompiler, colorByRarity, stateFactory, subnameFactory } from "#shared/character.util";
|
||||
import { modal, tooltip } from "#shared/floating.util";
|
||||
import markdown from "#shared/markdown.util";
|
||||
import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom";
|
||||
import { button, foldable, loading, numberpicker, tabgroup, Toaster } from "#shared/components";
|
||||
import { CharacterCompiler, colorByRarity, stateFactory, subnameFactory } from "#shared/character";
|
||||
import { modal, tooltip } from "#shared/floating";
|
||||
import markdown from "#shared/markdown";
|
||||
import { preview } from "#shared/proses";
|
||||
import { Socket } from "#shared/websocket.util";
|
||||
import { Socket } from "#shared/websocket";
|
||||
import { reactive } from "#shared/reactive";
|
||||
import type { Character, CharacterConfig } from "~/types/character";
|
||||
import { MarkdownEditor } from "./editor.util";
|
||||
import { MarkdownEditor } from "./editor";
|
||||
import { getText } from "./i18n";
|
||||
|
||||
const config = characterConfig as CharacterConfig;
|
||||
|
|
@ -1,11 +1,11 @@
|
|||
import type { CanvasContent, CanvasEdge, CanvasNode } from "~/types/canvas";
|
||||
import { clamp, lerp } from "#shared/general.util";
|
||||
import { dom, icon, svg, type RedrawableHTML } from "#shared/dom.util";
|
||||
import render from "#shared/markdown.util";
|
||||
import { tooltip } from "#shared/floating.util";
|
||||
import { History } from "#shared/history.util";
|
||||
import { clamp, lerp } from "#shared/general";
|
||||
import { dom, icon, svg, type RedrawableHTML } from "#shared/dom";
|
||||
import render from "#shared/markdown";
|
||||
import { tooltip } from "#shared/floating";
|
||||
import { History } from "#shared/history";
|
||||
import { preview } from "#shared/proses";
|
||||
import { SpatialGrid } from "#shared/physics.util";
|
||||
import { SpatialGrid } from "#shared/physics";
|
||||
import type { CanvasPreferences } from "~/types/general";
|
||||
|
||||
/*
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -2,15 +2,15 @@ import type { Ability, Alignment, ArmorConfig, ArmorState, Character, CharacterC
|
|||
import { z } from "zod/v4";
|
||||
import characterConfig from '#shared/character-config.json';
|
||||
import proses, { preview } from "#shared/proses";
|
||||
import { button, checkbox, floater, foldable, input, loading, multiselect, numberpicker, select, tabgroup, Toaster, toggle } from "#shared/components.util";
|
||||
import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { followermenu, fullblocker, tooltip } from "#shared/floating.util";
|
||||
import { clamp } from "#shared/general.util";
|
||||
import markdown from "#shared/markdown.util";
|
||||
import { button, checkbox, floater, foldable, input, loading, multiselect, numberpicker, select, tabgroup, Toaster, toggle } from "#shared/components";
|
||||
import { div, dom, icon, span, text, type RedrawableHTML } from "#shared/dom";
|
||||
import { followermenu, fullblocker, tooltip } from "#shared/floating";
|
||||
import { clamp } from "#shared/general";
|
||||
import markdown from "#shared/markdown";
|
||||
import { getText } from "#shared/i18n";
|
||||
import type { User } from "~/types/auth";
|
||||
import { MarkdownEditor } from "#shared/editor.util";
|
||||
import { Socket } from "#shared/websocket.util";
|
||||
import { MarkdownEditor } from "#shared/editor";
|
||||
import { Socket } from "#shared/websocket";
|
||||
import { raw, reactive } from '#shared/reactive';
|
||||
|
||||
const config = characterConfig as CharacterConfig;
|
||||
|
|
@ -1469,12 +1469,163 @@ export class CharacterSheet
|
|||
|
||||
{ id: 'inventory', title: [ text('Inventaire') ], content: this.itemsTab(character) },
|
||||
|
||||
{ id: 'aspect', title: [ text('Aspect') ], content: () => [
|
||||
text('TODO'),
|
||||
] },
|
||||
|
||||
{ id: 'notes', title: [ text('Notes') ], content: () => [
|
||||
div('flex flex-col gap-2', [
|
||||
div('flex flex-col gap-2 border-b border-light-35 dark:border-dark-35 pb-4', [ div('flex flex-row w-full items-center justify-between', [ span('text-lg font-bold', 'Notes publics'), tooltip(button(loadableIcon, saveNotes, 'p-1 items-center justify-center'), 'Enregistrer', 'right') ]), div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 h-64', [ publicNotes.dom ]) ]),
|
||||
div('flex flex-col gap-2', [ span('text-lg font-bold', 'Notes privés'), div('border border-light-35 dark:border-dark-35 bg-light20 dark:bg-dark-20 p-1 h-64', [ privateNotes.dom ]) ]),
|
||||
])
|
||||
] },
|
||||
|
||||
{ id: 'details', title: [ text('Détails') ], content: () => [
|
||||
div('grid grid-cols-3 gap-2', [
|
||||
() => character.health ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Vie'), span('font-semibold', text(() => character.health ?? "")) ]) : undefined,
|
||||
() => character.mana ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Mana'), span('font-semibold', text(() => character.mana ?? "")) ]) : undefined,
|
||||
() => character.spellslots ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Sorts maitrisés'), span('font-semibold', text(() => character.spellslots ?? "")) ]) : undefined,
|
||||
() => character.artslots ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Oeuvres maitrisées'), span('font-semibold', text(() => character.artslots ?? "")) ]) : undefined,
|
||||
() => character.speed ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Vitesse de course'), span('font-semibold', text(() => character.speed === false ? 'Aucun' : character.speed ?? "")) ]) : undefined,
|
||||
() => character.capacity ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Poids supporté'), span('font-semibold', text(() => character.capacity === false ? 'Aucun' : character.capacity ?? "")) ]) : undefined,
|
||||
() => character.initiative ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Initiative'), span('font-semibold', text(() => character.initiative ?? "")) ]) : undefined,
|
||||
() => character.exhaust ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Fatigue max bonus'), span('font-semibold', text(() => character.exhaust ?? "")) ]) : undefined,
|
||||
() => character.itempower ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Puissance magique max'), span('font-semibold', text(() => character.itempower ?? "")) ]) : undefined,
|
||||
() => character.aspect.amount ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Nombre de transformation'), span('font-semibold', text(() => character.aspect.amount ?? "")) ]) : undefined,
|
||||
() => character.aspect.duration ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Durée de transformation'), span('font-semibold', text(() => character.aspect.duration ?? "")) ]) : undefined,
|
||||
() => character.aspect.shift_bonus ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de transformation'), span('font-semibold', text(() => character.aspect.shift_bonus ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.strength ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de force'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.strength ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.dexterity ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de dextérité'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.dexterity ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.constitution ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de constitution'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.constitution ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.intelligence ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance d\'intelligence'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.intelligence ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.curiosity ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de curiosité'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.curiosity ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.charisma ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de charisme'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.charisma ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.defense?.psyche ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de psyché'), span('font-semibold', text(() => character.aspect?.bonus?.defense?.psyche ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.athletics ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Athlétisme'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.athletics ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.acrobatics ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Acrobatisme'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.acrobatics ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.intimidation ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Intimidation'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.intimidation ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.sleightofhand ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Doigté'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.sleightofhand ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.stealth ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Discrétion'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.stealth ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.survival ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Survie'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.survival ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.investigation ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Enquête'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.investigation ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.history ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Histoire'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.history ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.religion ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Religion'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.religion ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.arcana ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Arcanes'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.arcana ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.understanding ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Compréhension'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.understanding ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.perception ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Perception'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.perception ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.performance ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Représentation'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.performance ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.medecine ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Médicine'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.medecine ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.persuasion ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Persuasion'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.persuasion ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.animalhandling ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Dressage'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.animalhandling ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.abilities?.deception ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Mensonge'), span('font-semibold', text(() => character.aspect?.bonus?.abilities?.deception ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.type.instinct ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts d\'instinct'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.type.instinct ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.type.precision ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de précision'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.type.precision ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.type.knowledge ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de savoir'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.type.knowledge ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.type.arts ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux oeuvres'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.type.arts ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.rank[1] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de rang 1'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.rank[1] ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.rank[2] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de rang 2'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.rank[2] ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.rank[3] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de rang 3'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.rank[3] ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.rank[4] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts spéciaux'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.rank[4] ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.fire ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de feu'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.fire ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.ice ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de glace'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.ice ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.thunder ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de foudre'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.thunder ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.earth ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de terre'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.earth ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.arcana ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts d\'arcane'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.arcana ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.air ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts d\'air'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.air ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.nature ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de nature'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.nature ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.light ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de lumière'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.light ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.spells?.elements.psyche ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de psy'), span('font-semibold', text(() => character.aspect?.bonus?.spells?.elements.psyche ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.light ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes légères'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.light ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.shield ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux boucliers'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.shield ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.heavy ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes lourdes'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.heavy ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.classic ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes standard'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.classic ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.throw ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes de jet'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.throw ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.natural ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes naturelles'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.natural ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.twohanded ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes à deux mains'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.twohanded ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.finesse ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes maniables'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.finesse ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.reach ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes longues'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.reach ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.projectile ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes à projectiles'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.projectile ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.weapon?.improvised ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes improvisées'), span('font-semibold', text(() => character.aspect?.bonus?.weapon?.improvised ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.stun ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des hébètements'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.stun ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.bleed ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des saignements'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.bleed ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.poison ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des poisons'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.poison ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.fear ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté de la peur'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.fear ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.influence ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des influences'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.influence ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.charm ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des charmes'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.charm ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.possesion ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des possessions'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.possesion ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.precision ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des sorts de précision'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.precision ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.knowledge ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des sorts de savoir'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.knowledge ?? "")) ]) : undefined,
|
||||
() => character.aspect.bonus?.resistance?.instinct ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des sorts d\'instinct'), span('font-semibold', text(() => character.aspect?.bonus?.resistance?.instinct ?? "")) ]) : undefined,
|
||||
() => character.defense.activeparry ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de parade active'), span('font-semibold', text(() => character.defense.activeparry ?? "")) ]) : undefined,
|
||||
() => character.defense.activedodge ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus d\'esquive active'), span('font-semibold', text(() => character.defense.activedodge ?? "")) ]) : undefined,
|
||||
() => character.defense.passiveparry ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de parade passive'), span('font-semibold', text(() => character.defense.passiveparry ?? "")) ]) : undefined,
|
||||
() => character.defense.passivedodge ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus d\'esquive parade passive'), span('font-semibold', text(() => character.defense.passivedodge ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.strength ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de force'), span('font-semibold', text(() => character.bonus.defense?.strength ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.dexterity ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de dextérité'), span('font-semibold', text(() => character.bonus.defense?.dexterity ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.constitution ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de constitution'), span('font-semibold', text(() => character.bonus.defense?.constitution ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.intelligence ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance d\'intelligence'), span('font-semibold', text(() => character.bonus.defense?.intelligence ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.curiosity ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de curiosité'), span('font-semibold', text(() => character.bonus.defense?.curiosity ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.charisma ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de charisme'), span('font-semibold', text(() => character.bonus.defense?.charisma ?? "")) ]) : undefined,
|
||||
() => character.bonus.defense.psyche ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de jet de résistance de psyché'), span('font-semibold', text(() => character.bonus.defense?.psyche ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.athletics ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Athlétisme'), span('font-semibold', text(() => character.bonus.abilities?.athletics ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.acrobatics ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Acrobatisme'), span('font-semibold', text(() => character.bonus.abilities?.acrobatics ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.intimidation ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Intimidation'), span('font-semibold', text(() => character.bonus.abilities?.intimidation ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.sleightofhand ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Doigté'), span('font-semibold', text(() => character.bonus.abilities?.sleightofhand ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.stealth ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Discrétion'), span('font-semibold', text(() => character.bonus.abilities?.stealth ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.survival ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Survie'), span('font-semibold', text(() => character.bonus.abilities?.survival ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.investigation ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Enquête'), span('font-semibold', text(() => character.bonus.abilities?.investigation ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.history ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Histoire'), span('font-semibold', text(() => character.bonus.abilities?.history ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.religion ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Religion'), span('font-semibold', text(() => character.bonus.abilities?.religion ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.arcana ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Arcanes'), span('font-semibold', text(() => character.bonus.abilities?.arcana ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.understanding ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Compréhension'), span('font-semibold', text(() => character.bonus.abilities?.understanding ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.perception ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Perception'), span('font-semibold', text(() => character.bonus.abilities?.perception ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.performance ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Représentation'), span('font-semibold', text(() => character.bonus.abilities?.performance ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.medecine ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Médicine'), span('font-semibold', text(() => character.bonus.abilities?.medecine ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.persuasion ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Persuasion'), span('font-semibold', text(() => character.bonus.abilities?.persuasion ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.animalhandling ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Dressage'), span('font-semibold', text(() => character.bonus.abilities?.animalhandling ?? "")) ]) : undefined,
|
||||
() => character.bonus.abilities.deception ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus en Mensonge'), span('font-semibold', text(() => character.bonus.abilities?.deception ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.type.instinct ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts d\'instinct'), span('font-semibold', text(() => character.bonus.spells?.type.instinct ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.type.precision ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de précision'), span('font-semibold', text(() => character.bonus.spells?.type.precision ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.type.knowledge ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de savoir'), span('font-semibold', text(() => character.bonus.spells?.type.knowledge ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.type.arts ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux oeuvres'), span('font-semibold', text(() => character.bonus.spells?.type.arts ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.rank[1] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de rang 1'), span('font-semibold', text(() => character.bonus.spells?.rank[1] ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.rank[2] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de rang 2'), span('font-semibold', text(() => character.bonus.spells?.rank[2] ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.rank[3] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de rang 3'), span('font-semibold', text(() => character.bonus.spells?.rank[3] ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.rank[4] ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts spéciaux'), span('font-semibold', text(() => character.bonus.spells?.rank[4] ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.fire ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de feu'), span('font-semibold', text(() => character.bonus.spells?.elements.fire ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.ice ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de glace'), span('font-semibold', text(() => character.bonus.spells?.elements.ice ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.thunder ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de foudre'), span('font-semibold', text(() => character.bonus.spells?.elements.thunder ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.earth ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de terre'), span('font-semibold', text(() => character.bonus.spells?.elements.earth ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.arcana ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts d\'arcane'), span('font-semibold', text(() => character.bonus.spells?.elements.arcana ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.air ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts d\'air'), span('font-semibold', text(() => character.bonus.spells?.elements.air ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.nature ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de nature'), span('font-semibold', text(() => character.bonus.spells?.elements.nature ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.light ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de lumière'), span('font-semibold', text(() => character.bonus.spells?.elements.light ?? "")) ]) : undefined,
|
||||
() => character.bonus.spells.elements.psyche ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux sorts de psy'), span('font-semibold', text(() => character.bonus.spells?.elements.psyche ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.light ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes légères'), span('font-semibold', text(() => character.bonus.weapon?.light ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.shield ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux boucliers'), span('font-semibold', text(() => character.bonus.weapon?.shield ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.heavy ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes lourdes'), span('font-semibold', text(() => character.bonus.weapon?.heavy ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.classic ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes standard'), span('font-semibold', text(() => character.bonus.weapon?.classic ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.throw ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes de jet'), span('font-semibold', text(() => character.bonus.weapon?.throw ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.natural ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes naturelles'), span('font-semibold', text(() => character.bonus.weapon?.natural ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.twohanded ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes à deux mains'), span('font-semibold', text(() => character.bonus.weapon?.twohanded ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.finesse ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes maniables'), span('font-semibold', text(() => character.bonus.weapon?.finesse ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.reach ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes longues'), span('font-semibold', text(() => character.bonus.weapon?.reach ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.projectile ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes à projectiles'), span('font-semibold', text(() => character.bonus.weapon?.projectile ?? "")) ]) : undefined,
|
||||
() => character.bonus.weapon.improvised ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus aux armes improvisées'), span('font-semibold', text(() => character.bonus.weapon?.improvised ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.stun ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des hébètements'), span('font-semibold', text(() => character.bonus.resistance?.stun ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.bleed ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des saignements'), span('font-semibold', text(() => character.bonus.resistance?.bleed ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.poison ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des poisons'), span('font-semibold', text(() => character.bonus.resistance?.poison ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.fear ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté de la peur'), span('font-semibold', text(() => character.bonus.resistance?.fear ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.influence ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des influences'), span('font-semibold', text(() => character.bonus.resistance?.influence ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.charm ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des charmes'), span('font-semibold', text(() => character.bonus.resistance?.charm ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.possesion ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des possessions'), span('font-semibold', text(() => character.bonus.resistance?.possesion ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.precision ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des sorts de précision'), span('font-semibold', text(() => character.bonus.resistance?.precision ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.knowledge ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des sorts de savoir'), span('font-semibold', text(() => character.bonus.resistance?.knowledge ?? "")) ]) : undefined,
|
||||
() => character.bonus.resistance.instinct ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de difficulté des sorts d\'instinct'), span('font-semibold', text(() => character.bonus.resistance?.instinct ?? "")) ]) : undefined,
|
||||
() => character.craft.level ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Tier de fabrication'), span('font-semibold', text(() => character.craft.level ?? "")) ]) : undefined,
|
||||
() => character.craft.bonus ? div('flex flex-row gap-1 items-center justify-between', [ span('italic text-light-70 dark:text-dark-70', 'Bonus de fabrication'), span('font-semibold', text(() => character.craft.bonus ?? "")) ]) : undefined,
|
||||
])
|
||||
] },
|
||||
], { focused: this.tab, class: { container: 'flex-1 gap-4 px-4 w-[960px] h-full', content: 'overflow-auto' }, switch: v => { this.tab = v; } });
|
||||
this.container.replaceChildren(div('flex flex-col justify-start gap-1 h-full', [
|
||||
div("flex flex-row gap-4 justify-between", [
|
||||
|
|
@ -1607,27 +1758,6 @@ export class CharacterSheet
|
|||
]),
|
||||
|
||||
div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", { list: character.mastery, render: (e, _c) => proses('a', preview, [ text('Arme légère') ], { href: 'regles/annexes/equipement#Les armes légères', label: 'Arme légère', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline', }) }),
|
||||
/* () => character.mastery.strength + character.mastery.dexterity > 0 ? div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
||||
() => character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme légère') ], { href: 'regles/annexes/equipement#Les armes légères', label: 'Arme légère', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline', }) : undefined,
|
||||
() => character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme de jet') ], { href: 'regles/annexes/equipement#Les armes de jet', label: 'Arme de jet', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.strength + character.mastery.dexterity > 0 ? proses('a', preview, [ text('Arme naturelle') ], { href: 'regles/annexes/equipement#Les armes naturelles', label: 'Arme naturelle', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.strength > 1 ? proses('a', preview, [ text('Arme standard') ], { href: 'regles/annexes/equipement#Les armes', label: 'Arme standard', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.strength > 1 ? proses('a', preview, [ text('Arme improvisée') ], { href: 'regles/annexes/equipement#Les armes improvisées', label: 'Arme improvisée', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.strength > 2 ? proses('a', preview, [ text('Arme lourde') ], { href: 'regles/annexes/equipement#Les armes lourdes', label: 'Arme lourde', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.strength > 3 ? proses('a', preview, [ text('Arme à deux mains') ], { href: 'regles/annexes/equipement#Les armes à deux mains', label: 'Arme à deux mains', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.dexterity > 0 && character.mastery.strength > 1 ? proses('a', preview, [ text('Arme maniable') ], { href: 'regles/annexes/equipement#Les armes maniables', label: 'Arme maniable', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.dexterity > 1 && character.mastery.strength > 1 ? proses('a', preview, [ text('Arme à projectiles') ], { href: 'regles/annexes/equipement#Les armes à projectiles', label: 'Arme à projectiles', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.dexterity > 1 && character.mastery.strength > 2 ? proses('a', preview, [ text('Arme longue') ], { href: 'regles/annexes/equipement#Les armes longues', label: 'Arme longue', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.shield > 0 ? proses('a', preview, [ text('Bouclier') ], { href: 'regles/annexes/equipement#Les boucliers', label: 'Bouclier', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.shield > 0 && character.mastery.strength > 3 ? proses('a', preview, [ text('Bouclier à deux mains') ], { href: 'regles/annexes/equipement#Les boucliers à deux mains', label: 'Bouclier à deux mains', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
]) : undefined,
|
||||
|
||||
() => character.mastery.armor > 0 ? div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
||||
() => character.mastery.armor > 0 ? proses('a', preview, [ text('Armure légère') ], { href: 'regles/annexes/equipement#Les armures légères', label: 'Armure légère', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.armor > 1 ? proses('a', preview, [ text('Armure standard') ], { href: 'regles/annexes/equipement#Les armures', label: 'Armure standard', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
() => character.mastery.armor > 2 ? proses('a', preview, [ text('Armure lourde') ], { href: 'regles/annexes/equipement#Les armures lourdes', label: 'Armure lourde', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }) : undefined,
|
||||
]) : undefined, */
|
||||
|
||||
div("grid grid-cols-2 gap-x-3 gap-y-1 text-sm", [
|
||||
() => character.spellranks.precision > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Précision') ], { href: 'regles/la-magie/magie#Les sorts de précision', label: 'Précision', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', text(() => character.spellranks.precision)) ]) : undefined,
|
||||
() => character.spellranks.knowledge > 0 ? div('flex flex-row items-center gap-2', [ proses('a', preview, [ text('Savoir') ], { href: 'regles/la-magie/magie#Les sorts de savoir', label: 'Savoir', class: 'text-sm text-light-70 dark:text-dark-70 cursor-help decoration-dotted underline' }), span('font-bold', text(() => character.spellranks.knowledge)) ]) : undefined,
|
||||
|
|
@ -1897,7 +2027,7 @@ export class CharacterSheet
|
|||
const weight = div(() => ['flex flex-row min-w-16 gap-2 justify-between items-center px-2', { 'cursor-help': e.amount > 1 && !!item.weight }], [ icon('mdi:weight', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span(() => ({ 'underline decoration-1 decoration-dotted underline-offset-2': e.amount > 1 && !!item.weight }), () => item.weight ? `${item.weight * e.amount}` : '-') ]);
|
||||
return foldable(() => [
|
||||
markdown(getText(item.description)),
|
||||
div('flex flex-row gap-1', { list: () => e.enchantments!.map(e => config.enchantments[e]).filter(e => !!e), render: (e, _c) => _c ?? floater(div('flex flex-row gap-2 border border-accent-blue px-2 rounded-full py-px bg-accent-blue bg-opacity-20', [ span('text-sm font-semibold tracking-thigh', e.name), div('flex flex-row gap-1 items-center', [icon('game-icons:bolt-drop', { width: 12, height: 12 }), span('text-sm font-light', e.power)]) ]), () => [markdown(getText(e.description), undefined, { tags: { a: preview } })], { class: 'max-w-96 max-h-48 p-2', position: "right" }) }),
|
||||
div('flex flex-row gap-1', { list: () => e.enchantments!.map(e => config.enchantments[e]).filter(e => !!e), render: (e, _c) => _c ?? floater(div('flex flex-row gap-2 border border-accent-blue px-2 rounded-full py-px bg-accent-blue bg-opacity-20', [ span('text-sm font-semibold tracking-tight', e.name), div('flex flex-row gap-1 items-center', [icon('game-icons:bolt-drop', { width: 12, height: 12 }), span('text-sm font-light', e.power)]) ]), () => [markdown(getText(e.description), undefined, { tags: { a: preview } })], { class: 'max-w-96 max-h-48 p-2', position: "right" }) }),
|
||||
div('flex flex-row justify-center gap-1', [
|
||||
this.character?.character.campaign ? button(text('Partager'), () => {
|
||||
|
||||
|
|
@ -1929,7 +2059,7 @@ export class CharacterSheet
|
|||
div('flex flex-row items-center gap-4', [
|
||||
item.equippable ? checkbox({ defaultValue: e.equipped, change: v => {
|
||||
e.equipped = v;
|
||||
this.character?.update(e);
|
||||
|
||||
}, class: { container: '!w-5 !h-5' } }) : undefined,
|
||||
div('flex flex-row items-center gap-4', [ span([colorByRarity[item.rarity], 'text-lg'], item.name), div('flex flex-row gap-2 text-light-60 dark:text-dark-60 text-sm italic', subnameFactory(item).map(e => span('', e))) ]),
|
||||
item.category === 'armor' ? div('flex flex-row gap-2 items-center text-sm', [ icon('game-icons:shoulder-armor', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('italic', () => `${item.health + ((e.state as ArmorState)?.health ?? 0) - ((e.state as ArmorState)?.loss ?? 0)}/${item.health + ((e.state as ArmorState)?.health ?? 0)} (${[item.absorb.static + ((e.state as ArmorState).absorb?.flat ?? 0) > 0 ? '-' + (item.absorb.static + ((e.state as ArmorState).absorb?.flat ?? 0)) : undefined, item.absorb.percent + ((e.state as ArmorState).absorb?.percent ?? 0) > 0 ? '-' + (item.absorb.percent + ((e.state as ArmorState).absorb?.percent ?? 0)) + '%' : undefined].filter(e => !!e).join('/')})`) ]) :
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import type { RouteLocationAsRelativeTyped, RouteLocationRaw, RouteMapGeneric } from "vue-router";
|
||||
import { type NodeProperties, type Class, type NodeChildren, dom, mergeClasses, text, div, icon, type Node, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { contextmenu, followermenu, minimizeBox, popper, teleport, tooltip, type FloatState } from "#shared/floating.util";
|
||||
import { clamp } from "#shared/general.util";
|
||||
import { type NodeProperties, type Class, type NodeChildren, dom, mergeClasses, text, div, icon, type Node, type RedrawableHTML } from "#shared/dom";
|
||||
import { contextmenu, followermenu, minimizeBox, popper, teleport, tooltip, type FloatState } from "#shared/floating";
|
||||
import { clamp } from "#shared/general";
|
||||
import { Tree } from "#shared/tree";
|
||||
import type { Placement } from "@floating-ui/dom";
|
||||
import { type Reactive } from '#shared/reactive';
|
||||
|
|
@ -1,14 +1,14 @@
|
|||
import { safeDestr as parse } from 'destr';
|
||||
import { Canvas, CanvasEditor } from "#shared/canvas.util";
|
||||
import render, { renderMDAsText } from "#shared/markdown.util";
|
||||
import { confirm, contextmenu, tooltip } from "#shared/floating.util";
|
||||
import { cancelPropagation, dom, icon, text, type Node, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { loading } from "#shared/components.util";
|
||||
import { Canvas, CanvasEditor } from "#shared/canvas";
|
||||
import render, { renderMDAsText } from "#shared/markdown";
|
||||
import { confirm, contextmenu, tooltip } from "#shared/floating";
|
||||
import { cancelPropagation, dom, icon, text, type Node, type RedrawableHTML } from "#shared/dom";
|
||||
import { loading } from "#shared/components";
|
||||
import prose, { h1, h2 } from "#shared/proses";
|
||||
import { getID, lerp, parsePath } from '#shared/general.util';
|
||||
import { getID, lerp, parsePath } from '~~/shared/general';
|
||||
import { TreeDOM, type Recursive } from '#shared/tree';
|
||||
import { History } from '#shared/history.util';
|
||||
import { MarkdownEditor } from '#shared/editor.util';
|
||||
import { History } from '~~/shared/history';
|
||||
import { MarkdownEditor } from '~~/shared/editor';
|
||||
import type { CanvasContent } from '~/types/canvas';
|
||||
|
||||
import { combine } from '@atlaskit/pragmatic-drag-and-drop/combine';
|
||||
|
|
@ -18,7 +18,7 @@ import { autoScrollForElements } from '@atlaskit/pragmatic-drag-and-drop-auto-sc
|
|||
import type { CleanupFn } from '@atlaskit/pragmatic-drag-and-drop/dist/types/internal-types';
|
||||
import type { CharacterConfig, FeatureID, MainStat, TrainingLevel } from '~/types/character';
|
||||
import { getText } from './i18n';
|
||||
import { mainStatTexts } from './character.util';
|
||||
import { mainStatTexts } from './character';
|
||||
|
||||
export type FileType = keyof ContentMap;
|
||||
export interface ContentMap
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { buildIcon, getIcon, iconLoaded, loadIcon, type IconifyIcon } from 'iconify-icon';
|
||||
import { loading } from './components.util';
|
||||
import { loading } from './components';
|
||||
import { _defer, raw, reactivity, type Proxy, type Reactive } from './reactive';
|
||||
|
||||
export type RedrawableHTML = HTMLElement;
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { iconLoaded, loadIcon, getIcon } from "iconify-icon";
|
||||
import type { NodeProperties, Class } from "#shared/dom.util";
|
||||
import type { NodeProperties, Class } from "#shared/dom";
|
||||
|
||||
export type VirtualNode = string;
|
||||
export function dom<K extends keyof HTMLElementTagNameMap>(tag: K, properties?: NodeProperties, children?: VirtualNode[]): VirtualNode
|
||||
|
|
@ -7,15 +7,15 @@ import { acceptCompletion, autocompletion, closeBrackets, closeBracketsKeymap, c
|
|||
import { markdown, markdownLanguage } from '@codemirror/lang-markdown';
|
||||
import { IterMode, Tree, type SyntaxNodeRef } from '@lezer/common';
|
||||
import { tags } from '@lezer/highlight';
|
||||
import { div, dom, icon, span, type RedrawableHTML } from '#shared/dom.util';
|
||||
import { div, dom, icon, span, type RedrawableHTML } from '~~/shared/dom';
|
||||
import { callout as calloutExtension, calloutKeymap } from '#shared/grammar/callout.extension';
|
||||
import { wikilink as wikilinkExtension, autocompletion as wikilinkAutocompletion } from '#shared/grammar/wikilink.extension';
|
||||
import renderMarkdown from '#shared/markdown.util';
|
||||
import renderMarkdown from '~~/shared/markdown';
|
||||
import prose, { a, blockquote, tag, h1, h2, h3, h4, h5, hr, li, small, table, td, th, callout } from "#shared/proses";
|
||||
import { tagTag, tag as tagExtension } from './grammar/tag.extension';
|
||||
import { WeakerSet } from './general.util';
|
||||
import { button, numberpicker } from './components.util';
|
||||
import { contextmenu, followermenu } from './floating.util';
|
||||
import { WeakerSet } from './general';
|
||||
import { button, numberpicker } from './components';
|
||||
import { contextmenu, followermenu } from './floating';
|
||||
|
||||
const External = Annotation.define<boolean>();
|
||||
const Hidden = Decoration.mark({ class: 'hidden' });
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
import type { Ability, AspectConfig, CharacterConfig, CommonItemConfig, DamageType, Feature, FeatureChoice, FeatureEquipment, FeatureItem, FeatureList, FeatureTree, FeatureValue, ItemConfig, Level, MainStat, RaceConfig, Resistance, SpellConfig, TrainingLevel, WeaponType } from "~/types/character";
|
||||
import { div, dom, icon, span, text, type NodeChildren, type RedrawableHTML } from "#shared/dom.util";
|
||||
import { MarkdownEditor } from "#shared/editor.util";
|
||||
import type { Ability, ArmorConfig, AspectConfig, CharacterConfig, CommonItemConfig, DamageType, Feature, FeatureChoice, FeatureEquipment, FeatureItem, FeatureList, FeatureTree, FeatureValue, ItemConfig, Level, MainStat, MundaneConfig, RaceConfig, Resistance, SpellConfig, TrainingLevel, WeaponConfig, WeaponType, WondrousConfig } from "~/types/character";
|
||||
import { div, dom, icon, span, text, type NodeChildren, type RedrawableHTML } from "#shared/dom";
|
||||
import { MarkdownEditor } from "#shared/editor";
|
||||
import { preview } from "#shared/proses";
|
||||
import { button, checkbox, combobox, foldable, input, multiselect, numberpicker, optionmenu, select, tabgroup, table, toggle, type Option } from "#shared/components.util";
|
||||
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating.util";
|
||||
import { ABILITIES, abilityTexts, ALIGNMENTS, alignmentTexts, categoryText, damageTypeTexts, elementTexts, LEVELS, MAIN_STATS, mainStatShortTexts, mainStatTexts, masteryTexts, rarityText, RESISTANCES, resistanceTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts, weaponTypeTexts } from "#shared/character.util";
|
||||
import { button, checkbox, combobox, foldable, input, multiselect, numberpicker, optionmenu, select, tabgroup, table, toggle, type Option } from "#shared/components";
|
||||
import { confirm, contextmenu, fullblocker, tooltip } from "#shared/floating";
|
||||
import { ABILITIES, abilityTexts, ALIGNMENTS, alignmentTexts, categoryText, colorByRarity, damageTypeTexts, elementTexts, LEVELS, MAIN_STATS, mainStatShortTexts, mainStatTexts, masteryTexts, rarityText, RESISTANCES, resistanceTexts, SPELL_ELEMENTS, SPELL_TYPES, spellTypeTexts, subnameFactory, weaponTypeTexts } from "#shared/character";
|
||||
import characterConfig from "#shared/character-config.json";
|
||||
import { getID } from "#shared/general.util";
|
||||
import markdown, { markdownReference, renderMDAsText } from "#shared/markdown.util";
|
||||
import { getID } from "#shared/general";
|
||||
import markdown, { markdownReference, renderMDAsText } from "#shared/markdown";
|
||||
import { Tree } from "#shared/tree";
|
||||
import { getText, setText } from "#shared/i18n";
|
||||
import { reactive } from "./reactive";
|
||||
|
|
@ -226,7 +226,6 @@ export class HomebrewBuilder
|
|||
const editing = reactive({
|
||||
id: '',
|
||||
});
|
||||
|
||||
const render = (spell: SpellConfig) => {
|
||||
return foldable(() => [
|
||||
markdown(spell.description, undefined, { tags: { a: preview } }),
|
||||
|
|
@ -246,8 +245,7 @@ export class HomebrewBuilder
|
|||
div('flex flex-row justify-center gap-2', [ button(icon('radix-icons:pencil-1'), () => editing.id = spell.id, 'p-1'), button(icon('radix-icons:trash'), () => remove(spell), 'p-1') ])
|
||||
])
|
||||
], { class: { container: 'border-light-35 dark:border-dark-35 py-1', content: 'gap-2 px-4 py-1 flex items-center *:flex-1' }, open: false });
|
||||
}
|
||||
|
||||
};
|
||||
const edit = (spell: SpellConfig) => {
|
||||
MarkdownEditor.singleton.onChange = v => {};
|
||||
MarkdownEditor.singleton.content = spell.description;
|
||||
|
|
@ -270,7 +268,6 @@ export class HomebrewBuilder
|
|||
])
|
||||
], { class: { container: 'border-light-35 dark:border-dark-35 py-1', content: 'gap-2 px-4 py-1 flex items-center *:flex-1' }, open: false });
|
||||
};
|
||||
|
||||
const add = () => {
|
||||
const id = getID();
|
||||
config.spells[id] = {
|
||||
|
|
@ -295,7 +292,7 @@ export class HomebrewBuilder
|
|||
}
|
||||
});
|
||||
}
|
||||
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), div('flex flex-col divide-y', { list: Object.values(config.spells), render: (e, _c) => editing.id === e.id ? edit(e) : _c ?? render(e)}) ] ) ];
|
||||
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), add, 'p-1') ]), div('flex flex-col divide-y', { list: () => Object.values(config.spells), render: (e, _c) => editing.id === e.id ? edit(e) : _c ?? render(e)}) ] ) ];
|
||||
}
|
||||
actions()
|
||||
{
|
||||
|
|
@ -404,36 +401,22 @@ export class HomebrewBuilder
|
|||
rarity: 'common',
|
||||
equippable: false,
|
||||
consummable: false,
|
||||
}
|
||||
};
|
||||
switch(category)
|
||||
{
|
||||
case 'armor':
|
||||
return { ...common, category: category, health: 0, absorb: { percent: 0, static: 0 }, type: 'light' };
|
||||
return { ...common, category: category, health: 0, absorb: { percent: 0, static: 0 }, type: 'light' } as CommonItemConfig & ArmorConfig;
|
||||
case 'weapon':
|
||||
return { ...common, category: category, damage: { type: 'slashing', value: '0' }, type: ['classic'] };
|
||||
return { ...common, category: category, damage: { type: 'slashing', value: '0' }, type: ['classic'] } as CommonItemConfig & WeaponConfig;
|
||||
case 'wondrous':
|
||||
case 'mundane':
|
||||
return { ...common, category: category };
|
||||
return { ...common, category: category } as CommonItemConfig & (MundaneConfig | WondrousConfig);
|
||||
}
|
||||
};
|
||||
const render = (item: ItemConfig) => {
|
||||
return {
|
||||
dom: div('flex flex-col gap-2 border border-light-35 dark:border-dark-35 p-1', [
|
||||
div('flex flex-row justify-between', [ span('text-xl font-bold ps-2', item.name), div('flex flex-row gap-2 items-center', [ div('flex flex-row items-center gap-2', [ tooltip(button(icon('radix-icons:pencil-1'), () => edit(item), 'p-1'), 'Modifier', 'top'), tooltip(button(icon('radix-icons:trash'), () => remove(item), 'p-1'), 'Supprimer', 'top') ]) ])]),
|
||||
div('flex flex-row gap-2 px-4 items-center', [ span('text-sm text-light-70 dark:text-dark-70', categoryText[item.category]), text('-'), span('text-sm text-light-70 dark:text-dark-70', rarityText[item.rarity]), ]),
|
||||
markdown(getText(item.description), undefined, { tags: { a: preview }, class: 'px-2 py-1 border-l-4 border-light-30 dark:border-dark-30 h-full' }),
|
||||
]),
|
||||
item,
|
||||
};
|
||||
};
|
||||
const add = (category: Category) => {
|
||||
const item = defaultItem(category);
|
||||
config.texts[item.description!] = { 'fr_FR': '' };
|
||||
setText(item.description, '');
|
||||
config.items[item.id!] = item;
|
||||
|
||||
const option = render(item);
|
||||
options.push(option);
|
||||
optionHolder.appendChild(option.dom);
|
||||
};
|
||||
const remove = (item: ItemConfig) => {
|
||||
confirm(`Voulez vous vraiment supprimer l'effet "${item.name}" ?`).then(e => {
|
||||
|
|
@ -441,24 +424,38 @@ export class HomebrewBuilder
|
|||
{
|
||||
delete config.texts[item.description];
|
||||
delete config.items[item.id];
|
||||
|
||||
const idx = options.findIndex(e => e.item === item);
|
||||
options.splice(idx, 1)[0]?.dom.remove();
|
||||
}
|
||||
});
|
||||
};
|
||||
const edit = (item: ItemConfig) => {
|
||||
ItemPanel.edit(item).then(f => {
|
||||
const idx = options.findIndex(e => e.item === item);
|
||||
config.items[item.id] = f;
|
||||
const element = render(f);
|
||||
options[idx]?.dom.replaceWith(element.dom);
|
||||
options[idx] = element;
|
||||
Object.assign(config.items[f.id]!, f);
|
||||
}).catch((e) => {});
|
||||
}
|
||||
const options = Object.values(config.items).map(e => render(e));
|
||||
const optionHolder = div('grid grid-cols-3 gap-2', options.map(e => e.dom));
|
||||
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), optionmenu([{ title: 'Objet inerte', click: () => add('mundane') }, { title: 'Armure', click: () => add('armor') }, { title: 'Arme', click: () => add('weapon') }, { title: 'Objet magique', click: () => add('wondrous') }], { position: 'left-start' }), 'p-1') ]), optionHolder ] ) ];
|
||||
return [ div('flex px-8 py-4 flex-col gap-4', [ div('flex flex-row-reverse', [ button(icon('radix-icons:plus'), optionmenu([{ title: 'Objet inerte', click: () => add('mundane') }, { title: 'Armure', click: () => add('armor') }, { title: 'Arme', click: () => add('weapon') }, { title: 'Objet magique', click: () => add('wondrous') }], { position: 'left-start' }), 'p-1') ]), div('flex flex-col gap-1', {
|
||||
list: () => Object.keys(config.items),
|
||||
render: (e, _c) => {
|
||||
const item = config.items[e];
|
||||
|
||||
if(!item) return;
|
||||
|
||||
return _c ?? foldable(() => [ markdown(getText(item.description)) ], [div('flex flex-row justify-between', [
|
||||
div('flex flex-row items-center gap-4', [
|
||||
div('flex flex-row items-center gap-4', [ span(() => [colorByRarity[item.rarity], 'text-lg'], () => item.name), div('flex flex-row gap-2 text-light-60 dark:text-dark-60 text-sm italic', subnameFactory(item).map(e => (() => span('', e)))) ]),
|
||||
]),
|
||||
div('flex flex-row gap-1', [
|
||||
div('flex flex-row items-center divide-x divide-light-50 dark:divide-dark-50 divide-dashed px-2', [
|
||||
div('flex flex-row w-20 gap-2 justify-between items-center px-2', [ icon('game-icons:bolt-drop', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('', () => item.powercost || item.capacity ? `${item.powercost ?? 0}/${item.capacity ?? 0}` : '-') ]),
|
||||
div('flex flex-row w-20 gap-2 justify-between items-center px-2', [ icon('mdi:weight', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('', () => item.weight?.toString() ?? '-') ]),
|
||||
div('flex flex-row w-20 gap-2 justify-between items-center px-2', [ icon('game-icons:battery-pack', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('', () => item.charge ? `${item.charge}` : '-') ]),
|
||||
div('flex flex-row w-20 gap-2 justify-between items-center px-2', [ icon('ph:coin', { width: 16, height: 16, class: 'text-light-70 dark:text-dark-70' }), span('', () => item.price ? `${item.price}` : '-') ]),
|
||||
]),
|
||||
button(icon('radix-icons:pencil-2'), () => edit(config.items[e]!), 'p-1'),
|
||||
button(icon('radix-icons:trash'), () => remove(config.items[e]!), 'p-1'),
|
||||
])
|
||||
])], { open: false, class: { icon: 'px-2', container: 'border border-light-35 dark:border-dark-35 p-1 gap-2', content: 'px-2 pb-1' } })
|
||||
}
|
||||
}) ] ) ];
|
||||
}
|
||||
trees()
|
||||
{
|
||||
|
|
@ -471,13 +468,13 @@ export class HomebrewBuilder
|
|||
description,
|
||||
effect: [],
|
||||
};
|
||||
config.trees[editing.tree!]!.nodes.push(id);
|
||||
config.trees[editing.tree!]!.nodes[id] = { id };
|
||||
}
|
||||
const editing = reactive({ tree: undefined as string | undefined });
|
||||
return [div('', [
|
||||
() => editing.tree !== undefined ? undefined : div('flex flex-row gap-1 justify-start overflow-x-auto max-w-full', { list: Object.values(config.trees), render: (e, _c) => _c ?? div('grid grid-cols-2 gap-2 items-baseline w-64 border border-light-35 dark:border-dark-35 p-2', [ span('text-lg font-semibold tracking-thigh', e.name), div('flex flex-row justify-end', [ tooltip(button(icon('radix-icons:pencil-1', { width: 16, height: 16 }), () => editing.tree = e.name, 'p-1'), 'Modifier', 'left') ]), span('italic', `${Object.keys(e.nodes).length} nodes`) ]) }),
|
||||
() => editing.tree !== undefined ? undefined : div('flex flex-row gap-1 justify-start overflow-x-auto max-w-full', { list: Object.keys(config.trees), render: (e, _c) => _c ?? div('grid grid-cols-2 gap-2 items-baseline w-64 border border-light-35 dark:border-dark-35 p-2', [ span('text-lg font-semibold tracking-thigh', config.trees[e]!.name), div('flex flex-row justify-end', [ tooltip(button(icon('radix-icons:pencil-1', { width: 16, height: 16 }), () => editing.tree = e, 'p-1'), 'Modifier', 'left') ]), span('italic', `${Object.keys(config.trees[e]!.nodes).length} nodes`) ]) }),
|
||||
() => editing.tree === undefined ? undefined : div('', [
|
||||
foldable([ div('flex flex-col gap-2', { list: config.trees[editing.tree]!.nodes, render: (e, _c) => _c ?? dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: () => {
|
||||
foldable([ div('flex flex-col gap-2', { list: Object.keys(config.trees[editing.tree]!.nodes), render: (e, _c) => _c ?? dom("div", { class: ["border border-light-40 dark:border-dark-40 cursor-pointer px-2 py-1 w-[400px] hover:border-light-50 dark:hover:border-dark-50"], listeners: { click: () => {
|
||||
FeaturePanel.edit(config.features[e]!).then(feature => {
|
||||
config.features[e] = feature;
|
||||
}).catch(e => {});
|
||||
|
|
@ -650,38 +647,42 @@ class FeatureEditor
|
|||
}
|
||||
private editTree(buffer: Partial<FeatureTree>)
|
||||
{
|
||||
const path = input("text", { defaultValue: buffer.option ?? "", input: v => { buffer.option = v }, class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] w-[240px]' });
|
||||
const edit = tooltip(button(icon('radix-icons:gear', { width: 16, height: 16 }), function() {
|
||||
buffer.option = 0;
|
||||
path.value = "";
|
||||
this.parentNode?.insertBefore(path, this);
|
||||
this.replaceWith(remove);
|
||||
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Option fixe', 'right'), remove = tooltip(button(icon('radix-icons:cross-2', { width: 16, height: 16 }), function() {
|
||||
buffer.option = undefined;
|
||||
delete buffer.option;
|
||||
path.value = "";
|
||||
path.remove();
|
||||
this.replaceWith(edit);
|
||||
}, 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Option fixe', 'right');
|
||||
|
||||
return {
|
||||
top: [ combobox(Object.values(config.trees).map(e => ({ text: e.name, value: e.name })), { defaultValue: buffer.tree, change: v => buffer.tree = v, class: { container: 'bg-light-25 dark:bg-dark-25 w-48 -m-px hover:z-10 h-[36px]' }, fill: 'cover' }), buffer.option === undefined ? edit : remove ],
|
||||
top: [ combobox(Object.values(config.trees).map(e => ({ text: e.name, value: e.name })), { defaultValue: buffer.tree, change: v => buffer.tree = v, class: { container: 'bg-light-25 dark:bg-dark-25 w-48 -m-px hover:z-10 h-[36px]' }, fill: 'cover' }), ...(buffer.option === undefined ? [edit] : [path, remove]) ],
|
||||
bottom: [ ],
|
||||
}
|
||||
}
|
||||
private editChoice(buffer: Partial<FeatureChoice>)
|
||||
{
|
||||
const availableChoices: Option<Partial<FeatureValue | FeatureList>>[] = featureChoices.filter(e => (e?.value as FeatureOption)?.category !== 'choice').map(e => { if(e) e.value = Array.isArray(e.value) ? e.value.filter(f => (f?.value as FeatureOption)?.category !== 'choice') : e.value; return e; }) as Option<Partial<FeatureValue | FeatureList>>[];
|
||||
const availableChoices: Option<Partial<FeatureValue | FeatureList | FeatureTree>>[] = featureChoices.filter(e => (e?.value as FeatureOption)?.category !== 'choice').map(e => { if(e) e.value = Array.isArray(e.value) ? e.value.filter(f => (f?.value as FeatureOption)?.category !== 'choice') : e.value; return e; }) as Option<Partial<FeatureValue | FeatureList | FeatureTree>>[];
|
||||
const addChoice = () => {
|
||||
const choice: { text: string; effects: (Partial<FeatureValue | FeatureList>)[]; } = { effects: [{ id: getID() }], text: '' };
|
||||
const choice: { text: string; effects: (Partial<FeatureValue | FeatureList | FeatureTree>)[]; } = { effects: [{ id: getID() }], text: '' };
|
||||
buffer.options ??= [];
|
||||
buffer.options.push(choice as FeatureChoice["options"][number]);
|
||||
return choice;
|
||||
};
|
||||
const addEffect = (choice: { text: string; effects: (Partial<FeatureValue | FeatureList>)[] }) => {
|
||||
const effect: (Partial<FeatureValue | FeatureList>) = { id: getID() };
|
||||
const addEffect = (choice: { text: string; effects: (Partial<FeatureValue | FeatureList | FeatureTree>)[] }) => {
|
||||
const effect: (Partial<FeatureValue | FeatureList | FeatureTree>) = { id: getID() };
|
||||
choice.effects.push(effect);
|
||||
return effect;
|
||||
};
|
||||
const renderEffect = (option: { text: string; effects: (Partial<FeatureValue | FeatureList>)[] }, effect: Partial<FeatureValue | FeatureList>) => {
|
||||
const renderEffect = (option: { text: string; effects: (Partial<FeatureValue | FeatureList | FeatureTree>)[] }, effect: Partial<FeatureValue | FeatureList | FeatureTree>) => {
|
||||
const { top: _top, bottom: _bottom } = this.editByCategory(effect as FeatureOption);
|
||||
let element = div('border border-light-30 dark:border-dark-30 col-span-2 row-span-2', [ div('flex justify-between items-stretch', [
|
||||
div('flex flex-row flex-1', [
|
||||
combobox(availableChoices, { defaultValue: FeatureEditor.match(effect as FeatureOption) as Partial<FeatureValue | FeatureList> | undefined, class: { container: 'bg-light-25 dark:bg-dark-25 w-[300px] -m-px hover:z-10 h-[36px]' }, fill: 'cover', change: (e: Partial<FeatureValue | FeatureList>) => {
|
||||
combobox(availableChoices, { defaultValue: FeatureEditor.match(effect as FeatureOption) as Partial<FeatureValue | FeatureList | FeatureTree> | undefined, class: { container: 'bg-light-25 dark:bg-dark-25 w-[300px] -m-px hover:z-10 h-[36px]' }, fill: 'cover', change: (e: Partial<FeatureValue | FeatureList | FeatureTree>) => {
|
||||
const idx = option.effects.findIndex(e => e === effect);
|
||||
option.effects[idx] = effect = { ...e, id: effect.id };
|
||||
|
||||
|
|
@ -696,7 +697,7 @@ class FeatureEditor
|
|||
|
||||
return element;
|
||||
}
|
||||
const renderOption = (option: { text: string; effects: (Partial<FeatureValue | FeatureList>)[] }, state: boolean) => {
|
||||
const renderOption = (option: { text: string; effects: (Partial<FeatureValue | FeatureList | FeatureTree>)[] }, state: boolean) => {
|
||||
const effects = div('flex flex-col -m-px flex flex-col ms-px ps-8 w-full', option.effects.map(e => renderEffect(option, e))), effectLength = text(option.effects.length);
|
||||
let _content = foldable([ effects ], [ div('flex flex-row flex-1 justify-between', [ div('flex flex-row items-center', [ input('text', { defaultValue: option.text, input: (value) => { option.text = value }, placeholder: 'Nom de l\'option', class: 'bg-light-25 dark:bg-dark-25 !-m-px hover:z-10 h-[36px] flex-shrink-1' }), span('italic ps-8 pe-2', 'Effets: '), span('font-bold', effectLength) ]), div('flex flex-row flex-shrink-1', [ tooltip(button(icon('radix-icons:plus'), () => effects.appendChild(renderEffect(option, addEffect(option))), 'px-2 -m-px hover:z-10 border border-light-35 dark:border-dark-35 hover:border-light-50 dark:hover:border-dark-50'), 'Nouvel effet', 'bottom'), , tooltip(button(icon('radix-icons:trash'), () => {
|
||||
_content.remove();
|
||||
|
|
@ -898,7 +899,7 @@ const featureChoices: Option<Partial<FeatureOption>>[] = [
|
|||
{ text: 'Esquive active', value: { category: 'value', property: 'defense/activedodge', operation: 'add', value: 1 } },
|
||||
{ text: 'Esquive passive', value: { category: 'value', property: 'defense/passivedodge', operation: 'add', value: 1 } }
|
||||
] },
|
||||
{ text: 'Arbre', value: { category: 'tree', option: 0 } },
|
||||
{ text: 'Arbre', value: { category: 'tree' } },
|
||||
{ text: 'Maitrise', value: Object.keys(masteryTexts).map(e => ({ text: `Maitrise > ${masteryTexts[e as keyof typeof masteryTexts].text}`, value: { category: 'list', action: 'add', list: 'mastery', item: e } })) },
|
||||
{ text: 'Compétences', value: [
|
||||
...ABILITIES.map((e) => ({ text: abilityTexts[e as Ability], value: { category: 'value', property: `abilities/${e}`, operation: 'add', value: 1 } })) as Option<Partial<FeatureItem>>[],
|
||||
|
|
@ -1058,29 +1059,6 @@ function textFromEffect(effect: Partial<FeatureOption>): string
|
|||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Esquive passive ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Esquive passive fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Passive dodge = interdit).' });
|
||||
default: return 'Défense inconnue.';
|
||||
}
|
||||
case 'mastery':
|
||||
switch(splited[1])
|
||||
{
|
||||
case 'strength':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Maitrise des armes (for.) ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Maitrise des armes (for.) fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise for = interdit).' });
|
||||
case 'dexterity':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Maitrise des armes (dex.) ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Maitrise des armes (dex.) fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise dex = interdit).' });
|
||||
case 'shield':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Maitrise des boucliers ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Maitrise des boucliers fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise boucliers = interdit).' });
|
||||
case 'armor':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Maitrise des armure ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Maitrise des armure fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise armure = interdit).' });
|
||||
case 'multiattack':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Attaque multiple ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Attaque multiple fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Attaque multiple = interdit).' });
|
||||
case 'magicpower':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Puissance) ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Puissance) fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise puissance = interdit).' });
|
||||
case 'magicspeed':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Rapidité) ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Rapidité) fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise rapidité = interdit).' });
|
||||
case 'magicelement':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Elements) ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Elements) fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise elements = interdit).' });
|
||||
case 'magicinstinct':
|
||||
return effect.operation === 'add' ? textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Instinct) ', positive: '+', text: '+Mod. de ' }, suffix: { truely: '.' } }) : textFromValue(effect.value, { prefix: { truely: 'Arbre de magie (Instinct) fixée à ' }, suffix: { truely: '.' }, falsely: 'Opération interdite (Maitrise instinct = interdit).' });
|
||||
default: return 'Maitrise inconnue.';
|
||||
}
|
||||
case 'bonus':
|
||||
switch(splited[1])
|
||||
{
|
||||
|
|
@ -1117,6 +1095,8 @@ function textFromEffect(effect: Partial<FeatureOption>): string
|
|||
return effect.action === 'add' ? `Maitrise du sort "${effect.item ? (config.spells[effect.item]?.name ?? 'Sort inconnu') : 'Sort inconnu'}".` : `Perte de maitrise du sort "${effect.item ? (config.passive[effect.item]?.name ?? 'Sort inconnu') : 'Sort inconnu'}".`;
|
||||
case 'sickness':
|
||||
return effect.action === 'add' ? `Maladie "${effect.item ? (config.sickness[effect.item]?.name ?? 'inconnue') : 'inconnue'}" permanente.` : `Maladie "${effect.item ? (config.sickness[effect.item]?.name ?? 'inconnue') : 'inconnue'}" supprimée.`;
|
||||
case 'mastery':
|
||||
return effect.action === 'add' ? `Maitrise de "${effect.item ? masteryTexts[effect.item as keyof typeof masteryTexts]?.text ?? 'inconnu' : 'inconnu'}".` : `Maitrise de "${effect.item ? masteryTexts[effect.item as keyof typeof masteryTexts]?.text ?? 'inconnu' : 'inconnu'}" supprimée.`;
|
||||
}
|
||||
}
|
||||
else if(effect.category === 'choice')
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import * as FloatingUI from "@floating-ui/dom";
|
||||
import { cancelPropagation, dom, svg, text, type Class, type NodeChildren, type RedrawableHTML } from "./dom.util";
|
||||
import { button } from "./components.util";
|
||||
import { cancelPropagation, dom, svg, text, type Class, type NodeChildren, type RedrawableHTML } from "./dom";
|
||||
import { button } from "./components";
|
||||
import type { Reactive } from "./reactive";
|
||||
|
||||
export interface FloatingProperties
|
||||
|
|
@ -2,7 +2,7 @@ import type { MarkdownConfig } from '@lezer/markdown';
|
|||
import { styleTags, tags } from '@lezer/highlight';
|
||||
import type { Decoration, EditorView, KeyBinding } from '@codemirror/view';
|
||||
import { EditorSelection, EditorState, SelectionRange } from '@codemirror/state';
|
||||
import { BlockDecorator } from '../editor.util';
|
||||
import { BlockDecorator } from '../editor';
|
||||
|
||||
export const callout: MarkdownConfig = {
|
||||
defineNodes: [
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { EditorView } from '@codemirror/view';
|
|||
import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete';
|
||||
import type { Element, MarkdownConfig } from '@lezer/markdown';
|
||||
import { styleTags, tags } from '@lezer/highlight';
|
||||
import { Content } from '../content.util';
|
||||
import { Content } from '../content';
|
||||
import { selectAll } from 'hast-util-select';
|
||||
|
||||
function fuzzyMatch(text: string, search: string): number {
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { Element, Root, RootContent } from "hast";
|
||||
import { dom, text, type Class, type Node } from "#shared/dom.util";
|
||||
import { dom, text, type Class, type Node } from "#shared/dom";
|
||||
import prose, { a, blockquote, tag, h1, h2, h3, h4, h5, hr, li, small, table, td, th, callout, type Prose } from "#shared/proses";
|
||||
import { heading } from "hast-util-heading";
|
||||
import { headingRank } from "hast-util-heading-rank";
|
||||
import { parseId } from "#shared/general.util";
|
||||
import { async } from "#shared/components.util";
|
||||
import { parseId } from "#shared/general";
|
||||
import { async } from "#shared/components";
|
||||
|
||||
export const defaultProses = { a, blockquote, tag, callout, h1, h2, h3, h4, h5, hr, li, small, table, td, th };
|
||||
export function renderMarkdown(markdown: Root, proses: Record<string, Prose>)
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import type { CanvasNode } from "~/types/canvas";
|
||||
import type { CanvasPreferences } from "~/types/general";
|
||||
import type { Position, Box, Direction, CanvasEditor } from "./canvas.util";
|
||||
import type { Position, Box, Direction, CanvasEditor } from "./canvas";
|
||||
|
||||
interface SnapPoint {
|
||||
pos: Position;
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
import { dom, icon, type NodeChildren, type Node, div, type Class } from "#shared/dom.util";
|
||||
import { dom, icon, type NodeChildren, type Node, div, type Class } from "#shared/dom";
|
||||
import { parseURL } from 'ufo';
|
||||
import render from "#shared/markdown.util";
|
||||
import { Canvas } from "#shared/canvas.util";
|
||||
import { Content, iconByType, type LocalContent } from "#shared/content.util";
|
||||
import { unifySlug } from "#shared/general.util";
|
||||
import { async, floater } from "./components.util";
|
||||
import render from "#shared/markdown";
|
||||
import { Canvas } from "#shared/canvas";
|
||||
import { Content, iconByType, type LocalContent } from "#shared/content";
|
||||
import { unifySlug } from "#shared/general";
|
||||
import { async, floater } from "./components";
|
||||
|
||||
|
||||
export type CustomProse = (properties: any, children: NodeChildren) => Node;
|
||||
|
|
|
|||
|
|
@ -273,7 +273,12 @@ export function reactive<T extends object>(obj: T | Proxy<T>): T | Proxy<T>
|
|||
const result = Reflect.ownKeys(target);
|
||||
track(target, SYMBOLS.ITERATE);
|
||||
return result;
|
||||
}
|
||||
},
|
||||
defineProperty: (target, property, attributes) => {
|
||||
const result = Reflect.defineProperty(target, property, attributes);
|
||||
trigger(target, SYMBOLS.ITERATE);
|
||||
return result;
|
||||
},
|
||||
}) as Proxy<T>;
|
||||
|
||||
_reactiveCache.set(obj, proxy);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { Content, type LocalContent } from "./content.util";
|
||||
import { dom, type RedrawableHTML } from "./dom.util";
|
||||
import { clamp } from "./general.util";
|
||||
import { Content, type LocalContent } from "./content";
|
||||
import { dom, type RedrawableHTML } from "./dom";
|
||||
import { clamp } from "./general";
|
||||
|
||||
export type Recursive<T> = T & {
|
||||
children?: T[];
|
||||
|
|
|
|||
Loading…
Reference in New Issue