diff --git a/CHANGELOG.md b/CHANGELOG.md index e9430ac..ecf66e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,11 @@ All notable changes to this project will be documented in this file. Dates are displayed in UTC. ## [Unreleased] +## [0.4.13 - 0.4.14](https://github.com/averrin/alpha-suit/compare/0.4.13...0.4.14) +Added: + * "Folders white list" option for the File Manager. Only these folders will be indexed. + * "On demand" mode for file indexing. Starts the process only on a window opening. + ## [0.4.12 - 0.4.13](https://github.com/averrin/alpha-suit/compare/0.4.12...0.4.13) Fixed: * File search input is hidden when there is no favs diff --git a/package.json b/package.json index c732809..dcdf288 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,7 @@ "@typhonjs-fvtt/svelte-standard": "^0.0.10", "class-transformer": "^0.5.1", "consola": "^2.15.3", - "crew-components": "averrin/crew-components", + "crew-components": "../crew-components", "daisyui": "^2.22.0", "fast-sort": "^3.2.0", "filtrex": "^3.0.0", diff --git a/src/index.js b/src/index.js index 4e12a13..120a10c 100644 --- a/src/index.js +++ b/src/index.js @@ -173,24 +173,48 @@ Hooks.once('init', async () => { }); }); +const gridTool = { + name: "alpha-grid-btn", + title: "Toggle Alpha Grid", + icon: "ic:twotone-widgets", + onClick: () => { + grid.toggle(); + }, + toggle: true, + isActive: _ => setting("show-grid"), +} + Hooks.on('renderSceneControls', (_) => { if (game?.user?.isGM) { - if (!document.querySelector(`[data-control='${tools.name}']`)) { - addTools(tools); + if (setting(SETTINGS.DEV_FEATURES) && !tools.tools.includes(gridTool)) { + tools.tools.push(...[ + gridTool, + ]) } + } else { + tools.tools = [] + if (setting(SETTINGS.DEV_FEATURES)) { + tools.tools = [ + gridTool + ] + } + } + if (tools.tools?.length > 0 && !document.querySelector(`[data-control='${tools.name}']`)) { + addTools(tools); } }); Hooks.once('ready', async () => { + helperStores() + initStores(); + grid.start(); + if (game.user.isGM) { - helperStores() - initStores(); tree.start(); browser.start(); help.start(); settings.start(); files.start(); - grid.start(); if (globalThis.game.modules.get("director")?.active) { hud.add(new DirectorWidget()); @@ -203,23 +227,8 @@ Hooks.once('ready', async () => { new NotificationsApp().render(true); logger.info(`Started! Version: ${game.modules.get("alpha-suit").data.version}`) - if (setting(SETTINGS.DEV_FEATURES)) { - tools.tools.push(...[ - { - name: "alpha-grid-btn", - title: "Toggle Alpha Grid", - icon: "ic:twotone-widgets", - onClick: () => { - grid.toggle(); - }, - toggle: true, - isActive: _ => setting("show-grid"), - }, - ]) - } - const indexMode = setting(SETTINGS.FILES_INDEX_MODE); - if (indexMode != "manual") { + if (indexMode != "manual" && indexMode != "ondemand") { const delay = indexMode == "auto" ? setting(SETTINGS.FILES_INDEX_DELAY) : 0; if (delay >= 0) { if (delay != 0) { diff --git a/src/main.scss b/src/main.scss index 4064b17..8740682 100644 --- a/src/main.scss +++ b/src/main.scss @@ -14,3 +14,8 @@ background: hsl(var(--window-background)); } } +#alpha-grid { + .window-content { + overflow: visible !important; + } +} diff --git a/src/modules/constants.js b/src/modules/constants.js index 1b107c4..1b5df6b 100644 --- a/src/modules/constants.js +++ b/src/modules/constants.js @@ -5,6 +5,7 @@ export const FLAGS = { } export const SETTINGS = { + FILES_WHITE_LIST: "files-white-list", GRID_LAYOUT: "grid-layout", FILES_FUZZY: "files-fuzzy", FILES_INDEX_MODE: "files-index-mode", diff --git a/src/modules/file_index.js b/src/modules/file_index.js index 8669bee..0e54373 100644 --- a/src/modules/file_index.js +++ b/src/modules/file_index.js @@ -29,6 +29,7 @@ export async function clearSavedIndex() { export async function startCache() { const mode = setting(SETTINGS.FILES_INDEX_MODE); + const whiteList = setting(SETTINGS.FILES_WHITE_LIST); if (mode == "persist") { if (isPremium()) { @@ -76,54 +77,65 @@ export async function startCache() { let indexLimit = setting(SETTINGS.FILES_INDEX_COUNT); let firstLevel = []; let n = 0; - for (const source of sources) { - path = source.startsWith("forge") ? "" : "."; - await picker.browse(source, path).then((res) => { - firstLevel.push(...res.dirs); - }); + if (whiteList?.length > 0) { + firstLevel = whiteList + sources = sources.filter(s => whiteList?.find(p => p.startsWith(s))); + } else { + for (const source of sources) { + path = source.startsWith("forge") ? "" : "."; + await picker.browse(source, path).then((res) => { + firstLevel.push(...res.dirs); + }); + } } for (const source of sources) { - path = source.startsWith("forge") ? "" : "."; + let pathes = [source.startsWith("forge") ? "" : "."]; + if (whiteList?.length > 0) { + pathes = whiteList.filter(p => p.startsWith(source)).map(p => p.replace(source + "/", "")); + } // await breadth({ + console.time("indexing " + source); - await depth({ - tree: path, - getChildren(node, nodeResult) { - if (get(stopFileIndex)) return []; - return picker.browse(source, node).then((res) => { - if (res.error) return []; - res.files = res.files.map(p => source + "/" + p); + for (const path of pathes) { + await depth({ + tree: path, + getChildren(node, _) { if (get(stopFileIndex)) return []; - if (setting(SETTINGS.FILES_INDEX_ONLY_ASSETS)) { - index.push(...res.files.filter((f) => isImage(f) || isVideo(f) || isSound(f))); + return picker.browse(source, node).then((res) => { + if (res.error) return []; + res.files = res.files.map(p => source + "/" + p); + if (get(stopFileIndex)) return []; + if (setting(SETTINGS.FILES_INDEX_ONLY_ASSETS)) { + index.push(...res.files.filter((f) => isImage(f) || isVideo(f) || isSound(f))); + } else { + index.push(...res.files); + } + fileIndex.set(index); + return res.dirs; + }).catch(_ => []); + }, + leave(node) { + if (firstLevel.includes(node)) { + const per = Math.round((n / firstLevel.length) * 100); + indexPercents.set(per) + n++; + } + return new Promise((r) => r(node)); + }, + filter(node) { + if (get(stopFileIndex)) return false; + if (index.length >= indexLimit) return false; + const isGood = node.split("/").length < depthLimit && !excludedFolders.some((p) => node.match(new RegExp(p))); + if (isGood) { + indexPath.set(`${source}/${node}`); } else { - index.push(...res.files); } - fileIndex.set(index); - return res.dirs; - }).catch(_ => []); - }, - leave(node) { - if (firstLevel.includes(node)) { - const per = Math.round((n / firstLevel.length) * 100); - indexPercents.set(per) - n++; - } - return new Promise((r) => r(node)); - }, - filter(node) { - if (get(stopFileIndex)) return false; - if (index.length >= indexLimit) return false; - const isGood = node.split("/").length < depthLimit && !excludedFolders.some((p) => node.match(new RegExp(p))); - if (isGood) { - indexPath.set(`${source}/${node}`); - } else { - } - return isGood; - }, - }); - // n = 1; - console.timeEnd("Alpha | Indexing " + source); + return isGood; + }, + }); + // n = 1; + console.timeEnd("Alpha | Indexing " + source); + } } logger.info(`Indexed: ${index.length} files. Depth: ${depthLimit}`); console.timeEnd("indexing"); diff --git a/src/modules/settings.js b/src/modules/settings.js index 7733cf2..49160e4 100644 --- a/src/modules/settings.js +++ b/src/modules/settings.js @@ -266,6 +266,15 @@ export function initSettings(app) { type: Number, }); + game.settings.register(moduleId, SETTINGS.FILES_WHITE_LIST, { + name: 'Folders white list', + hint: "List of folders for indexing. Do not forget specify a storage. For example: 'data/modules/jb2a_patreon/Library' instead of 'modules/jb2a_patreon/Library'", + scope: "world", + config: false, + default: [], + type: Array, + }); + game.settings.register(moduleId, SETTINGS.FILES_EXCLUDE_SOURCES, { name: 'Excluded storages', hint: "E.g. you can exclude 'Data' to prevent 'modules' folders indexing", @@ -384,8 +393,9 @@ export function initSettings(app) { 'auto': "Automatic", 'persist': "Stored index", 'manual': "Manual", + 'ondemand': "On demand", }, - default: "auto", + default: "ondemand", type: String, }); diff --git a/src/modules/stores.js b/src/modules/stores.js index 3326c8c..f8bba41 100644 --- a/src/modules/stores.js +++ b/src/modules/stores.js @@ -11,7 +11,7 @@ import { tick } from "svelte"; import { moduleId, SETTINGS, infoColor } from "./constants.js"; import initHelpers from "crew-components/helpers"; -import { userSettingStore } from 'crew-components/stores'; +import { userFlagStore } from 'crew-components/stores'; initHelpers(moduleId, infoColor, SETTINGS); let tagSource = moduleId; @@ -263,12 +263,7 @@ function initDropHandler() { export let gridLayout = writable([]); export function initStores() { - gridLayout = userSettingStore(SETTINGS.GRID_LAYOUT, { - scope: "world", - config: false, - default: [], - type: Array, - }) + gridLayout = userFlagStore(SETTINGS.GRID_LAYOUT, []) initDropHandler() diff --git a/src/view/FilesUI.svelte b/src/view/FilesUI.svelte index 8482bc0..5fc423a 100644 --- a/src/view/FilesUI.svelte +++ b/src/view/FilesUI.svelte @@ -29,6 +29,11 @@ const position = application.position; const { height } = position.stores; + const indexMode = setting(SETTINGS.FILES_INDEX_MODE); + if (indexMode == "ondemand" && $fileIndex.length == 0) { + tick().then(rebuildIndex); + } + export let elementRoot; let topic; let nameFilter = ""; diff --git a/src/view/GridUI.svelte b/src/view/GridUI.svelte index afe40d7..6767817 100644 --- a/src/view/GridUI.svelte +++ b/src/view/GridUI.svelte @@ -9,6 +9,8 @@ import DocumentThumb from "./components/DocumentThumb.svelte"; import ActiveEffectThumb from "./components/ActiveEffectThumb.svelte"; import RemoveButton from "crew-components/RemoveButton"; + import ArgInput from "crew-components/ArgInput"; + import EditWidgetDialog from "./EditWidgetDialog.svelte"; import { gridLayout } from "../modules/stores.js"; import { getContext, onDestroy, tick } from "svelte"; const { application } = getContext("external"); @@ -23,8 +25,16 @@ import Grid from "svelte-grid"; import gridHelp from "svelte-grid/build/helper/index.mjs"; import FileThumb from "./components/FileThumb.svelte"; + import { notify } from "../modules/notify"; + import CreateApplication from "crew-components/AlphaApplication"; let entities = []; + let gap = 4; + let rowHeight = 52; + let hm = 1; + let COLS = 6; + let ROWS = $height / (rowHeight * hm); + let cols = [[1200, COLS]]; let items = []; let layoutId = uuidv4(); @@ -32,7 +42,7 @@ if ($gridLayout.length > 0) { layoutId = $gridLayout[0].layoutId; } else { - serializeItems(); + serializeItems(true); } tick().then(async (_) => { items = await deserializeItems(); @@ -40,13 +50,6 @@ }); let locked = true; - let gap = 4; - let rowHeight = 52; - let hm = 1; - let COLS = 6; - let ROWS = $height / (rowHeight * hm); - let cols = [[1200, COLS]]; - function _resizeItems() { const old_items = [...items]; items = []; @@ -75,6 +78,7 @@ let layout = l?.layout ?? []; layoutId = l?.layoutId ?? layoutId; layoutName = l?.name ?? layoutName; + rowHeight = l?.options?.rowHeight ?? rowHeight; for (const i of layout) { let source; if (i.uuid) { @@ -86,8 +90,8 @@ }; } else if (i.type == "File") { source = { - id: i.id, name: i.id.split("/")[i.id.split("/").length - 1], + ...i, }; } else if (i.type == "ActiveEffect") { source = game.dfreds?.effects?.all.find((e) => e._id == i.id); @@ -113,9 +117,18 @@ // logger.info(JSON.stringify(layout)); // logger.info(layout); gridLayout.update((gl) => { - const l = gl.find((l) => l.layoutId == layoutId); + let l = gl.find((l) => l.layoutId == layoutId); + if (!l) { + layoutId = uuidv4(); + l = { layoutId }; + gl.push(l); + } l.layout = layout; l.name = layoutName; + if (!l.options) { + l.options = {}; + } + l.options.rowHeight = rowHeight; return gl; }); } @@ -200,6 +213,7 @@ async function dropHandler(event) { const data = TextEditor.getDragEventData(event); + logger.info(data); let w, h = 1; let entity; @@ -212,7 +226,7 @@ entity.documentName = "ActiveEffect"; entity.id = entity._id; if (entities.find((e) => e?.id == data?.id)) return; - } else { + } else if (data.uuid) { if (entities.find((e) => e.uuid == data.uuid)) return; entity = await fromUuid(data.uuid); @@ -220,6 +234,10 @@ w = 4; } } + if (!entity) { + logger.error("Drop item is unknown", data); + return; + } logger.info(entity); entities = [...entities, entity]; addItem( @@ -267,9 +285,7 @@ function reset() { entities = []; items = []; - gridLayout.update((gl) => { - return gl.filter((l) => l.layoutId != layoutId); - }); + serializeItems(true); } function removeItem(dataItem) { @@ -288,73 +304,108 @@ return gl; }); } + + function editWidget() { + const dialogClass = CreateApplication("edit-widget", "Edit widget", EditWidgetDialog, 600,600, true); + const dialog = new dialogClass(); + dialog.start(); + dialog.show(); + } -
- - - - { - items = await deserializeItems(); - resizeItems(); - }} - size="xs" - /> - { - const text = ` This is a text!`; - addItem( - { - type: "Text", - id: uuidv4(), - text: text, - persist: text, - }, - 0, - 0, - 2, - 1 - ); +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + - serializeItems(); - }} - size="xs" - /> + + + + - + {#if !locked} +
+ { + serializeItems(); + }} + /> + + + + + + + + + + + + + +
+ {/if}
-
+
Drop Item / Actor / File / Macros here
OR
- Add widget
{/if} diff --git a/src/view/settings/FilesSearchSettings.svelte b/src/view/settings/FilesSearchSettings.svelte index e2e4b41..241916e 100644 --- a/src/view/settings/FilesSearchSettings.svelte +++ b/src/view/settings/FilesSearchSettings.svelte @@ -77,6 +77,8 @@ + +