diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..7d74fe2 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,9 @@ +# Package Managers +package-lock.json +pnpm-lock.yaml +yarn.lock +bun.lock +bun.lockb + +# Miscellaneous +/static/ diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..3f7802c --- /dev/null +++ b/.prettierrc @@ -0,0 +1,15 @@ +{ + "useTabs": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "plugins": ["prettier-plugin-svelte"], + "overrides": [ + { + "files": "*.svelte", + "options": { + "parser": "svelte" + } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 28d1e67..38f928a 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["svelte.svelte-vscode"] + "recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode"] } diff --git a/AGENTS.md b/AGENTS.md index 3a524df..2140b9b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1 +1 @@ -- Non modificare mai i file direttamente, proponi la soluzione nella chat. \ No newline at end of file +- Puoi modificare direttamente i tag e lo stile di componenti e pagine. diff --git a/package-lock.json b/package-lock.json index fe7167f..68f2455 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,6 +21,8 @@ "better-auth": "~1.4.21", "drizzle-kit": "^0.31.8", "drizzle-orm": "^0.45.1", + "prettier": "^3.8.1", + "prettier-plugin-svelte": "^3.4.1", "svelte": "^5.51.0", "svelte-check": "^4.4.2", "typescript": "^5.9.3", @@ -4658,6 +4660,17 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/prettier-plugin-svelte": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/prettier-plugin-svelte/-/prettier-plugin-svelte-3.5.1.tgz", + "integrity": "sha512-65+fr5+cgIKWKiqM1Doum4uX6bY8iFCdztvvp2RcF+AJoieaw9kJOFMNcJo/bkmKYsxFaM9OsVZK/gWauG/5mg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "prettier": "^3.0.0", + "svelte": "^3.2.0 || ^4.0.0-next.0 || ^5.0.0-next.0" + } + }, "node_modules/process-warning": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", diff --git a/package.json b/package.json index 9f84605..9dcc865 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,9 @@ "db:generate": "drizzle-kit generate", "db:migrate": "drizzle-kit migrate", "db:studio": "drizzle-kit studio", - "auth:schema": "better-auth generate --config src/lib/server/auth.ts --output src/lib/server/db/auth.schema.ts --yes" + "auth:schema": "better-auth generate --config src/lib/server/auth.ts --output src/lib/server/db/auth.schema.ts --yes", + "lint": "prettier --check .", + "format": "prettier --write ." }, "devDependencies": { "@better-auth/cli": "~1.4.21", @@ -26,6 +28,8 @@ "better-auth": "~1.4.21", "drizzle-kit": "^0.31.8", "drizzle-orm": "^0.45.1", + "prettier": "^3.8.1", + "prettier-plugin-svelte": "^3.4.1", "svelte": "^5.51.0", "svelte-check": "^4.4.2", "typescript": "^5.9.3", diff --git a/src/app.d.ts b/src/app.d.ts index 87f8dbd..2ae6f13 100644 --- a/src/app.d.ts +++ b/src/app.d.ts @@ -4,7 +4,10 @@ import type { User, Session } from 'better-auth/minimal'; // for information about these interfaces declare global { namespace App { - interface Locals { user?: User; session?: Session } + interface Locals { + user?: User; + session?: Session; + } // interface Error {} // interface PageData {} diff --git a/src/app.html b/src/app.html index f273cc5..19689b3 100644 --- a/src/app.html +++ b/src/app.html @@ -3,6 +3,12 @@ + + + %sveltekit.head% diff --git a/src/hooks.server.ts b/src/hooks.server.ts index 85a772e..2c6220d 100644 --- a/src/hooks.server.ts +++ b/src/hooks.server.ts @@ -4,8 +4,6 @@ import { auth } from '$lib/server/auth'; import { svelteKitHandler } from 'better-auth/svelte-kit'; import { sequence } from '@sveltejs/kit/hooks'; - - const handleBetterAuth: Handle = async ({ event, resolve }) => { const session = await auth.api.getSession({ headers: event.request.headers }); @@ -29,6 +27,6 @@ const handleRouting: Handle = async ({ event, resolve }) => { } return resolve(event); -} +}; export const handle: Handle = sequence(handleBetterAuth, handleRouting); diff --git a/src/lib/assets/composer/palette.svg b/src/lib/assets/composer/palette.svg new file mode 100644 index 0000000..77a1f56 --- /dev/null +++ b/src/lib/assets/composer/palette.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/src/lib/components/Note.svelte b/src/lib/components/Note.svelte deleted file mode 100644 index df9fff7..0000000 --- a/src/lib/components/Note.svelte +++ /dev/null @@ -1,7 +0,0 @@ - - - \ No newline at end of file diff --git a/src/lib/components/NoteEditor.svelte b/src/lib/components/NoteEditor.svelte deleted file mode 100644 index f53080b..0000000 --- a/src/lib/components/NoteEditor.svelte +++ /dev/null @@ -1,34 +0,0 @@ - \ No newline at end of file diff --git a/src/lib/components/NoteNavbar.svelte b/src/lib/components/NoteNavbar.svelte deleted file mode 100644 index 9ec367a..0000000 --- a/src/lib/components/NoteNavbar.svelte +++ /dev/null @@ -1,31 +0,0 @@ - - - { - if (e.key === "n" && e.metaKey) { - expanded = true; - return; - } - if( e.key === "Escape") { - expanded = false; - return; - } - }} -/> - -{#if !expanded} -
Scrivi una nota...
-{:else} -
- -
-{/if} \ No newline at end of file diff --git a/src/lib/components/board/BoardCanvas.svelte b/src/lib/components/board/BoardCanvas.svelte new file mode 100644 index 0000000..d91a1d1 --- /dev/null +++ b/src/lib/components/board/BoardCanvas.svelte @@ -0,0 +1,61 @@ + + +
+ {#each notes as note (note.id)} +
{ + event.stopPropagation(); + onStartDrag(event, note.id); + }} + > + +
+ {/each} +
+ + diff --git a/src/lib/components/board/Note.svelte b/src/lib/components/board/Note.svelte new file mode 100644 index 0000000..68f66bf --- /dev/null +++ b/src/lib/components/board/Note.svelte @@ -0,0 +1,65 @@ + + +
+ {#if note.title.trim().length > 0} +

{note.title}

+ {/if} + + {#if note.content.trim().length > 0} +

{note.content}

+ {:else} +

Nota senza contenuto

+ {/if} + +
{note.date.toLocaleString('it-IT')}
+
+ + diff --git a/src/lib/components/board/script/constants.ts b/src/lib/components/board/script/constants.ts new file mode 100644 index 0000000..eaddb6f --- /dev/null +++ b/src/lib/components/board/script/constants.ts @@ -0,0 +1,9 @@ +export const BOARD_WIDTH = 5400; +export const BOARD_HEIGHT = 5400; + +export const BOARD_GRID_SIZE = 16; + +export const NOTE_CARD_WIDTH = 280; +export const NOTE_CARD_MIN_HEIGHT = 140; + +export const MINIMAP_WIDTH = 220; diff --git a/src/lib/components/board/script/types.ts b/src/lib/components/board/script/types.ts new file mode 100644 index 0000000..d7bfe59 --- /dev/null +++ b/src/lib/components/board/script/types.ts @@ -0,0 +1,31 @@ +import type { Note } from '$lib/components/editor/script/editor'; + +export type BoardNote = Note & { + x: number; + y: number; + z: number; +}; + +export type DragState = + | { + id: string; + offsetX: number; + offsetY: number; + } + | null; + +export type PanState = + | { + startX: number; + startY: number; + scrollLeft: number; + scrollTop: number; + } + | null; + +export type ViewportState = { + scrollLeft: number; + scrollTop: number; + width: number; + height: number; +}; diff --git a/src/lib/components/board/script/utils.ts b/src/lib/components/board/script/utils.ts new file mode 100644 index 0000000..99267f8 --- /dev/null +++ b/src/lib/components/board/script/utils.ts @@ -0,0 +1,18 @@ +import { + BOARD_HEIGHT, + BOARD_WIDTH, + NOTE_CARD_MIN_HEIGHT, + NOTE_CARD_WIDTH +} from '$lib/components/board/script/constants'; + +export const clamp = (value: number, min: number, max: number) => + Math.min(max, Math.max(min, value)); + +export const getRandomPosition = (idx: number) => ({ + x: 200 + ((idx * 420) % (BOARD_WIDTH - NOTE_CARD_WIDTH - 260)), + y: 140 + Math.floor(idx / 8) * 260 +}); + +export const clampNoteX = (x: number) => clamp(x, 0, BOARD_WIDTH - NOTE_CARD_WIDTH); + +export const clampNoteY = (y: number) => clamp(y, 0, BOARD_HEIGHT - NOTE_CARD_MIN_HEIGHT); diff --git a/src/lib/components/editor/ColorSelector.svelte b/src/lib/components/editor/ColorSelector.svelte new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/components/editor/NoteEditor.svelte b/src/lib/components/editor/NoteEditor.svelte new file mode 100644 index 0000000..f896292 --- /dev/null +++ b/src/lib/components/editor/NoteEditor.svelte @@ -0,0 +1,193 @@ + + + + +{#if note} +
+ + + + +
+
+ + + +
+ +
+ +
+
+
+{/if} + + diff --git a/src/lib/components/editor/script/types.ts b/src/lib/components/editor/script/types.ts new file mode 100644 index 0000000..cada5ab --- /dev/null +++ b/src/lib/components/editor/script/types.ts @@ -0,0 +1,23 @@ +export type Note = { + id: string; + + title: string; + content: string; + color: string; + date: Date; + + images: ImageAttachment[]; + files: FileAttachment[]; +}; + +export type ImageAttachment = NoteAttachment & { kind: 'image' }; +export type FileAttachment = NoteAttachment & { kind: 'file' }; + +interface NoteAttachment { + id: string; + + name: string; + size: number; + mimeType: string; + previewUrl?: string; +} diff --git a/src/lib/components/editor/script/utils.ts b/src/lib/components/editor/script/utils.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/lib/components/ui/Minimap.svelte b/src/lib/components/ui/Minimap.svelte new file mode 100644 index 0000000..a9ac2ad --- /dev/null +++ b/src/lib/components/ui/Minimap.svelte @@ -0,0 +1,112 @@ + + + + + diff --git a/src/lib/components/ui/Navbar.svelte b/src/lib/components/ui/Navbar.svelte new file mode 100644 index 0000000..17f03b6 --- /dev/null +++ b/src/lib/components/ui/Navbar.svelte @@ -0,0 +1,62 @@ + + + { + if (e.key === 'Escape') { + expanded = false; + return; + } + }} +/> + +{#if !expanded} + +{:else} +
+ (expanded = false)} /> +
+{/if} + + diff --git a/src/lib/editor.ts b/src/lib/editor.ts deleted file mode 100644 index 2f585e9..0000000 --- a/src/lib/editor.ts +++ /dev/null @@ -1,25 +0,0 @@ -export type Note = { - id: string; - - title: string; - content: string; - color: string; - date: Date; - - images: ImageAttachment[]; - files: FileAttachment[]; -}; - - -export type ImageAttachment = NoteAttachment & { kind: "image" }; -export type FileAttachment = NoteAttachment & { kind: "file" }; - -interface NoteAttachment { - id: string; - - name: string; - size: number; - mimeType: string; - previewUrl?: string; -} - diff --git a/src/lib/media.ts b/src/lib/media.ts deleted file mode 100644 index 98e048c..0000000 --- a/src/lib/media.ts +++ /dev/null @@ -1 +0,0 @@ -export type MediaProvider = 'youtube' | 'youtube-music' | 'spotify'; diff --git a/src/lib/server/db/auth.schema.ts b/src/lib/server/db/auth.schema.ts index 66fc633..4fb0524 100644 --- a/src/lib/server/db/auth.schema.ts +++ b/src/lib/server/db/auth.schema.ts @@ -1,107 +1,105 @@ -import { relations, sql } from "drizzle-orm"; -import { sqliteTable, text, integer, index } from "drizzle-orm/sqlite-core"; +import { relations, sql } from 'drizzle-orm'; +import { sqliteTable, text, integer, index } from 'drizzle-orm/sqlite-core'; -export const user = sqliteTable("user", { - id: text("id").primaryKey(), - name: text("name").notNull(), - email: text("email").notNull().unique(), - emailVerified: integer("email_verified", { mode: "boolean" }) - .default(false) - .notNull(), - image: text("image"), - createdAt: integer("created_at", { mode: "timestamp_ms" }) - .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) - .notNull(), - updatedAt: integer("updated_at", { mode: "timestamp_ms" }) - .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) - .$onUpdate(() => /* @__PURE__ */ new Date()) - .notNull(), +export const user = sqliteTable('user', { + id: text('id').primaryKey(), + name: text('name').notNull(), + email: text('email').notNull().unique(), + emailVerified: integer('email_verified', { mode: 'boolean' }).default(false).notNull(), + image: text('image'), + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull() }); export const session = sqliteTable( - "session", - { - id: text("id").primaryKey(), - expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), - token: text("token").notNull().unique(), - createdAt: integer("created_at", { mode: "timestamp_ms" }) - .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) - .notNull(), - updatedAt: integer("updated_at", { mode: "timestamp_ms" }) - .$onUpdate(() => /* @__PURE__ */ new Date()) - .notNull(), - ipAddress: text("ip_address"), - userAgent: text("user_agent"), - userId: text("user_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), - }, - (table) => [index("session_userId_idx").on(table.userId)], + 'session', + { + id: text('id').primaryKey(), + expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(), + token: text('token').notNull().unique(), + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp_ms' }) + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull(), + ipAddress: text('ip_address'), + userAgent: text('user_agent'), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }) + }, + (table) => [index('session_userId_idx').on(table.userId)] ); export const account = sqliteTable( - "account", - { - id: text("id").primaryKey(), - accountId: text("account_id").notNull(), - providerId: text("provider_id").notNull(), - userId: text("user_id") - .notNull() - .references(() => user.id, { onDelete: "cascade" }), - accessToken: text("access_token"), - refreshToken: text("refresh_token"), - idToken: text("id_token"), - accessTokenExpiresAt: integer("access_token_expires_at", { - mode: "timestamp_ms", - }), - refreshTokenExpiresAt: integer("refresh_token_expires_at", { - mode: "timestamp_ms", - }), - scope: text("scope"), - password: text("password"), - createdAt: integer("created_at", { mode: "timestamp_ms" }) - .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) - .notNull(), - updatedAt: integer("updated_at", { mode: "timestamp_ms" }) - .$onUpdate(() => /* @__PURE__ */ new Date()) - .notNull(), - }, - (table) => [index("account_userId_idx").on(table.userId)], + 'account', + { + id: text('id').primaryKey(), + accountId: text('account_id').notNull(), + providerId: text('provider_id').notNull(), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }), + accessToken: text('access_token'), + refreshToken: text('refresh_token'), + idToken: text('id_token'), + accessTokenExpiresAt: integer('access_token_expires_at', { + mode: 'timestamp_ms' + }), + refreshTokenExpiresAt: integer('refresh_token_expires_at', { + mode: 'timestamp_ms' + }), + scope: text('scope'), + password: text('password'), + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp_ms' }) + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull() + }, + (table) => [index('account_userId_idx').on(table.userId)] ); export const verification = sqliteTable( - "verification", - { - id: text("id").primaryKey(), - identifier: text("identifier").notNull(), - value: text("value").notNull(), - expiresAt: integer("expires_at", { mode: "timestamp_ms" }).notNull(), - createdAt: integer("created_at", { mode: "timestamp_ms" }) - .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) - .notNull(), - updatedAt: integer("updated_at", { mode: "timestamp_ms" }) - .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) - .$onUpdate(() => /* @__PURE__ */ new Date()) - .notNull(), - }, - (table) => [index("verification_identifier_idx").on(table.identifier)], + 'verification', + { + id: text('id').primaryKey(), + identifier: text('identifier').notNull(), + value: text('value').notNull(), + expiresAt: integer('expires_at', { mode: 'timestamp_ms' }).notNull(), + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .notNull(), + updatedAt: integer('updated_at', { mode: 'timestamp_ms' }) + .default(sql`(cast(unixepoch('subsecond') * 1000 as integer))`) + .$onUpdate(() => /* @__PURE__ */ new Date()) + .notNull() + }, + (table) => [index('verification_identifier_idx').on(table.identifier)] ); export const userRelations = relations(user, ({ many }) => ({ - sessions: many(session), - accounts: many(account), + sessions: many(session), + accounts: many(account) })); export const sessionRelations = relations(session, ({ one }) => ({ - user: one(user, { - fields: [session.userId], - references: [user.id], - }), + user: one(user, { + fields: [session.userId], + references: [user.id] + }) })); export const accountRelations = relations(account, ({ one }) => ({ - user: one(user, { - fields: [account.userId], - references: [user.id], - }), + user: one(user, { + fields: [account.userId], + references: [user.id] + }) })); diff --git a/src/lib/server/db/schema.ts b/src/lib/server/db/schema.ts index 6a9d741..b5b75ad 100644 --- a/src/lib/server/db/schema.ts +++ b/src/lib/server/db/schema.ts @@ -1,34 +1,44 @@ import { integer, real, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { user } from './auth.schema'; -export * from './auth.schema'; +export * from './auth.schema'; export const notes = sqliteTable('notes', { id: text('id').primaryKey(), - userId: text('user_id').notNull().references(() => user.id, { onDelete: 'cascade' }), + userId: text('user_id') + .notNull() + .references(() => user.id, { onDelete: 'cascade' }), title: text('title').notNull(), content: text('text').notNull(), - x: real("x").default(0).notNull(), - y: real("y").default(0).notNull(), - z: integer("z").default(1).notNull(), + x: real('x').default(0).notNull(), + y: real('y').default(0).notNull(), + z: integer('z').default(1).notNull(), - color: text("color").default("#ffffff").notNull(), - createdAt: integer("created_at", { mode: "timestamp_ms" }).notNull().$defaultFn(() => new Date()), + color: text('color').default('#ffffff').notNull(), + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()), updatedAt: integer('updated_at', { mode: 'timestamp_ms' }) }); export const noteFiles = sqliteTable('note_files', { id: text('id').primaryKey(), - noteId: text('note_id').notNull().references(() => notes.id, { onDelete: 'cascade' }), + noteId: text('note_id') + .notNull() + .references(() => notes.id, { onDelete: 'cascade' }), - kind: text('kind', { enum: ['image', 'file'] }).notNull().default('file'), + kind: text('kind', { enum: ['image', 'file'] }) + .notNull() + .default('file'), position: integer('position').notNull().default(0), name: text('name').notNull(), size: integer('size').notNull(), mimeType: text('mime_type').notNull(), - createdAt: integer('created_at', { mode: 'timestamp_ms' }).notNull().$defaultFn(() => new Date()) + createdAt: integer('created_at', { mode: 'timestamp_ms' }) + .notNull() + .$defaultFn(() => new Date()) }); diff --git a/src/lib/server/imap.ts b/src/lib/server/imap.ts index 4649322..f1d5976 100644 --- a/src/lib/server/imap.ts +++ b/src/lib/server/imap.ts @@ -50,7 +50,9 @@ export const imapAuth = () => ({ const adapter = ctx.context.internalAdapter; const existingUser = await adapter.findUserByEmail(email, { includeAccounts: true }); - const credentialAccount = existingUser?.accounts.find((account) => account.providerId === 'credential'); + const credentialAccount = existingUser?.accounts.find( + (account) => account.providerId === 'credential' + ); const credentialPassword = credentialAccount?.password; const isDbPasswordValid = typeof credentialPassword === 'string' && diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 9be146c..bb623fe 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -9,3 +9,9 @@ {@render children()} + + \ No newline at end of file diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index cc4c245..8ad7bda 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,7 +1,183 @@ + { + moveDrag(event); + movePan(event); + }} + onpointerup={() => { + stopDrag(); + stopPan(); + }} + onpointercancel={() => { + stopDrag(); + stopPan(); + }} +/> + +
+
+
+ +
+
+ + + + +
+ \ No newline at end of file + :global(body) { + margin: 0; + } + + .board-viewport { + height: 100vh; + overflow: auto; + background: + radial-gradient(circle at top right, #f8fbff, #eef2f7 45%, #e6ebf2 100%); + scrollbar-width: none; + -ms-overflow-style: none; + cursor: grab; + } + + .board-viewport:active { + cursor: grabbing; + } + + .board-viewport::-webkit-scrollbar { + display: none; + } + + .top-composer-shell { + position: fixed; + top: 0; + left: 0; + right: 0; + z-index: 80; + padding: 16px 14px 0; + pointer-events: none; + background: transparent; + } + + .top-composer { + max-width: 720px; + margin: 0 auto; + pointer-events: auto; + } + diff --git a/svelte.config.js b/svelte.config.js index 162e413..7bb618f 100644 --- a/svelte.config.js +++ b/svelte.config.js @@ -6,7 +6,8 @@ const config = { adapter: adapter() }, vitePlugin: { - dynamicCompileOptions: ({ filename }) => filename.includes('node_modules') ? undefined : { runes: true } + dynamicCompileOptions: ({ filename }) => + filename.includes('node_modules') ? undefined : { runes: true } } };