@musnows/scriverse 0.3.11 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +2 -0
- package/README.md +2 -0
- package/dist/ai.js +292 -19
- package/dist/ai.js.map +1 -1
- package/dist/app.js +45 -4
- package/dist/app.js.map +1 -1
- package/dist/database.js +47 -0
- package/dist/database.js.map +1 -1
- package/dist/public/app.js +826 -173
- package/dist/public/character-markdown.js +12 -0
- package/dist/public/index.html +57 -26
- package/dist/public/model-config.d.ts +1 -0
- package/dist/public/model-config.js +12 -3
- package/dist/public/module-layout.d.ts +17 -0
- package/dist/public/module-layout.js +28 -0
- package/dist/public/page-route.js +1 -1
- package/dist/public/styles.css +183 -60
- package/dist/public/vendor/vditor/dist/css/content-theme/ant-design.css +210 -0
- package/dist/public/vendor/vditor/dist/css/content-theme/dark.css +123 -0
- package/dist/public/vendor/vditor/dist/css/content-theme/light.css +101 -0
- package/dist/public/vendor/vditor/dist/css/content-theme/wechat.css +94 -0
- package/dist/public/vendor/vditor/dist/images/emoji/b3log.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/chainbook.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/doge.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/hacpai.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/huaji.gif +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/latke.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/liandi.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/lute.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/octocat.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/pipe.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/siyuan.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/solo.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/sym.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/trollface.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/vditor.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/wide.png +0 -0
- package/dist/public/vendor/vditor/dist/images/emoji/wulian.png +0 -0
- package/dist/public/vendor/vditor/dist/images/img-loading.svg +1 -0
- package/dist/public/vendor/vditor/dist/images/logo.png +0 -0
- package/dist/public/vendor/vditor/dist/index.css +1823 -0
- package/dist/public/vendor/vditor/dist/index.d.ts +82 -0
- package/dist/public/vendor/vditor/dist/index.js +15995 -0
- package/dist/public/vendor/vditor/dist/index.min.js +1 -0
- package/dist/public/vendor/vditor/dist/js/abcjs/abcjs_basic.min.js +3 -0
- package/dist/public/vendor/vditor/dist/js/echarts/echarts.min.js +45 -0
- package/dist/public/vendor/vditor/dist/js/flowchart.js/flowchart.min.js +8 -0
- package/dist/public/vendor/vditor/dist/js/graphviz/full.render.js +90 -0
- package/dist/public/vendor/vditor/dist/js/graphviz/viz.js +333 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/LICENSE +29 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/highlight.min.js +3835 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/a11y-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/a11y-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/agate.min.css +20 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/an-old-hope.min.css +9 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/androidstudio.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/ant-design.min.css +84 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/arduino-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/arta.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/ascetic.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/atom-one-dark-reasonable.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/atom-one-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/atom-one-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/3024.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/apathy.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/apprentice.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ashes.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-cave-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-cave.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-dune-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-dune.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-estuary-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-estuary.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-forest-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-forest.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-heath-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-heath.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-lakeside-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-lakeside.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-plateau-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-plateau.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-savanna-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-savanna.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-seaside-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-seaside.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-sulphurpool-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atelier-sulphurpool.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/atlas.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/bespin.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-bathory.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-burzum.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-dark-funeral.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-gorgoroth.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-immortal.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-khold.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-marduk.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-mayhem.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-nile.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal-venom.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/black-metal.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/brewer.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/bright.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/brogrammer.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/brush-trees-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/brush-trees.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/chalk.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/circus.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/classic-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/classic-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/codeschool.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/colors.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/cupcake.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/cupertino.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/danqing.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/darcula.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/dark-violet.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/darkmoss.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/darktooth.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/decaf.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/default-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/default-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/dirtysea.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/dracula.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/edge-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/edge-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/eighties.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/embers.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/equilibrium-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/equilibrium-gray-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/equilibrium-gray-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/equilibrium-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/espresso.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/eva-dim.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/eva.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/flat.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/framer.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/fruit-soda.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gigavolt.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/github.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/google-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/google-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/grayscale-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/grayscale-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/green-screen.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-dark-hard.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-dark-medium.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-dark-pale.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-dark-soft.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-light-hard.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-light-medium.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/gruvbox-light-soft.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/hardcore.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/harmonic16-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/harmonic16-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/heetch-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/heetch-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/helios.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/hopscotch.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/horizon-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/horizon-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/humanoid-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/humanoid-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ia-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ia-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/icy-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ir-black.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/isotope.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/kimber.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/london-tube.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/macintosh.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/marrakesh.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/materia.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/material-darker.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/material-lighter.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/material-palenight.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/material-vivid.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/material.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/mellow-purple.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/mexico-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/mocha.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/monokai.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/nebula.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/nord.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/nova.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ocean.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/oceanicnext.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/one-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/onedark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/outrun-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/papercolor-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/papercolor-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/paraiso.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/pasque.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/phd.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/pico.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/pop.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/porple.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/qualia.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/railscasts.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/rebecca.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ros-pine-dawn.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ros-pine-moon.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/ros-pine.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/sagelight.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/sandcastle.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/seti-ui.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/shapeshifter.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/silk-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/silk-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/snazzy.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/solar-flare-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/solar-flare.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/solarized-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/solarized-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/spacemacs.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/summercamp.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/summerfruit-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/summerfruit-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/synth-midnight-terminal-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/synth-midnight-terminal-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/tango.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/tender.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/tomorrow-night.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/tomorrow.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/twilight.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/unikitty-dark.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/unikitty-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/vulcan.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-10-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-10.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-95-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-95.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-high-contrast-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-high-contrast.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-nt-light.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/windows-nt.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/woodland.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/xcode-dusk.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/base16/zenburn.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/brown-paper.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/brown-papersq.png +0 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/codepen-embed.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/color-brewer.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/default.min.css +9 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/devibeans.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/docco.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/far.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/felipec.min.css +7 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/foundation.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/github-dark-dimmed.min.css +9 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/github-dark.min.css +10 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/github.min.css +10 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/gml.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/googlecode.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/gradient-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/gradient-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/grayscale.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/hybrid.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/idea.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/intellij-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/ir-black.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/isbl-editor-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/isbl-editor-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/kimbie-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/kimbie-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/lightfair.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/lioshi.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/magula.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/mono-blue.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/monokai-sublime.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/monokai.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/night-owl.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/nnfx-dark.min.css +10 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/nnfx-light.min.css +10 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/nord.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/obsidian.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/panda-syntax-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/panda-syntax-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/paraiso-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/paraiso-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/pojoaque.jpg +0 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/pojoaque.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/purebasic.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/qtcreator-dark.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/qtcreator-light.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/rainbow.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/routeros.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/school-book.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/shades-of-purple.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/srcery.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/stackoverflow-dark.min.css +13 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/stackoverflow-light.min.css +13 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/sunburst.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/tokyo-night-dark.min.css +8 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/tokyo-night-light.min.css +8 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/tomorrow-night-blue.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/tomorrow-night-bright.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/vs.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/vs2015.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/xcode.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/styles/xt256.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/highlight.js/third-languages.js +135 -0
- package/dist/public/vendor/vditor/dist/js/i18n/de_DE.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/en_US.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/es_ES.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/fr_FR.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/ja_JP.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/ko_KR.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/pt_BR.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/ru_RU.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/sv_SE.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/vi_VN.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/zh_CN.js +86 -0
- package/dist/public/vendor/vditor/dist/js/i18n/zh_TW.js +86 -0
- package/dist/public/vendor/vditor/dist/js/icons/ant.js +174 -0
- package/dist/public/vendor/vditor/dist/js/icons/material.js +195 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_AMS-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_AMS-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_AMS-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Caligraphic-Bold.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Caligraphic-Bold.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Caligraphic-Bold.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Caligraphic-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Caligraphic-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Caligraphic-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Fraktur-Bold.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Fraktur-Bold.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Fraktur-Bold.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Fraktur-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Fraktur-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Fraktur-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Bold.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Bold.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Bold.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-BoldItalic.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-BoldItalic.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-BoldItalic.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Italic.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Italic.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Italic.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Main-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Math-BoldItalic.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Math-BoldItalic.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Math-BoldItalic.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Math-Italic.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Math-Italic.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Math-Italic.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Bold.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Bold.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Bold.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Italic.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Italic.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Italic.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_SansSerif-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Script-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Script-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Script-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size1-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size1-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size1-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size2-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size2-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size2-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size3-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size3-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size3-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size4-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size4-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Size4-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Typewriter-Regular.ttf +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Typewriter-Regular.woff +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/fonts/KaTeX_Typewriter-Regular.woff2 +0 -0
- package/dist/public/vendor/vditor/dist/js/katex/katex.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/katex/katex.min.js +1 -0
- package/dist/public/vendor/vditor/dist/js/katex/mhchem.min.js +1 -0
- package/dist/public/vendor/vditor/dist/js/lute/lute.min.js +72 -0
- package/dist/public/vendor/vditor/dist/js/markmap/katex.min.css +1 -0
- package/dist/public/vendor/vditor/dist/js/markmap/markmap.min.js +8251 -0
- package/dist/public/vendor/vditor/dist/js/markmap/prism.css +140 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/LICENSE +202 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/a11y/assistive-mml.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/a11y/complexity.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/a11y/explorer.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/a11y/semantic-enrich.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/asciimath.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/mml/entities.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/mml.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/action.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/all-packages.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/ams.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/amscd.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/autoload.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/bbox.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/boldsymbol.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/braket.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/bussproofs.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/cancel.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/color.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/colorV2.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/configMacros.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/enclose.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/extpfeil.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/html.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/mhchem.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/newcommand.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/noerrors.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/noundefined.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/physics.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/require.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/tagFormat.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/textmacros.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/unicode.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex/extensions/verb.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex-base.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex-full.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/input/tex.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/mathmaps/de.js +104 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/mathmaps/en.js +110 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/mathmaps/es.js +104 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/mathmaps/fr.js +104 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/mathmaps/mathmaps_ie.js +518 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/mathmaps/nemeth.js +104 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/sre-node.js +11 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/sre/sre_browser.js +1633 -0
- package/dist/public/vendor/vditor/dist/js/mathjax/tex-svg-full.js +1 -0
- package/dist/public/vendor/vditor/dist/js/mermaid/mermaid.min.js +2607 -0
- package/dist/public/vendor/vditor/dist/js/plantuml/plantuml-encoder.min.js +1 -0
- package/dist/public/vendor/vditor/dist/js/smiles-drawer/smiles-drawer.min.js +71 -0
- package/dist/public/vendor/vditor/dist/method.d.ts +51 -0
- package/dist/public/vendor/vditor/dist/method.js +2673 -0
- package/dist/public/vendor/vditor/dist/method.min.js +1 -0
- package/dist/public/vendor/vditor/dist/ts/constants.d.ts +45 -0
- package/dist/public/vendor/vditor/dist/ts/devtools/index.d.ts +7 -0
- package/dist/public/vendor/vditor/dist/ts/export/index.d.ts +4 -0
- package/dist/public/vendor/vditor/dist/ts/hint/index.d.ts +14 -0
- package/dist/public/vendor/vditor/dist/ts/ir/expandMarker.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/ir/highlightToolbarIR.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/ir/index.d.ts +13 -0
- package/dist/public/vendor/vditor/dist/ts/ir/input.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/ir/process.d.ts +8 -0
- package/dist/public/vendor/vditor/dist/ts/ir/processKeydown.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/SMILESRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/abcRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/adapterRender.d.ts +41 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/anchorRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/chartRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/codeRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/flowchartRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/getHTML.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/getMarkdown.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/graphvizRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/highlightRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/lazyLoadImageRender.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/markmapRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/mathRender.d.ts +9 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/mediaRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/mermaidRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/mindmapRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/outlineRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/plantumlRender.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/previewRender.d.ts +2 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/setLute.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/markdown/speechRender.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/outline/index.d.ts +7 -0
- package/dist/public/vendor/vditor/dist/ts/preview/image.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/preview/index.d.ts +10 -0
- package/dist/public/vendor/vditor/dist/ts/resize/index.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/sv/combineFootnote.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/sv/index.d.ts +13 -0
- package/dist/public/vendor/vditor/dist/ts/sv/inputEvent.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/sv/process.d.ts +11 -0
- package/dist/public/vendor/vditor/dist/ts/sv/processKeydown.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/tip/index.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Both.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Br.d.ts +4 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/CodeTheme.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/ContentTheme.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Counter.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Custom.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Devtools.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Divider.d.ts +4 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/EditMode.d.ts +8 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Emoji.d.ts +7 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Export.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Fullscreen.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Headings.d.ts +7 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Help.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Indent.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Info.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/InsertAfter.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/InsertBefore.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/MenuItem.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Outdent.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Outline.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Preview.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Record.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Redo.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Undo.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/Upload.d.ts +6 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/index.d.ts +10 -0
- package/dist/public/vendor/vditor/dist/ts/toolbar/setToolbar.d.ts +20 -0
- package/dist/public/vendor/vditor/dist/ts/ui/initUI.d.ts +4 -0
- package/dist/public/vendor/vditor/dist/ts/ui/setCodeTheme.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/ui/setContentTheme.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/ui/setPreviewMode.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/ui/setTheme.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/undo/index.d.ts +19 -0
- package/dist/public/vendor/vditor/dist/ts/upload/getElement.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/upload/index.d.ts +8 -0
- package/dist/public/vendor/vditor/dist/ts/upload/setHeaders.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/Options.d.ts +8 -0
- package/dist/public/vendor/vditor/dist/ts/util/RecordMedia.d.ts +18 -0
- package/dist/public/vendor/vditor/dist/ts/util/addScript.d.ts +2 -0
- package/dist/public/vendor/vditor/dist/ts/util/addStyle.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/code160to32.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/compatibility.d.ts +7 -0
- package/dist/public/vendor/vditor/dist/ts/util/editorCommonEvent.d.ts +9 -0
- package/dist/public/vendor/vditor/dist/ts/util/fixBrowserBehavior.d.ts +39 -0
- package/dist/public/vendor/vditor/dist/ts/util/function.d.ts +3 -0
- package/dist/public/vendor/vditor/dist/ts/util/getSelectText.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/hasClosest.d.ts +9 -0
- package/dist/public/vendor/vditor/dist/ts/util/hasClosestByHeadings.d.ts +2 -0
- package/dist/public/vendor/vditor/dist/ts/util/highlightToolbar.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/hotKey.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/log.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/merge.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/util/processCode.d.ts +2 -0
- package/dist/public/vendor/vditor/dist/ts/util/selection.d.ts +14 -0
- package/dist/public/vendor/vditor/dist/ts/util/toc.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/afterRenderEvent.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/highlightToolbarWYSIWYG.d.ts +4 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/index.d.ts +22 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/inlineTag.d.ts +10 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/input.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/processKeydown.d.ts +2 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/renderDomByMd.d.ts +5 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/setHeading.d.ts +2 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/showCode.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/ts/wysiwyg/toolbarEvent.d.ts +1 -0
- package/dist/public/vendor/vditor/dist/types/index.d.ts +921 -0
- package/dist/public/work-permissions.d.ts +5 -2
- package/dist/public/work-permissions.js +40 -13
- package/dist/security.js +11 -0
- package/dist/security.js.map +1 -1
- package/dist/server-runtime.js +5 -2
- package/dist/server-runtime.js.map +1 -1
- package/dist/store.js +182 -27
- package/dist/store.js.map +1 -1
- package/dist/user-auth.js +36 -7
- package/dist/user-auth.js.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/dist/work-permissions.js +29 -6
- package/dist/work-permissions.js.map +1 -1
- package/package.json +2 -1
package/dist/public/app.js
CHANGED
|
@@ -4,7 +4,7 @@ import { renderMarkdown } from "/markdown.js?v=20260722-inline-code";
|
|
|
4
4
|
import { buildAiReferenceScope, findAiMention, listAiMentionOptions } from "/ai-mentions.js?v=20260716-chapter-references";
|
|
5
5
|
import { shouldShowAiQuickActions } from "/ai-conversation.js?v=20260713-quick-actions";
|
|
6
6
|
import { calculateLineNumberRowHeight, calculateLineNumberRowTop, calculateLineNumberTextOffset, calculateLineNumberTop } from "/line-number-layout.js?v=20260713-row-box-alignment";
|
|
7
|
-
import { MODEL_PURPOSE_OPTIONS, modelFormValues, modelOptionLabel, modelPayload } from "/model-config.js?v=
|
|
7
|
+
import { MODEL_PURPOSE_OPTIONS, isKimiModelId, modelFormValues, modelOptionLabel, modelPayload } from "/model-config.js?v=20260723-kimi-temperature";
|
|
8
8
|
import { shouldSendAiPrompt } from "/ai-prompt-keyboard.js?v=20260713-enter-to-send";
|
|
9
9
|
import { estimateAiMessageTokens, formatAiMessageMeta } from "/ai-message-meta.js?v=20260713-persisted-output-tokens";
|
|
10
10
|
import { formatAiMessageTime } from "/ai-message-time.js?v=20260713-cross-day-time";
|
|
@@ -14,12 +14,13 @@ import { THEME_STORAGE_KEY, nextTheme, normalizeTheme, themeToggleLabel } from "
|
|
|
14
14
|
import { buildCharacterDetails, buildCharacterState, characterStateEntries, normalizeCharacterDetails, normalizeCharacterSections } from "/character-profile.js?v=20260713-character-editor";
|
|
15
15
|
import { characterVersionSourceLabel, describeCharacterVersionChanges } from "/character-version.js?v=20260713-character-history";
|
|
16
16
|
import { VERSIONED_ENTITY_LABELS, entityVersionSnapshotSummary, entityVersionSourceLabel } from "/entity-version.js?v=20260714-all-knowledge-history";
|
|
17
|
-
import { parsePageRoute, serializePageRoute } from "/page-route.js?v=
|
|
17
|
+
import { parsePageRoute, serializePageRoute } from "/page-route.js?v=20260723-knowledge-editor-page";
|
|
18
18
|
import { splitRelationshipKeywordInput, splitRelationshipKeywords, uniqueRelationshipKeywords } from "/relationship-keywords.js?v=20260720-relationship-keyword-chips";
|
|
19
19
|
import { tokenizeVisibleSpaces } from "/whitespace-visualization.js?v=20260718-visible-whitespace";
|
|
20
20
|
import { buildRaceForest, eligibleRaceParents, racePathLabel } from "/race-hierarchy.js?v=20260721-race-hierarchy";
|
|
21
21
|
import { ANALYSIS_TYPES, analysisTypeDescription } from "/analysis-types.js?v=20260721-analysis-descriptions";
|
|
22
|
-
import { WORK_PERMISSION_MODULES, canReadUiModule, canWriteUiModule, emptyModulePermissions, firstReadableUiModule, normalizeModulePermissions, permissionSummary } from "/work-permissions.js?v=
|
|
22
|
+
import { WORK_PERMISSION_MODULES, canReadPermissionModule, canReadUiModule, canWritePermissionModule, canWriteUiModule, emptyModulePermissions, firstReadableUiModule, normalizeModulePermissions, permissionSummary } from "/work-permissions.js?v=20260723-ai-analysis-permission";
|
|
23
|
+
import { MODULE_LAYOUT_STORAGE_KEY, LEGACY_SETTINGS_LAYOUT_STORAGE_KEY, normalizeModuleLayout, moduleLayoutLabel } from "/module-layout.js?v=20260723-module-layout-toggle";
|
|
23
24
|
|
|
24
25
|
const state = {
|
|
25
26
|
user: null,
|
|
@@ -82,7 +83,7 @@ function analysisTaskStatusLabel(status) {
|
|
|
82
83
|
}
|
|
83
84
|
|
|
84
85
|
function canEditWork(work = state.work) {
|
|
85
|
-
return WORK_PERMISSION_MODULES.some((item) =>
|
|
86
|
+
return WORK_PERMISSION_MODULES.some((item) => canWritePermissionModule(work, item.id));
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
function canEditProse(work = state.work) {
|
|
@@ -92,7 +93,7 @@ function canEditProse(work = state.work) {
|
|
|
92
93
|
function canReplaceProse(work = state.work) {
|
|
93
94
|
return WORK_PERMISSION_MODULES
|
|
94
95
|
.filter((item) => item.id !== "ai-settings")
|
|
95
|
-
.every((item) =>
|
|
96
|
+
.every((item) => canWritePermissionModule(work, item.id));
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
function canManageWork(work = state.work) {
|
|
@@ -116,8 +117,8 @@ function applyWorkAccessMode() {
|
|
|
116
117
|
const viewOnly = Boolean(state.work) && !canEditWork();
|
|
117
118
|
const proseReadOnly = Boolean(state.work) && !canEditProse();
|
|
118
119
|
const proseHidden = Boolean(state.work) && !canReadModule("editor");
|
|
119
|
-
const aiHidden = Boolean(state.work) && !
|
|
120
|
-
const aiReadOnly = Boolean(state.work) && !
|
|
120
|
+
const aiHidden = Boolean(state.work) && !canReadPermissionModule(state.work, "ai-chat");
|
|
121
|
+
const aiReadOnly = Boolean(state.work) && !canWritePermissionModule(state.work, "ai-chat");
|
|
121
122
|
const moduleReadOnly = Boolean(state.work) && !canEditModule(state.module);
|
|
122
123
|
$("#app").classList.toggle("view-only-mode", viewOnly);
|
|
123
124
|
$("#app").classList.toggle("prose-read-only-mode", proseReadOnly);
|
|
@@ -125,14 +126,16 @@ function applyWorkAccessMode() {
|
|
|
125
126
|
$("#app").classList.toggle("ai-hidden-mode", aiHidden);
|
|
126
127
|
document.body.classList.toggle("work-viewer-mode", moduleReadOnly);
|
|
127
128
|
for (const item of WORK_PERMISSION_MODULES) {
|
|
129
|
+
if (!item.uiModule) continue;
|
|
128
130
|
const button = $(`#module-nav [data-module="${item.uiModule}"]`);
|
|
129
131
|
if (button) button.classList.toggle("permission-hidden", !canReadModule(item.uiModule));
|
|
130
132
|
}
|
|
131
133
|
$("#module-nav [data-work-settings]").classList.toggle("permission-hidden", Boolean(state.work) && !canManageWork());
|
|
132
|
-
$("#
|
|
133
|
-
$("#
|
|
134
|
+
$("#new-volume-button").classList.toggle("permission-hidden", Boolean(state.work) && proseReadOnly);
|
|
135
|
+
$("#welcome-new-work").classList.toggle("permission-hidden", Boolean(state.work) && proseReadOnly);
|
|
136
|
+
$("#import-file-button").setAttribute("aria-disabled", String(proseReadOnly));
|
|
137
|
+
$("#import-file-button").setAttribute("title", proseReadOnly ? "当前权限不能导入正文" : "导入 TXT / DOCX");
|
|
134
138
|
$("#import-file").disabled = proseReadOnly;
|
|
135
|
-
$("#import-history-button").classList.toggle("permission-hidden", Boolean(state.work) && !canReplaceProse());
|
|
136
139
|
$(".ai-panel").classList.toggle("permission-hidden", aiHidden);
|
|
137
140
|
$("#chapter-title").readOnly = proseReadOnly;
|
|
138
141
|
$("#chapter-content").readOnly = proseReadOnly;
|
|
@@ -226,7 +229,7 @@ const shelfOnboardingSteps = [
|
|
|
226
229
|
const workspaceOnboardingSteps = [
|
|
227
230
|
{ selector: "#home-button", eyebrow: "作品入口", title: "随时返回书架", description: "点击叙界标志返回书架,切换作品或创建新的故事世界。", placement: "bottom" },
|
|
228
231
|
{ selector: ".file-button", eyebrow: "稿件导入", title: "导入 TXT 或 DOCX", description: "已有稿件可以直接导入,系统会解析分卷与章节结构。", placement: "right" },
|
|
229
|
-
{ selector: "
|
|
232
|
+
{ selector: "[data-new-chapter-volume]", eyebrow: "正文结构", title: "新建章节", description: "使用分卷和章节组织长篇正文,章节会自动保存并保留版本。", placement: "right" },
|
|
230
233
|
{ selector: "#versions-button", eyebrow: "版本安全", title: "查看章节版本", description: "每次保存都会生成可恢复版本,误改内容时可以随时回溯。", placement: "bottom" },
|
|
231
234
|
{ selector: "[data-module=\"characters\"]", eyebrow: "作品知识", title: "维护角色与世界资料", description: "角色、种族、组织、设定和时间线共同构成 AI 可引用的作品知识。", placement: "right" },
|
|
232
235
|
{ selector: "[data-module=\"outlines\"]", eyebrow: "创作规划", title: "跟踪大纲与伏笔", description: "记录剧情目标、冲突、转折和伏笔回收,避免长线遗漏。", placement: "right" },
|
|
@@ -408,7 +411,7 @@ function replacePageRoute(route) {
|
|
|
408
411
|
function currentPageRoute() {
|
|
409
412
|
const workId = state.work?.id ?? null;
|
|
410
413
|
if (!$("#entity-editor-view").classList.contains("hidden") && workId && entityEditorType) {
|
|
411
|
-
const entityId = entityEditorType === "setting" ? settingEditorItem?.id : characterEditorItem?.id;
|
|
414
|
+
const entityId = entityEditorType === "setting" ? settingEditorItem?.id : entityEditorType === "character" ? characterEditorItem?.id : knowledgeEditorItem?.id;
|
|
412
415
|
return { view: "entity-editor", workId, entity: entityEditorType, entityId: entityId ?? null };
|
|
413
416
|
}
|
|
414
417
|
if (!$("#settings-hub-view").classList.contains("hidden")) return { view: "settings", workId, ...settingsRouteContext() };
|
|
@@ -524,50 +527,76 @@ let entityEditorType = null;
|
|
|
524
527
|
let entityEditorDirty = false;
|
|
525
528
|
let settingEditorItem = null;
|
|
526
529
|
let characterEditorItem = null;
|
|
530
|
+
let knowledgeEditorItem = null;
|
|
531
|
+
let knowledgeEditorKind = null;
|
|
532
|
+
let knowledgeEditorSections = [];
|
|
533
|
+
let knowledgeSectionEditorIndex = null;
|
|
534
|
+
let knowledgeSectionEditorDirty = false;
|
|
527
535
|
let characterEditorVersions = [];
|
|
528
536
|
let characterEditorRelationships = [];
|
|
529
537
|
let characterEditorRelationshipsLoading = false;
|
|
530
538
|
let characterEditorSections = [];
|
|
531
|
-
let characterSectionPreviewTimer = null;
|
|
532
539
|
let characterSectionPendingAttachments = [];
|
|
540
|
+
let markdownEditorPendingAttachments = [];
|
|
533
541
|
let characterSectionEditorDirty = false;
|
|
542
|
+
let settingEditorVditor = null;
|
|
543
|
+
let knowledgeSectionVditor = null;
|
|
544
|
+
let characterSectionVditor = null;
|
|
534
545
|
let entityHistoryContext = null;
|
|
535
546
|
|
|
536
547
|
function showEntityEditorPage(type) {
|
|
537
548
|
entityEditorType = type;
|
|
538
549
|
entityEditorDirty = false;
|
|
539
550
|
characterSectionEditorDirty = false;
|
|
551
|
+
knowledgeSectionEditorDirty = false;
|
|
540
552
|
$("#entity-editor-view").classList.remove("hidden");
|
|
541
553
|
$("#setting-editor-form").classList.toggle("hidden", type !== "setting");
|
|
542
554
|
$("#character-editor-form").classList.toggle("hidden", type !== "character");
|
|
555
|
+
$("#knowledge-editor-form").classList.toggle("hidden", !["race", "organization"].includes(type));
|
|
543
556
|
$("#character-section-editor-view").classList.add("hidden");
|
|
557
|
+
$("#knowledge-section-editor-view").classList.add("hidden");
|
|
544
558
|
$("#app").inert = true;
|
|
545
559
|
document.body.classList.add("entity-editor-open");
|
|
546
560
|
replacePageRoute(currentPageRoute());
|
|
547
561
|
}
|
|
548
562
|
|
|
549
563
|
function markEntityEditorDirty() {
|
|
550
|
-
const module = entityEditorType === "setting" ? "settings" : "characters";
|
|
564
|
+
const module = entityEditorType === "setting" ? "settings" : entityEditorType === "character" ? "characters" : entityEditorType === "race" ? "races" : "organizations";
|
|
551
565
|
if (entityEditorType && canEditModule(module)) entityEditorDirty = true;
|
|
552
566
|
}
|
|
553
567
|
|
|
554
|
-
function confirmEntityEditorDiscard(message) {
|
|
568
|
+
async function confirmEntityEditorDiscard(message) {
|
|
555
569
|
if (!entityEditorDirty) return true;
|
|
556
|
-
return
|
|
570
|
+
return confirmToast(message ?? "当前资料有未保存修改,返回列表将丢弃这些修改。是否继续?", {
|
|
571
|
+
title: "放弃未保存修改",
|
|
572
|
+
confirmLabel: "放弃并继续",
|
|
573
|
+
cancelLabel: "继续编辑"
|
|
574
|
+
});
|
|
557
575
|
}
|
|
558
576
|
|
|
559
577
|
async function closeEntityEditor({ force = false } = {}) {
|
|
560
578
|
if (!$("#character-section-editor-view").classList.contains("hidden")) return closeCharacterSectionEditor({ force });
|
|
561
|
-
if (
|
|
579
|
+
if (!$("#knowledge-section-editor-view").classList.contains("hidden")) return closeKnowledgeSectionEditor({ force });
|
|
580
|
+
if (!force && !(await confirmEntityEditorDiscard())) return false;
|
|
562
581
|
await discardPendingCharacterAttachments();
|
|
563
|
-
|
|
582
|
+
await discardPendingMarkdownAttachments();
|
|
583
|
+
destroyVditorEditor(settingEditorVditor);
|
|
584
|
+
settingEditorVditor = null;
|
|
585
|
+
const module = entityEditorType === "setting" ? "settings" : entityEditorType === "character" ? "characters" : entityEditorType === "race" ? "races" : "organizations";
|
|
564
586
|
entityEditorType = null;
|
|
565
587
|
entityEditorDirty = false;
|
|
566
588
|
settingEditorItem = null;
|
|
567
589
|
characterEditorItem = null;
|
|
590
|
+
knowledgeEditorItem = null;
|
|
591
|
+
knowledgeEditorKind = null;
|
|
592
|
+
knowledgeEditorSections = [];
|
|
593
|
+
knowledgeSectionEditorIndex = null;
|
|
594
|
+
knowledgeSectionEditorDirty = false;
|
|
568
595
|
$("#entity-editor-view").classList.add("hidden");
|
|
569
596
|
$("#setting-editor-form").classList.add("hidden");
|
|
570
597
|
$("#character-editor-form").classList.add("hidden");
|
|
598
|
+
$("#knowledge-editor-form").classList.add("hidden");
|
|
599
|
+
$("#knowledge-section-editor-view").classList.add("hidden");
|
|
571
600
|
$("#app").inert = false;
|
|
572
601
|
document.body.classList.remove("entity-editor-open");
|
|
573
602
|
await showModule(module);
|
|
@@ -1407,6 +1436,7 @@ function applyColorTheme(theme) {
|
|
|
1407
1436
|
button.setAttribute("aria-pressed", String(normalized === "dark"));
|
|
1408
1437
|
const themeColor = document.querySelector('meta[name="theme-color"]');
|
|
1409
1438
|
if (themeColor) themeColor.content = normalized === "dark" ? "#171714" : "#8b3d2c";
|
|
1439
|
+
[settingEditorVditor, knowledgeSectionVditor, characterSectionVditor].forEach((editor) => editor?.setTheme(normalized === "dark" ? "dark" : "classic"));
|
|
1410
1440
|
}
|
|
1411
1441
|
|
|
1412
1442
|
function saveColorTheme(theme) {
|
|
@@ -1701,6 +1731,47 @@ function toast(message, type = "info") {
|
|
|
1701
1731
|
}, 3600);
|
|
1702
1732
|
}
|
|
1703
1733
|
|
|
1734
|
+
function confirmToast(message, { title = "请再次确认", confirmLabel = "确认", cancelLabel = "取消" } = {}) {
|
|
1735
|
+
const region = $("#toast-region");
|
|
1736
|
+
const element = document.createElement("section");
|
|
1737
|
+
element.className = "toast toast-confirmation";
|
|
1738
|
+
element.setAttribute("role", "alertdialog");
|
|
1739
|
+
element.setAttribute("aria-label", title);
|
|
1740
|
+
const heading = document.createElement("strong");
|
|
1741
|
+
heading.textContent = title;
|
|
1742
|
+
const description = document.createElement("p");
|
|
1743
|
+
description.textContent = message;
|
|
1744
|
+
const actions = document.createElement("div");
|
|
1745
|
+
actions.className = "toast-confirmation-actions";
|
|
1746
|
+
const cancel = document.createElement("button");
|
|
1747
|
+
cancel.className = "ghost-button";
|
|
1748
|
+
cancel.type = "button";
|
|
1749
|
+
cancel.textContent = cancelLabel;
|
|
1750
|
+
const confirm = document.createElement("button");
|
|
1751
|
+
confirm.className = "primary-button";
|
|
1752
|
+
confirm.type = "button";
|
|
1753
|
+
confirm.textContent = confirmLabel;
|
|
1754
|
+
actions.append(cancel, confirm);
|
|
1755
|
+
element.append(heading, description, actions);
|
|
1756
|
+
region.append(element);
|
|
1757
|
+
raiseToastRegion();
|
|
1758
|
+
cancel.focus();
|
|
1759
|
+
return new Promise((resolve) => {
|
|
1760
|
+
const finish = (confirmed) => {
|
|
1761
|
+
element.remove();
|
|
1762
|
+
if (!region.childElementCount && typeof region.hidePopover === "function" && region.matches(":popover-open")) region.hidePopover();
|
|
1763
|
+
resolve(confirmed);
|
|
1764
|
+
};
|
|
1765
|
+
cancel.addEventListener("click", () => finish(false), { once: true });
|
|
1766
|
+
confirm.addEventListener("click", () => finish(true), { once: true });
|
|
1767
|
+
element.addEventListener("keydown", (event) => {
|
|
1768
|
+
if (event.key !== "Escape") return;
|
|
1769
|
+
event.preventDefault();
|
|
1770
|
+
finish(false);
|
|
1771
|
+
});
|
|
1772
|
+
});
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1704
1775
|
document.addEventListener("toggle", (event) => {
|
|
1705
1776
|
const target = event.target;
|
|
1706
1777
|
if (target instanceof HTMLDialogElement && target.open && $("#toast-region").childElementCount) {
|
|
@@ -1806,23 +1877,34 @@ async function persistChapter({ automatic = false } = {}) {
|
|
|
1806
1877
|
}
|
|
1807
1878
|
}
|
|
1808
1879
|
|
|
1809
|
-
function confirmDiscardChanges(message = "当前章节有未保存修改,继续将丢弃这些修改。是否继续?") {
|
|
1880
|
+
async function confirmDiscardChanges(message = "当前章节有未保存修改,继续将丢弃这些修改。是否继续?") {
|
|
1810
1881
|
if (!state.dirty) return true;
|
|
1811
|
-
return
|
|
1882
|
+
return confirmToast(message, {
|
|
1883
|
+
title: "放弃未保存修改",
|
|
1884
|
+
confirmLabel: "放弃并继续",
|
|
1885
|
+
cancelLabel: "继续编辑"
|
|
1886
|
+
});
|
|
1812
1887
|
}
|
|
1813
1888
|
|
|
1814
1889
|
function chooseExistingWorkImportMode(file) {
|
|
1815
1890
|
const dialog = $("#import-mode-dialog");
|
|
1891
|
+
const form = dialog.querySelector("form");
|
|
1892
|
+
const confirm = $("#import-mode-confirm");
|
|
1816
1893
|
const canOverwrite = canReplaceProse();
|
|
1817
1894
|
$("#import-mode-file-summary").textContent = `文件:${file.name};当前作品:《${state.work.title}》`;
|
|
1818
1895
|
$("#import-mode-unsaved-warning").classList.toggle("hidden", !state.dirty);
|
|
1896
|
+
form.reset();
|
|
1819
1897
|
$("#import-mode-overwrite").disabled = !canOverwrite;
|
|
1820
1898
|
$("#import-mode-overwrite-permission").classList.toggle("hidden", canOverwrite);
|
|
1899
|
+
confirm.disabled = true;
|
|
1900
|
+
form.onchange = () => { confirm.disabled = !form.querySelector('input[name="importMode"]:checked'); };
|
|
1821
1901
|
dialog.returnValue = "cancel";
|
|
1822
1902
|
dialog.showModal();
|
|
1903
|
+
$("#import-mode-dialog-title").focus();
|
|
1823
1904
|
return new Promise((resolve) => {
|
|
1824
1905
|
dialog.addEventListener("close", () => {
|
|
1825
|
-
|
|
1906
|
+
const mode = form.querySelector('input[name="importMode"]:checked')?.value;
|
|
1907
|
+
resolve(dialog.returnValue === "confirm" && ["append", "overwrite"].includes(mode) ? mode : null);
|
|
1826
1908
|
}, { once: true });
|
|
1827
1909
|
});
|
|
1828
1910
|
}
|
|
@@ -1874,7 +1956,7 @@ async function initializePage() {
|
|
|
1874
1956
|
state.module = route.view === "module"
|
|
1875
1957
|
? route.module
|
|
1876
1958
|
: route.view === "entity-editor"
|
|
1877
|
-
? (
|
|
1959
|
+
? ({ setting: "settings", character: "characters", race: "races", organization: "organizations" }[route.entity] ?? "characters")
|
|
1878
1960
|
: "editor";
|
|
1879
1961
|
await selectWork(requestedWork.id, route.view === "editor" ? route.chapterId : null);
|
|
1880
1962
|
}
|
|
@@ -1885,14 +1967,16 @@ async function initializePage() {
|
|
|
1885
1967
|
}
|
|
1886
1968
|
if (route.view === "module") return;
|
|
1887
1969
|
if (route.view === "entity-editor") {
|
|
1888
|
-
const records = route.entity === "setting" ? state.settings : state.characters;
|
|
1970
|
+
const records = route.entity === "setting" ? state.settings : route.entity === "character" ? state.characters : route.entity === "race" ? state.races : state.organizations;
|
|
1889
1971
|
const item = route.entityId ? records.find((record) => record.id === route.entityId) : null;
|
|
1890
1972
|
if (route.entityId && !item) {
|
|
1891
|
-
toast(
|
|
1973
|
+
toast(({ setting: "未找到要编辑的设定", character: "未找到要编辑的角色", race: "未找到要编辑的种族", organization: "未找到要编辑的组织" }[route.entity] ?? "未找到要编辑的档案"), "error");
|
|
1892
1974
|
return;
|
|
1893
1975
|
}
|
|
1894
1976
|
if (route.entity === "setting") openSettingEditor(item);
|
|
1895
|
-
else await openCharacterEditor(item);
|
|
1977
|
+
else if (route.entity === "character") await openCharacterEditor(item);
|
|
1978
|
+
else if (route.entity === "race") await openRaceDialog(item);
|
|
1979
|
+
else if (route.entity === "organization") await openOrganizationDialog(item);
|
|
1896
1980
|
return;
|
|
1897
1981
|
}
|
|
1898
1982
|
if (route.view === "welcome") {
|
|
@@ -1900,7 +1984,7 @@ async function initializePage() {
|
|
|
1900
1984
|
return;
|
|
1901
1985
|
}
|
|
1902
1986
|
if (route.view === "settings") {
|
|
1903
|
-
showSettingsHub();
|
|
1987
|
+
await showSettingsHub();
|
|
1904
1988
|
settingsReturnContext = restoredSettingsReturnContext(route);
|
|
1905
1989
|
renderSettingsHub();
|
|
1906
1990
|
return;
|
|
@@ -2195,10 +2279,10 @@ async function openSearchResult(result) {
|
|
|
2195
2279
|
}
|
|
2196
2280
|
}
|
|
2197
2281
|
|
|
2198
|
-
function showSettingsHub() {
|
|
2282
|
+
async function showSettingsHub() {
|
|
2199
2283
|
const alreadyInSettings = !$("#settings-hub-view").classList.contains("hidden") || !$("#platform-ai-view").classList.contains("hidden");
|
|
2200
2284
|
if (!alreadyInSettings) {
|
|
2201
|
-
if (state.dirty && !confirmDiscardChanges("当前章节有未保存修改,进入设置将放弃本地修改。是否继续?")) return false;
|
|
2285
|
+
if (state.dirty && !(await confirmDiscardChanges("当前章节有未保存修改,进入设置将放弃本地修改。是否继续?"))) return false;
|
|
2202
2286
|
settingsReturnContext = captureSettingsReturnContext();
|
|
2203
2287
|
state.dirty = false;
|
|
2204
2288
|
}
|
|
@@ -2235,7 +2319,7 @@ async function returnFromSettings() {
|
|
|
2235
2319
|
}
|
|
2236
2320
|
|
|
2237
2321
|
async function showPlatformAi() {
|
|
2238
|
-
if (state.dirty && !confirmDiscardChanges("当前章节有未保存修改,进入平台 AI 管理将放弃本地修改。是否继续?")) return false;
|
|
2322
|
+
if (state.dirty && !(await confirmDiscardChanges("当前章节有未保存修改,进入平台 AI 管理将放弃本地修改。是否继续?"))) return false;
|
|
2239
2323
|
state.dirty = false;
|
|
2240
2324
|
updateDocumentTitle();
|
|
2241
2325
|
$("#app").classList.add("shelf-mode");
|
|
@@ -2311,7 +2395,7 @@ function resetWorkScopedUiCaches() {
|
|
|
2311
2395
|
|
|
2312
2396
|
async function selectWork(workId, preferredChapterId = null) {
|
|
2313
2397
|
const discarding = state.work?.id !== workId && state.dirty;
|
|
2314
|
-
if (discarding && !confirmDiscardChanges()) return false;
|
|
2398
|
+
if (discarding && !(await confirmDiscardChanges())) return false;
|
|
2315
2399
|
const nextWork = await api(`/api/works/${workId}?page=1&limit=100`);
|
|
2316
2400
|
if (state.work?.id !== nextWork.id) resetWorkScopedUiCaches();
|
|
2317
2401
|
if (discarding) setSaveState("就绪");
|
|
@@ -2342,11 +2426,15 @@ async function selectWork(workId, preferredChapterId = null) {
|
|
|
2342
2426
|
function renderTree() {
|
|
2343
2427
|
if (!state.work) return;
|
|
2344
2428
|
const count = state.work.volumes.reduce((total, volume) => total + volume.chapters.length, 0);
|
|
2429
|
+
const proseEditable = canEditProse();
|
|
2345
2430
|
$("#chapter-count").textContent = `${count} 章`;
|
|
2346
2431
|
$("#novel-tree").classList.remove("empty-copy");
|
|
2347
2432
|
$("#novel-tree").innerHTML = state.work.volumes.map((volume) => `
|
|
2348
2433
|
<div class="volume-node ${state.collapsedVolumeIds.has(volume.id) ? "is-collapsed" : ""}" data-volume-id="${esc(volume.id)}">
|
|
2349
|
-
<
|
|
2434
|
+
<div class="volume-title">
|
|
2435
|
+
<button class="volume-toggle" type="button" data-volume-toggle="${esc(volume.id)}" aria-expanded="${state.collapsedVolumeIds.has(volume.id) ? "false" : "true"}" title="左键折叠,右键设置分卷"><span>${esc(volume.title)}</span><span>${volume.chapters.length} 章</span></button>
|
|
2436
|
+
${proseEditable ? `<button class="add-button chapter-add-button" type="button" data-new-chapter-volume="${esc(volume.id)}" aria-label="在“${esc(volume.title)}”中新建章节" title="在“${esc(volume.title)}”中新建章节">+</button>` : ""}
|
|
2437
|
+
</div>
|
|
2350
2438
|
<div class="volume-chapters">
|
|
2351
2439
|
${volume.chapters.map((chapter) => `
|
|
2352
2440
|
<button class="chapter-node ${state.chapter?.id === chapter.id ? "active" : ""}" type="button" data-chapter-id="${esc(chapter.id)}">
|
|
@@ -2367,6 +2455,9 @@ function renderTree() {
|
|
|
2367
2455
|
openVolumeDialog(state.work.volumes.find((volume) => volume.id === button.dataset.volumeToggle));
|
|
2368
2456
|
});
|
|
2369
2457
|
});
|
|
2458
|
+
$("#novel-tree").querySelectorAll("[data-new-chapter-volume]").forEach((button) => {
|
|
2459
|
+
button.addEventListener("click", () => openChapterDialog(button.dataset.newChapterVolume));
|
|
2460
|
+
});
|
|
2370
2461
|
$("#novel-tree").querySelectorAll("[data-chapter-id]").forEach((button) => {
|
|
2371
2462
|
button.addEventListener("click", () => selectChapter(button.dataset.chapterId));
|
|
2372
2463
|
button.addEventListener("contextmenu", (event) => {
|
|
@@ -2399,7 +2490,7 @@ function openChapterTypeMenu(chapterId, clientX, clientY) {
|
|
|
2399
2490
|
}
|
|
2400
2491
|
|
|
2401
2492
|
async function selectChapter(chapterId) {
|
|
2402
|
-
if (state.chapter?.id !== chapterId && !confirmDiscardChanges("当前章节有未保存修改,仍要切换吗?")) return;
|
|
2493
|
+
if (state.chapter?.id !== chapterId && !(await confirmDiscardChanges("当前章节有未保存修改,仍要切换吗?"))) return;
|
|
2403
2494
|
cancelChapterAutoSave();
|
|
2404
2495
|
state.chapter = await api(`/api/chapters/${chapterId}`);
|
|
2405
2496
|
lastSavedChapterSnapshot = { chapterId: state.chapter.id, title: state.chapter.title, content: state.chapter.content };
|
|
@@ -2487,7 +2578,7 @@ async function showModule(module) {
|
|
|
2487
2578
|
}
|
|
2488
2579
|
module = fallback;
|
|
2489
2580
|
}
|
|
2490
|
-
if (module !== "editor" && state.module === "editor" && !confirmDiscardChanges()) return;
|
|
2581
|
+
if (module !== "editor" && state.module === "editor" && !(await confirmDiscardChanges())) return;
|
|
2491
2582
|
if (module !== "editor" && state.module === "editor" && state.dirty) setSaveState("已放弃修改");
|
|
2492
2583
|
state.module = module;
|
|
2493
2584
|
applyWorkAccessMode();
|
|
@@ -2616,8 +2707,10 @@ function openEntityMergeDialog({ typeLabel, source, candidates, endpoint, body,
|
|
|
2616
2707
|
}
|
|
2617
2708
|
|
|
2618
2709
|
async function deleteManagedEntity({ typeLabel, item, endpoint, refresh, warning = "" }) {
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2710
|
+
const message = warning
|
|
2711
|
+
? `确认删除${typeLabel}“${item.name}”吗?\n${warning}`
|
|
2712
|
+
: `确认删除${typeLabel}“${item.name}”吗?`;
|
|
2713
|
+
if (!(await confirmToast(message, { title: `删除${typeLabel}`, confirmLabel: "确认删除" }))) return;
|
|
2621
2714
|
try {
|
|
2622
2715
|
await api(endpoint(item), { method: "DELETE" });
|
|
2623
2716
|
await refresh();
|
|
@@ -2628,14 +2721,76 @@ async function deleteManagedEntity({ typeLabel, item, endpoint, refresh, warning
|
|
|
2628
2721
|
}
|
|
2629
2722
|
}
|
|
2630
2723
|
|
|
2724
|
+
function readModuleLayout() {
|
|
2725
|
+
try {
|
|
2726
|
+
const stored = localStorage.getItem(MODULE_LAYOUT_STORAGE_KEY) ?? localStorage.getItem(LEGACY_SETTINGS_LAYOUT_STORAGE_KEY);
|
|
2727
|
+
return normalizeModuleLayout(stored);
|
|
2728
|
+
} catch {
|
|
2729
|
+
return "cards";
|
|
2730
|
+
}
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
function saveModuleLayout(layout) {
|
|
2734
|
+
const normalized = normalizeModuleLayout(layout);
|
|
2735
|
+
try {
|
|
2736
|
+
localStorage.setItem(MODULE_LAYOUT_STORAGE_KEY, normalized);
|
|
2737
|
+
} catch {
|
|
2738
|
+
/* 浏览器禁用存储时仅保留当前会话选择 */
|
|
2739
|
+
}
|
|
2740
|
+
return normalized;
|
|
2741
|
+
}
|
|
2742
|
+
|
|
2743
|
+
function moduleRowPreview(text, max = 180) {
|
|
2744
|
+
const preview = String(text ?? "").replace(/\s+/g, " ").trim();
|
|
2745
|
+
return preview.length > max ? `${preview.slice(0, max)}…` : preview;
|
|
2746
|
+
}
|
|
2747
|
+
|
|
2748
|
+
function renderModuleLayoutToggle(layout, ariaLabel = "列表样式") {
|
|
2749
|
+
return `<div class="module-layout-toolbar">
|
|
2750
|
+
<div class="module-layout-toggle" role="group" aria-label="${esc(ariaLabel)}">
|
|
2751
|
+
<button type="button" data-module-layout="cards" aria-pressed="${layout === "cards"}">卡片</button>
|
|
2752
|
+
<button type="button" data-module-layout="rows" aria-pressed="${layout === "rows"}">列表</button>
|
|
2753
|
+
</div>
|
|
2754
|
+
<span class="module-layout-hint">当前:${esc(moduleLayoutLabel(layout))}</span>
|
|
2755
|
+
</div>`;
|
|
2756
|
+
}
|
|
2757
|
+
|
|
2758
|
+
function bindModuleLayoutToggle(refresh) {
|
|
2759
|
+
$("#module-content").querySelectorAll("[data-module-layout]").forEach((button) => button.addEventListener("click", async () => {
|
|
2760
|
+
saveModuleLayout(button.dataset.moduleLayout);
|
|
2761
|
+
await refresh();
|
|
2762
|
+
}));
|
|
2763
|
+
}
|
|
2764
|
+
|
|
2765
|
+
function settingRecordActions(item) {
|
|
2766
|
+
return `${item.status === "pending" ? `<button data-setting-status="confirmed" data-setting-id="${esc(item.id)}">确认候选</button><button data-setting-status="deprecated" data-setting-id="${esc(item.id)}">弃用</button>` : ""}<button data-edit-setting="${esc(item.id)}">编辑</button><button data-entity-history="setting" data-entity-id="${esc(item.id)}" data-entity-title="${esc(item.title)}">版本历史</button>`;
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
function renderSettingCards(records) {
|
|
2770
|
+
return `<div class="card-grid">${records.map((item) => `
|
|
2771
|
+
<article class="record-card"><small>${esc(item.category)} · ${item.locked ? "已锁定" : esc(item.status)}</small>
|
|
2772
|
+
<h3>${esc(item.title)}</h3><div class="record-markdown-preview message-body">${renderMarkdown(item.content) || '<p class="markdown-editor-empty">暂无正文</p>'}</div>
|
|
2773
|
+
<div class="card-actions">${settingRecordActions(item)}</div></article>`).join("")}</div>`;
|
|
2774
|
+
}
|
|
2775
|
+
|
|
2776
|
+
function renderSettingRows(records) {
|
|
2777
|
+
return `<div class="module-row-list">${records.map((item) => {
|
|
2778
|
+
const preview = moduleRowPreview(item.content);
|
|
2779
|
+
return `
|
|
2780
|
+
<article class="record-card module-row"><small>${esc(item.category)} · ${item.locked ? "已锁定" : esc(item.status)}</small>
|
|
2781
|
+
<h3>${esc(item.title)}</h3><p class="module-row-preview" title="${esc(preview)}">${esc(preview)}</p>
|
|
2782
|
+
<div class="card-actions">${settingRecordActions(item)}</div></article>`;
|
|
2783
|
+
}).join("")}</div>`;
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2631
2786
|
async function renderSettings() {
|
|
2632
2787
|
const records = (await apiPage(`/api/works/${state.work.id}/settings`)).items;
|
|
2633
2788
|
state.settings = records;
|
|
2634
|
-
|
|
2635
|
-
|
|
2636
|
-
|
|
2637
|
-
<div class="card-actions">${item.status === "pending" ? `<button data-setting-status="confirmed" data-setting-id="${esc(item.id)}">确认候选</button><button data-setting-status="deprecated" data-setting-id="${esc(item.id)}">弃用</button>` : ""}<button data-edit-setting="${esc(item.id)}">编辑</button><button data-entity-history="setting" data-entity-id="${esc(item.id)}" data-entity-title="${esc(item.title)}">版本历史</button></div></article>`).join("")}</div>`
|
|
2789
|
+
const layout = readModuleLayout();
|
|
2790
|
+
$("#module-content").innerHTML = records.length
|
|
2791
|
+
? `${renderModuleLayoutToggle(layout, "设定列表样式")}${layout === "rows" ? renderSettingRows(records) : renderSettingCards(records)}`
|
|
2638
2792
|
: emptyModule("还没有世界观设定", "新建规则、地点、组织、科技或创作约束。AI 提取的候选也会进入这里。");
|
|
2793
|
+
bindModuleLayoutToggle(renderSettings);
|
|
2639
2794
|
$("#module-content").querySelectorAll("[data-setting-status]").forEach((button) => button.addEventListener("click", async () => {
|
|
2640
2795
|
await api(`/api/settings/${button.dataset.settingId}`, { method: "PATCH", body: { status: button.dataset.settingStatus, changeNote: button.dataset.settingStatus === "confirmed" ? "确认 AI 设定候选" : "弃用 AI 设定候选" } });
|
|
2641
2796
|
await renderSettings();
|
|
@@ -2651,8 +2806,9 @@ async function renderCharacters() {
|
|
|
2651
2806
|
canReadModule("races") ? apiAllPages(`/api/works/${state.work.id}/races`) : Promise.resolve([]),
|
|
2652
2807
|
canReadModule("organizations") ? apiAllPages(`/api/works/${state.work.id}/organizations`) : Promise.resolve([])
|
|
2653
2808
|
]);
|
|
2654
|
-
const
|
|
2655
|
-
$("
|
|
2809
|
+
const layout = readModuleLayout();
|
|
2810
|
+
const characterActions = (item) => `<button data-edit-character="${esc(item.id)}">编辑</button>${canEditModule("characters") && state.characters.length > 1 ? `<button data-merge-character="${esc(item.id)}">合并</button>` : ""}${canEditModule("characters") ? `<button class="danger-button" data-delete-character="${esc(item.id)}">删除</button>` : ""}`;
|
|
2811
|
+
const characterCards = () => `<div class="card-grid">${state.characters.map((item) => {
|
|
2656
2812
|
const details = normalizeCharacterDetails(item.attributes?.details);
|
|
2657
2813
|
return `
|
|
2658
2814
|
<article class="record-card character-card" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}"><small>${item.lockedFields.length ? `锁定 ${item.lockedFields.length} 项` : esc(item.visibility)}</small>
|
|
@@ -2663,9 +2819,29 @@ async function renderCharacters() {
|
|
|
2663
2819
|
<div class="organization-links"><b>所属组织</b>${(item.organizations ?? []).length ? item.organizations.map((organization) => `<span class="pill organization-pill">${esc(organization.name)}</span>`).join("") : '<span class="organization-empty">未加入组织</span>'}</div>
|
|
2664
2820
|
${item.profile?.summary ? `<p class="character-summary">${esc(item.profile.summary)}</p>` : `<p>${esc(Object.entries(item.currentState).map(([key, value]) => `${key}:${value}`).join("\n") || "尚未记录当前状态")}</p>`}
|
|
2665
2821
|
${item.profileSectionCount ? `<small class="character-section-count">${item.profileSectionCount} 个设定章节</small>` : ""}
|
|
2666
|
-
<div class="card-actions"
|
|
2667
|
-
}).join("")}</div
|
|
2822
|
+
<div class="card-actions">${characterActions(item)}</div></article>`;
|
|
2823
|
+
}).join("")}</div>`;
|
|
2824
|
+
const characterRows = () => `<div class="module-row-list">${state.characters.map((item) => {
|
|
2825
|
+
const preview = moduleRowPreview(item.profile?.summary || item.attributes?.identity || Object.entries(item.currentState).map(([key, value]) => `${key}:${value}`).join(" ") || "尚未记录当前状态");
|
|
2826
|
+
const meta = [
|
|
2827
|
+
item.species ? (racePathLabel(item.race) || item.species) : "",
|
|
2828
|
+
...(item.aliases ?? []).slice(0, 3),
|
|
2829
|
+
(item.organizations ?? []).length ? (item.organizations ?? []).map((organization) => organization.name).join("、") : ""
|
|
2830
|
+
].filter(Boolean).join(" · ");
|
|
2831
|
+
const line = meta ? `${meta} · ${preview}` : preview;
|
|
2832
|
+
return `
|
|
2833
|
+
<article class="record-card module-row character-card" data-open-character="${esc(item.id)}" role="button" tabindex="0" aria-label="查看角色 ${esc(item.name)}">
|
|
2834
|
+
<small>${item.lockedFields.length ? `锁定 ${item.lockedFields.length} 项` : esc(item.visibility)}</small>
|
|
2835
|
+
<h3>${esc(item.name)}</h3>
|
|
2836
|
+
<p class="module-row-preview" title="${esc(line)}">${esc(line)}</p>
|
|
2837
|
+
<div class="card-actions">${characterActions(item)}</div>
|
|
2838
|
+
</article>`;
|
|
2839
|
+
}).join("")}</div>`;
|
|
2840
|
+
const auditPanel = canEditModule("tasks") ? `<section class="character-audit-panel"><div><strong>角色身份确认</strong><small>让 AI 查询角色档案并搜索正文,找出可能被误建成两个档案的同一角色。AI 只提交审核建议,不会自动合并。</small></div><button id="create-character-audit-task" class="ghost-button" type="button" ${state.characters.length < 2 ? "disabled" : ""}>AI 角色查重</button></section>` : "";
|
|
2841
|
+
$("#module-content").innerHTML = auditPanel + (state.characters.length
|
|
2842
|
+
? `${renderModuleLayoutToggle(layout, "角色列表样式")}${layout === "rows" ? characterRows() : characterCards()}`
|
|
2668
2843
|
: emptyModule("还没有角色档案", "创建主要人物,并维护别名、身份、动机和当前状态。"));
|
|
2844
|
+
bindModuleLayoutToggle(renderCharacters);
|
|
2669
2845
|
$("#create-character-audit-task")?.addEventListener("click", async () => {
|
|
2670
2846
|
const button = $("#create-character-audit-task");
|
|
2671
2847
|
button.disabled = true;
|
|
@@ -2723,20 +2899,36 @@ async function renderRaces() {
|
|
|
2723
2899
|
apiAllPages(`/api/works/${state.work.id}/races`),
|
|
2724
2900
|
canReadModule("characters") ? apiAllPages(`/api/works/${state.work.id}/characters`) : Promise.resolve([])
|
|
2725
2901
|
]);
|
|
2902
|
+
const layout = readModuleLayout();
|
|
2903
|
+
const raceActions = (item) => `<button data-edit-race="${esc(item.id)}">编辑</button><button data-entity-history="race" data-entity-id="${esc(item.id)}" data-entity-title="${esc(item.name)}">版本历史</button>${canEditModule("races") && state.races.length > 1 ? `<button data-merge-race="${esc(item.id)}">合并</button>` : ""}${canEditModule("races") ? `<button class="danger-button" data-delete-race="${esc(item.id)}">删除</button>` : ""}`;
|
|
2726
2904
|
const renderRaceNode = (item) => `<details class="race-tree-node" open data-race-node="${esc(item.id)}">
|
|
2727
2905
|
<summary><span>${esc(item.name)}</span><small>${item.children.length} 个直接子种族</small></summary>
|
|
2728
2906
|
<div class="race-tree-branch">
|
|
2729
|
-
<article class="record-card race-card"><small>${item.memberIds.length} 位直接角色 · ${item.settings.length}
|
|
2907
|
+
<article class="record-card race-card"><small>${item.memberIds.length} 位直接角色 · ${item.settings.length ? "已填写共同设定" : "暂无共同设定"}</small>
|
|
2730
2908
|
<div class="race-path" aria-label="种族路径">${esc(racePathLabel(item))}</div>
|
|
2731
2909
|
<p>${esc(item.description || "尚未填写种族简介")}</p>
|
|
2732
|
-
<div class="race-settings">${item.effectiveSettings.length ? item.effectiveSettings.map((setting) => `<
|
|
2910
|
+
<div class="race-settings">${item.effectiveSettings.length ? item.effectiveSettings.map((setting) => `<section class="knowledge-markdown-block${setting.inherited ? " inherited" : ""}"><div class="knowledge-markdown-block-heading"><h4>${esc(setting.title || "未命名章节")}</h4><small>${esc(setting.inherited ? `继承自 ${setting.sourceRaceName}` : `定义于 ${setting.sourceRaceName}`)}</small></div><div class="message-body">${renderMarkdown(setting.value) || '<p class="markdown-editor-empty">暂无内容</p>'}</div></section>`).join("") : '<span class="pill">暂无共同设定</span>'}</div>
|
|
2733
2911
|
<p class="race-members">直接角色:${item.members.length ? item.members.map((member) => esc(member.name)).join("、") : "暂无绑定角色"}</p>
|
|
2734
|
-
<div class="card-actions"
|
|
2912
|
+
<div class="card-actions">${raceActions(item)}</div>
|
|
2735
2913
|
</article>
|
|
2736
2914
|
${item.children.length ? `<div class="race-tree-children">${item.children.map(renderRaceNode).join("")}</div>` : ""}
|
|
2737
2915
|
</div>
|
|
2738
2916
|
</details>`;
|
|
2739
|
-
|
|
2917
|
+
const raceRows = () => `<div class="module-row-list">${state.races.map((item) => {
|
|
2918
|
+
const preview = moduleRowPreview(item.description || "尚未填写种族简介");
|
|
2919
|
+
const meta = `${item.memberIds.length} 位直接角色 · ${item.settings.length ? "已填写共同设定" : "暂无共同设定"}`;
|
|
2920
|
+
return `
|
|
2921
|
+
<article class="record-card module-row race-card">
|
|
2922
|
+
<small>${esc(meta)}</small>
|
|
2923
|
+
<h3>${esc(item.name)}<span class="module-row-path">${esc(racePathLabel(item))}</span></h3>
|
|
2924
|
+
<p class="module-row-preview" title="${esc(preview)}">${esc(preview)}${item.members.length ? ` · ${esc(item.members.map((member) => member.name).join("、"))}` : ""}</p>
|
|
2925
|
+
<div class="card-actions">${raceActions(item)}</div>
|
|
2926
|
+
</article>`;
|
|
2927
|
+
}).join("")}</div>`;
|
|
2928
|
+
$("#module-content").innerHTML = state.races.length
|
|
2929
|
+
? `${renderModuleLayoutToggle(layout, "种族列表样式")}${layout === "rows" ? raceRows() : `<section class="race-tree" aria-label="种族层级">${buildRaceForest(state.races).map(renderRaceNode).join("")}</section>`}`
|
|
2930
|
+
: emptyModule("还没有种族档案", "先创建种族及共同设定,之后角色编辑器才能选择该种族。");
|
|
2931
|
+
bindModuleLayoutToggle(renderRaces);
|
|
2740
2932
|
$("#module-content").querySelectorAll("[data-edit-race]").forEach((button) => button.addEventListener("click", () => openRaceDialog(state.races.find((item) => item.id === button.dataset.editRace))));
|
|
2741
2933
|
$("#module-content").querySelectorAll("[data-merge-race]").forEach((button) => button.addEventListener("click", () => {
|
|
2742
2934
|
const source = state.races.find((item) => item.id === button.dataset.mergeRace);
|
|
@@ -2770,13 +2962,30 @@ async function renderOrganizations() {
|
|
|
2770
2962
|
apiAllPages(`/api/works/${state.work.id}/organizations`),
|
|
2771
2963
|
canReadModule("characters") ? apiAllPages(`/api/works/${state.work.id}/characters`) : Promise.resolve([])
|
|
2772
2964
|
]);
|
|
2773
|
-
|
|
2774
|
-
|
|
2965
|
+
const layout = readModuleLayout();
|
|
2966
|
+
const organizationActions = (item) => `<button data-edit-organization="${esc(item.id)}">编辑</button><button data-entity-history="organization" data-entity-id="${esc(item.id)}" data-entity-title="${esc(item.name)}">版本历史</button>${canEditModule("organizations") && state.organizations.length > 1 ? `<button data-merge-organization="${esc(item.id)}">合并</button>` : ""}${canEditModule("organizations") ? `<button class="danger-button" data-delete-organization="${esc(item.id)}">删除</button>` : ""}`;
|
|
2967
|
+
const organizationCards = () => `<div class="card-grid organization-grid">${state.organizations.map((item) => `
|
|
2968
|
+
<article class="record-card organization-card"><small>${item.memberIds.length} 位成员 · ${item.settings.length ? "已填写组织设定" : "暂无组织设定"}</small>
|
|
2775
2969
|
<h3>${esc(item.name)}</h3><p>${esc(item.description || "尚未填写组织简介")}</p>
|
|
2776
|
-
<div class="organization-settings">${item.
|
|
2970
|
+
<div class="organization-settings">${item.settingsSections?.length ? item.settingsSections.map((section) => `<article class="knowledge-markdown-block"><div class="knowledge-markdown-block-heading"><h4>${esc(section.title || "未命名章节")}</h4></div><div class="message-body">${renderMarkdown(section.contentMarkdown) || '<p class="markdown-editor-empty">暂无内容</p>'}</div></article>`).join("") : '<span class="pill">暂无组织设定</span>'}</div>
|
|
2777
2971
|
<p class="organization-members">成员:${item.members.length ? item.members.map((member) => esc(member.name)).join("、") : "暂无绑定角色"}</p>
|
|
2778
|
-
<div class="card-actions"
|
|
2779
|
-
</article>`).join("")}</div
|
|
2972
|
+
<div class="card-actions">${organizationActions(item)}</div>
|
|
2973
|
+
</article>`).join("")}</div>`;
|
|
2974
|
+
const organizationRows = () => `<div class="module-row-list">${state.organizations.map((item) => {
|
|
2975
|
+
const preview = moduleRowPreview(item.description || "尚未填写组织简介");
|
|
2976
|
+
const members = item.members.length ? item.members.map((member) => member.name).join("、") : "暂无绑定角色";
|
|
2977
|
+
return `
|
|
2978
|
+
<article class="record-card module-row organization-card">
|
|
2979
|
+
<small>${item.memberIds.length} 位成员 · ${item.settings.length ? "已填写组织设定" : "暂无组织设定"}</small>
|
|
2980
|
+
<h3>${esc(item.name)}</h3>
|
|
2981
|
+
<p class="module-row-preview" title="${esc(`${preview} · 成员:${members}`)}">${esc(preview)} · ${esc(members)}</p>
|
|
2982
|
+
<div class="card-actions">${organizationActions(item)}</div>
|
|
2983
|
+
</article>`;
|
|
2984
|
+
}).join("")}</div>`;
|
|
2985
|
+
$("#module-content").innerHTML = state.organizations.length
|
|
2986
|
+
? `${renderModuleLayoutToggle(layout, "组织列表样式")}${layout === "rows" ? organizationRows() : organizationCards()}`
|
|
2987
|
+
: emptyModule("还没有组织", "创建国家、机构、阵营或团队,并维护组织设定与成员。");
|
|
2988
|
+
bindModuleLayoutToggle(renderOrganizations);
|
|
2780
2989
|
$("#module-content").querySelectorAll("[data-edit-organization]").forEach((button) => button.addEventListener("click", () => openOrganizationDialog(state.organizations.find((item) => item.id === button.dataset.editOrganization))));
|
|
2781
2990
|
$("#module-content").querySelectorAll("[data-merge-organization]").forEach((button) => button.addEventListener("click", () => {
|
|
2782
2991
|
const source = state.organizations.find((item) => item.id === button.dataset.mergeOrganization);
|
|
@@ -2844,17 +3053,35 @@ async function renderOutlines() {
|
|
|
2844
3053
|
apiPage(`/api/works/${state.work.id}/outlines`).then((result) => result.items),
|
|
2845
3054
|
apiPage(`/api/works/${state.work.id}/foreshadows?status=all${currentChapterId ? `¤tChapterId=${encodeURIComponent(currentChapterId)}` : ""}`).then((result) => result.items)
|
|
2846
3055
|
]);
|
|
3056
|
+
const layout = readModuleLayout();
|
|
2847
3057
|
const unresolved = foreshadows.filter((item) => item.unresolved);
|
|
2848
3058
|
const overdue = unresolved.filter((item) => item.overdue);
|
|
2849
3059
|
const navButton = $("#module-nav [data-module=outlines] .nav-label");
|
|
2850
3060
|
if (navButton) navButton.textContent = unresolved.length ? `大纲与伏笔 · ${unresolved.length}` : "大纲与伏笔";
|
|
2851
|
-
const
|
|
3061
|
+
const foreshadowActions = (item) => `<button data-edit-foreshadow="${esc(item.id)}">编辑伏笔</button><button data-entity-history="foreshadow" data-entity-id="${esc(item.id)}" data-entity-title="${esc(item.title)}">版本历史</button>`;
|
|
3062
|
+
const foreshadowCards = () => `<div class="card-grid foreshadow-grid">${foreshadows.map((item) => `
|
|
2852
3063
|
<article class="record-card foreshadow-card ${item.overdue ? "is-overdue" : ""}">
|
|
2853
3064
|
<small>${esc(item.importance)} · ${esc(item.status)}${item.overdue ? " · 已逾期" : ""}</small>
|
|
2854
3065
|
<h3>${esc(item.title)}</h3><p>${esc(item.description || "暂无说明")}</p>
|
|
2855
3066
|
<div class="foreshadow-links">${item.occurrences.length ? item.occurrences.map((link) => `<span class="pill">${esc({ setup: "埋设", reminder: "提醒", payoff: "回收" }[link.role] ?? link.role)} · ${esc(link.volumeTitle)} / ${esc(link.chapterTitle)}</span>`).join("") : '<span class="pill">尚未关联章节</span>'}</div>
|
|
2856
|
-
<div class="card-actions"
|
|
2857
|
-
</article>`).join("")}</div
|
|
3067
|
+
<div class="card-actions">${foreshadowActions(item)}</div>
|
|
3068
|
+
</article>`).join("")}</div>`;
|
|
3069
|
+
const foreshadowRows = () => `<div class="module-row-list">${foreshadows.map((item) => {
|
|
3070
|
+
const preview = moduleRowPreview(item.description || "暂无说明");
|
|
3071
|
+
const links = item.occurrences.length
|
|
3072
|
+
? item.occurrences.map((link) => `${({ setup: "埋设", reminder: "提醒", payoff: "回收" }[link.role] ?? link.role)} · ${link.volumeTitle} / ${link.chapterTitle}`).join(";")
|
|
3073
|
+
: "尚未关联章节";
|
|
3074
|
+
return `
|
|
3075
|
+
<article class="record-card module-row foreshadow-card ${item.overdue ? "is-overdue" : ""}">
|
|
3076
|
+
<small>${esc(item.importance)} · ${esc(item.status)}${item.overdue ? " · 已逾期" : ""}</small>
|
|
3077
|
+
<h3>${esc(item.title)}</h3>
|
|
3078
|
+
<p class="module-row-preview" title="${esc(`${preview} · ${links}`)}">${esc(preview)} · ${esc(links)}</p>
|
|
3079
|
+
<div class="card-actions">${foreshadowActions(item)}</div>
|
|
3080
|
+
</article>`;
|
|
3081
|
+
}).join("")}</div>`;
|
|
3082
|
+
const foreshadowHtml = foreshadows.length
|
|
3083
|
+
? `${renderModuleLayoutToggle(layout, "伏笔列表样式")}${layout === "rows" ? foreshadowRows() : foreshadowCards()}`
|
|
3084
|
+
: emptyModule("还没有伏笔", "创建伏笔并关联埋设、提醒与回收章节,未回收项会持续显示。\n");
|
|
2858
3085
|
const outlineHtml = outlines.length ? `<div class="outline-list">${outlines.map((item) => `
|
|
2859
3086
|
<article class="outline-row ${item.status === "completed" ? "is-complete" : ""}">
|
|
2860
3087
|
<div><small>${esc(item.volumeTitle)} · ${esc(item.status)}</small><h3>${esc(item.chapterTitle)}</h3></div>
|
|
@@ -2864,6 +3091,7 @@ async function renderOutlines() {
|
|
|
2864
3091
|
<div class="outline-actions">${item.unresolvedForeshadowCount ? `<span>${item.unresolvedForeshadowCount} 个未回收伏笔</span>` : ""}<button data-edit-outline="${esc(item.chapterId)}">编辑</button><button data-entity-history="chapter-outline" data-entity-id="${esc(item.chapterId)}" data-entity-title="${esc(item.chapterTitle)}">版本历史</button></div>
|
|
2865
3092
|
</article>`).join("")}</div>` : emptyModule("还没有章节", "先创建章节,再为每章维护目标、冲突和转折。\n");
|
|
2866
3093
|
$("#module-content").innerHTML = `<div class="outline-summary"><article><strong>${outlines.length}</strong><span>章节规划</span></article><article><strong>${unresolved.length}</strong><span>未回收伏笔</span></article><article class="${overdue.length ? "danger-text" : ""}"><strong>${overdue.length}</strong><span>已逾期</span></article></div><section class="planning-section"><div class="section-title"><div><span class="eyebrow">伏笔追踪</span><h2>尚未回收与历史伏笔</h2></div></div>${foreshadowHtml}</section><section class="planning-section"><div class="section-title"><div><span class="eyebrow">逐章规划</span><h2>章节目标、冲突与转折</h2></div></div>${outlineHtml}</section>`;
|
|
3094
|
+
bindModuleLayoutToggle(renderOutlines);
|
|
2867
3095
|
$("#module-content").querySelectorAll("[data-edit-outline]").forEach((button) => button.addEventListener("click", () => openOutlineDialog(outlines.find((item) => item.chapterId === button.dataset.editOutline))));
|
|
2868
3096
|
$("#module-content").querySelectorAll("[data-edit-foreshadow]").forEach((button) => button.addEventListener("click", () => openForeshadowDialog(foreshadows.find((item) => item.id === button.dataset.editForeshadow))));
|
|
2869
3097
|
bindEntityHistoryButtons(renderOutlines);
|
|
@@ -2926,11 +3154,28 @@ async function renderReviews() {
|
|
|
2926
3154
|
: "";
|
|
2927
3155
|
return `<article class="record-card character-duplicate-review"><small>角色查重 · ${esc(item.severity)} · ${esc(item.status)}</small><h3>${esc(item.title)}</h3><div class="character-duplicate-pair">${sideHtml}</div><p>${esc(item.description)}${item.suggestion ? `\n建议:${esc(item.suggestion)}` : ""}</p>${evidenceHtml ? `<ul class="character-duplicate-evidence">${evidenceHtml}</ul>` : ""}${actions}${item.resolutionNote ? `<p class="review-resolution-note">处理结果:${esc(item.resolutionNote)}</p>` : ""}</article>`;
|
|
2928
3156
|
};
|
|
2929
|
-
|
|
3157
|
+
const layout = readModuleLayout();
|
|
3158
|
+
const reviewCard = (item) => item.itemType === "character-duplicate" ? duplicateCard(item) : `
|
|
2930
3159
|
<article class="record-card"><small>${esc(item.itemType)} · ${esc(item.severity)} · ${esc(item.status)}</small><h3>${esc(item.title)}</h3>
|
|
2931
3160
|
<p>${esc(item.description)}${item.suggestion ? `\n建议:${esc(item.suggestion)}` : ""}</p>
|
|
2932
|
-
${item.status === "pending" && canResolveReview ? `<div class="card-actions"><button data-review-status="fixed" data-review-id="${esc(item.id)}">标为已修复</button><button data-review-status="ignored" data-review-id="${esc(item.id)}">忽略</button></div>` : ""}</article
|
|
3161
|
+
${item.status === "pending" && canResolveReview ? `<div class="card-actions"><button data-review-status="fixed" data-review-id="${esc(item.id)}">标为已修复</button><button data-review-status="ignored" data-review-id="${esc(item.id)}">忽略</button></div>` : ""}</article>`;
|
|
3162
|
+
const reviewRow = (item) => {
|
|
3163
|
+
if (item.itemType === "character-duplicate") {
|
|
3164
|
+
return `<div class="module-row-span">${duplicateCard(item)}</div>`;
|
|
3165
|
+
}
|
|
3166
|
+
const preview = moduleRowPreview(`${item.description || ""}${item.suggestion ? ` 建议:${item.suggestion}` : ""}`);
|
|
3167
|
+
return `
|
|
3168
|
+
<article class="record-card module-row">
|
|
3169
|
+
<small>${esc(item.itemType)} · ${esc(item.severity)} · ${esc(item.status)}</small>
|
|
3170
|
+
<h3>${esc(item.title)}</h3>
|
|
3171
|
+
<p class="module-row-preview" title="${esc(preview)}">${esc(preview)}</p>
|
|
3172
|
+
<div class="card-actions">${item.status === "pending" && canResolveReview ? `<button data-review-status="fixed" data-review-id="${esc(item.id)}">标为已修复</button><button data-review-status="ignored" data-review-id="${esc(item.id)}">忽略</button>` : ""}</div>
|
|
3173
|
+
</article>`;
|
|
3174
|
+
};
|
|
3175
|
+
$("#module-content").innerHTML = reviews.length
|
|
3176
|
+
? `${renderModuleLayoutToggle(layout, "审核列表样式")}${layout === "rows" ? `<div class="module-row-list">${reviews.map(reviewRow).join("")}</div>` : `<div class="card-grid">${reviews.map(reviewCard).join("")}</div>`}`
|
|
2933
3177
|
: emptyModule("没有待审核事项", "候选设定、冲突与低置信度结论会集中显示在这里。");
|
|
3178
|
+
bindModuleLayoutToggle(renderReviews);
|
|
2934
3179
|
$("#module-content").querySelectorAll("[data-review-id]").forEach((button) => button.addEventListener("click", async () => {
|
|
2935
3180
|
await api(`/api/reviews/${button.dataset.reviewId}`, { method: "PATCH", body: { status: button.dataset.reviewStatus } });
|
|
2936
3181
|
await renderReviews();
|
|
@@ -2938,7 +3183,10 @@ async function renderReviews() {
|
|
|
2938
3183
|
$("#module-content").querySelectorAll("[data-merge-review]").forEach((button) => button.addEventListener("click", async () => {
|
|
2939
3184
|
const target = characterById.get(button.dataset.mergeTarget);
|
|
2940
3185
|
const source = characterById.get(button.dataset.mergeSource);
|
|
2941
|
-
if (!target || !source || !
|
|
3186
|
+
if (!target || !source || !(await confirmToast(
|
|
3187
|
+
`确认把“${source.name}”合并到“${target.name}”?来源角色的别名、组织、时间线和关系会迁移到目标角色。`,
|
|
3188
|
+
{ title: "确认合并角色", confirmLabel: "确认合并" }
|
|
3189
|
+
))) return;
|
|
2942
3190
|
button.disabled = true;
|
|
2943
3191
|
try {
|
|
2944
3192
|
await api(`/api/reviews/${button.dataset.mergeReview}/character-resolution`, { method: "POST", body: {
|
|
@@ -2971,7 +3219,7 @@ async function renderReviews() {
|
|
|
2971
3219
|
|
|
2972
3220
|
async function renderTasks() {
|
|
2973
3221
|
const [tasks, settings] = await Promise.all([
|
|
2974
|
-
apiPage(`/api/works/${state.work.id}/tasks`).then((result) => result.items),
|
|
3222
|
+
apiPage(`/api/works/${state.work.id}/tasks?view=summary`).then((result) => result.items),
|
|
2975
3223
|
canReadModule("ai-settings")
|
|
2976
3224
|
? api(`/api/works/${state.work.id}/ai-settings`)
|
|
2977
3225
|
: Promise.resolve({ autoRunEnabled: false, autoRunConcurrency: 2, autoRunBatchLimit: 20 })
|
|
@@ -3042,9 +3290,13 @@ async function renderTasks() {
|
|
|
3042
3290
|
}
|
|
3043
3291
|
});
|
|
3044
3292
|
|
|
3045
|
-
const taskById = new Map(tasks.map((item) => [item.id, item]));
|
|
3046
3293
|
$("#module-content").querySelectorAll("[data-task-detail]").forEach((button) => button.addEventListener("click", () => {
|
|
3047
|
-
|
|
3294
|
+
if (button.disabled) return;
|
|
3295
|
+
button.disabled = true;
|
|
3296
|
+
api(`/api/tasks/${encodeURIComponent(button.dataset.taskDetail)}`)
|
|
3297
|
+
.then((task) => openTaskDetailDialog(task))
|
|
3298
|
+
.catch((error) => toast(error.message, "error"))
|
|
3299
|
+
.finally(() => { button.disabled = false; });
|
|
3048
3300
|
}));
|
|
3049
3301
|
$("#module-content").querySelectorAll("[data-run-task]").forEach((button) => button.addEventListener("click", async () => {
|
|
3050
3302
|
const workId = state.work.id;
|
|
@@ -3307,17 +3559,19 @@ let aiContextUsageTimer = null;
|
|
|
3307
3559
|
let aiContextUsageRequest = 0;
|
|
3308
3560
|
|
|
3309
3561
|
function currentAiRequestScope() {
|
|
3310
|
-
if (!state.work
|
|
3562
|
+
if (!state.work) return null;
|
|
3311
3563
|
const taskType = $("#ai-task").value;
|
|
3312
3564
|
const scopeType = $("#ai-scope").value;
|
|
3313
|
-
const
|
|
3314
|
-
|
|
3565
|
+
const requiresChapter = taskType === "polish" || taskType === "continue" || scopeType !== "none";
|
|
3566
|
+
if (requiresChapter && !state.chapter) return null;
|
|
3567
|
+
const selection = state.chapter ? $("#chapter-content").value.slice($("#chapter-content").selectionStart, $("#chapter-content").selectionEnd) : "";
|
|
3568
|
+
const volume = state.chapter ? state.work.volumes.find((item) => item.id === state.chapter.volumeId) : null;
|
|
3315
3569
|
const includeBookSummary = scopeType === "chapter-summary";
|
|
3316
|
-
const scope = taskType === "polish" ? { type: "chapter", chapterId: state.chapter
|
|
3317
|
-
: scopeType === "none" ? { type: "none", ...(taskType === "continue" ? { chapterId: state.chapter.id } : {}) }
|
|
3570
|
+
const scope = taskType === "polish" ? { type: "chapter", chapterId: state.chapter?.id, selection }
|
|
3571
|
+
: scopeType === "none" ? { type: "none", ...(taskType === "continue" && state.chapter ? { chapterId: state.chapter.id } : {}) }
|
|
3318
3572
|
: scopeType === "book" ? { type: "book" }
|
|
3319
3573
|
: scopeType === "volume" ? { type: "volume", volumeId: volume?.id }
|
|
3320
|
-
: { type: "chapter", chapterId: state.chapter
|
|
3574
|
+
: { type: "chapter", chapterId: state.chapter?.id };
|
|
3321
3575
|
Object.assign(scope, buildAiReferenceScope(state.aiReferences));
|
|
3322
3576
|
if (includeBookSummary) scope.includeBookSummary = true;
|
|
3323
3577
|
return { taskType, scope, selection };
|
|
@@ -3442,6 +3696,7 @@ async function ensureAiReferencesLoaded() {
|
|
|
3442
3696
|
|
|
3443
3697
|
function field(name, label, type = "text", value = "", options = []) {
|
|
3444
3698
|
if (type === "textarea") return `<label>${esc(label)}<textarea name="${esc(name)}">${esc(value)}</textarea></label>`;
|
|
3699
|
+
if (type === "markdown") return `<div class="form-field markdown-editor-field" data-vditor-editor-field><div class="vditor-editor-host" data-vditor-editor data-placeholder="${esc(options.placeholder ?? `在这里编辑${label}`)}" aria-label="Markdown 编辑器"></div><textarea class="hidden" name="${esc(name)}" data-vditor-value maxlength="200000" aria-label="Markdown 原文">${esc(value)}</textarea></div>`;
|
|
3445
3700
|
if (type === "item-list") {
|
|
3446
3701
|
const values = Array.isArray(value) && value.length ? value : [""];
|
|
3447
3702
|
return `<div class="form-field item-list-field"><span>${esc(label)}</span><div class="item-list-rows" data-item-list-rows data-name="${esc(name)}" data-label="${esc(label)}">${values.map((item) => `<div class="item-list-row"><input name="${esc(name)}" value="${esc(item)}" aria-label="${esc(label)}"><button type="button" data-item-list-remove aria-label="删除此条">删除</button></div>`).join("")}</div><button class="item-list-add" type="button" data-item-list-add>添加一条</button></div>`;
|
|
@@ -3483,6 +3738,52 @@ function field(name, label, type = "text", value = "", options = []) {
|
|
|
3483
3738
|
return `<label>${esc(label)}<input name="${esc(name)}" type="${esc(type)}" value="${esc(value)}" ${type === "password" ? 'autocomplete="new-password"' : ""} ${type === "number" ? 'step="any"' : ""}></label>`;
|
|
3484
3739
|
}
|
|
3485
3740
|
|
|
3741
|
+
function knowledgeSectionTitleFromMarkdown(content, index) {
|
|
3742
|
+
return String(content).match(/^\s{0,3}#{1,6}\s+(.+?)\s*#*\s*$/mu)?.[1]?.trim() || `设定 ${index + 1}`;
|
|
3743
|
+
}
|
|
3744
|
+
|
|
3745
|
+
function normalizeKnowledgeEditorSections(item) {
|
|
3746
|
+
const sections = Array.isArray(item?.settingsSections)
|
|
3747
|
+
? item.settingsSections.map((section, index) => ({
|
|
3748
|
+
title: String(section.title ?? "").trim() || knowledgeSectionTitleFromMarkdown(section.contentMarkdown ?? "", index),
|
|
3749
|
+
contentMarkdown: String(section.contentMarkdown ?? section.content ?? ""),
|
|
3750
|
+
summary: String(section.summary ?? ""),
|
|
3751
|
+
sortOrder: Number.isFinite(Number(section.sortOrder)) ? Number(section.sortOrder) : index
|
|
3752
|
+
}))
|
|
3753
|
+
: [];
|
|
3754
|
+
if (sections.length > 0) return sections;
|
|
3755
|
+
const legacy = String(item?.settingsMarkdown ?? (Array.isArray(item?.settings) ? item.settings.join("\n\n") : ""));
|
|
3756
|
+
return legacy.trim() ? [{ title: knowledgeSectionTitleFromMarkdown(legacy, 0), contentMarkdown: legacy, summary: "", sortOrder: 0 }] : [];
|
|
3757
|
+
}
|
|
3758
|
+
|
|
3759
|
+
function knowledgeSectionPreviewText(section) {
|
|
3760
|
+
const content = String(section.contentMarkdown ?? "").replace(/!\[[^\]]*\]\([^)]*\)/gu, "图片").replace(/[`*_>#-]/gu, "").replace(/\s+/gu, " ").trim();
|
|
3761
|
+
return content ? `${content.slice(0, 180)}${content.length > 180 ? "…" : ""}` : "暂无正文内容";
|
|
3762
|
+
}
|
|
3763
|
+
|
|
3764
|
+
function renderKnowledgeMarkdownSections() {
|
|
3765
|
+
const host = $("#knowledge-markdown-sections");
|
|
3766
|
+
if (!host) return;
|
|
3767
|
+
const label = knowledgeEditorKind === "race" ? "种族" : "组织";
|
|
3768
|
+
const canEdit = canEditModule(knowledgeEditorKind === "race" ? "races" : "organizations");
|
|
3769
|
+
const sections = Array.isArray(knowledgeEditorSections) ? knowledgeEditorSections : [];
|
|
3770
|
+
host.innerHTML = `<div class="knowledge-markdown-list-toolbar"><div><b>${label} Markdown 设定</b><span>将每条设定单独保存为章节,需要编辑时打开大编辑器。</span></div>${canEdit ? '<button type="button" class="ghost-button" data-knowledge-section-create>新建设定</button>' : ""}</div>${sections.length ? `<div class="knowledge-markdown-list">${sections.map((section, index) => `<article class="knowledge-markdown-section" data-knowledge-section-index="${index}"><header><div><span>设定 ${index + 1}</span><h4>${esc(section.title || `未命名设定 ${index + 1}`)}</h4>${section.summary ? `<p>${esc(section.summary)}</p>` : ""}</div><div>${canEdit ? `<button type="button" data-knowledge-section-edit="${index}">编辑</button><button type="button" data-knowledge-section-delete="${index}">删除</button>` : ""}</div></header><p class="knowledge-section-card-preview">${esc(knowledgeSectionPreviewText(section))}</p></article>`).join("")}</div>` : '<p class="knowledge-markdown-empty">还没有 Markdown 设定,点击“新建设定”开始记录。</p>'}`;
|
|
3771
|
+
host.querySelector("[data-knowledge-section-create]")?.addEventListener("click", () => void openKnowledgeSectionEditor());
|
|
3772
|
+
host.querySelectorAll("[data-knowledge-section-edit]").forEach((button) => button.addEventListener("click", () => void openKnowledgeSectionEditor(Number(button.dataset.knowledgeSectionEdit))));
|
|
3773
|
+
host.querySelectorAll("[data-knowledge-section-delete]").forEach((button) => button.addEventListener("click", async () => {
|
|
3774
|
+
const index = Number(button.dataset.knowledgeSectionDelete);
|
|
3775
|
+
const section = knowledgeEditorSections[index];
|
|
3776
|
+
if (!section || !(await confirmToast(
|
|
3777
|
+
`确定删除“${section.title || `设定 ${index + 1}`}”吗?`,
|
|
3778
|
+
{ title: "删除设定", confirmLabel: "确认删除" }
|
|
3779
|
+
))) return;
|
|
3780
|
+
knowledgeEditorSections.splice(index, 1);
|
|
3781
|
+
knowledgeEditorSections.forEach((item, sortOrder) => { item.sortOrder = sortOrder; });
|
|
3782
|
+
markEntityEditorDirty();
|
|
3783
|
+
renderKnowledgeMarkdownSections();
|
|
3784
|
+
}));
|
|
3785
|
+
}
|
|
3786
|
+
|
|
3486
3787
|
function bindDynamicListControls(container) {
|
|
3487
3788
|
container.querySelectorAll("[data-item-list-add]").forEach((button) => button.addEventListener("click", () => {
|
|
3488
3789
|
const rows = button.previousElementSibling;
|
|
@@ -3565,6 +3866,7 @@ function commitRelationshipKeywordInputs(container) {
|
|
|
3565
3866
|
}
|
|
3566
3867
|
|
|
3567
3868
|
function openDialog(title, fields, onSubmit, eyebrow = "新增", options = {}) {
|
|
3869
|
+
void discardPendingMarkdownAttachments();
|
|
3568
3870
|
$("#dialog-title").textContent = title;
|
|
3569
3871
|
$("#dialog-eyebrow").textContent = eyebrow;
|
|
3570
3872
|
$("#dialog-fields").innerHTML = fields;
|
|
@@ -3572,15 +3874,21 @@ function openDialog(title, fields, onSubmit, eyebrow = "新增", options = {}) {
|
|
|
3572
3874
|
$("#form-dialog").classList.toggle("wide-dialog", Boolean(options.wide));
|
|
3573
3875
|
bindDynamicListControls($("#dialog-fields"));
|
|
3574
3876
|
bindRelationshipKeywordControls($("#dialog-fields"));
|
|
3877
|
+
bindVditorEditors($("#dialog-fields"));
|
|
3575
3878
|
const form = $("#dynamic-form");
|
|
3576
3879
|
form.onsubmit = async (event) => {
|
|
3577
|
-
if (event.submitter?.value === "cancel")
|
|
3880
|
+
if (event.submitter?.value === "cancel") {
|
|
3881
|
+
void discardPendingMarkdownAttachments();
|
|
3882
|
+
return;
|
|
3883
|
+
}
|
|
3578
3884
|
event.preventDefault();
|
|
3579
3885
|
const submit = $("#dialog-submit");
|
|
3580
3886
|
submit.disabled = true;
|
|
3581
3887
|
try {
|
|
3582
3888
|
commitRelationshipKeywordInputs(form);
|
|
3583
3889
|
await onSubmit(new FormData(form));
|
|
3890
|
+
const markdown = [...form.querySelectorAll("[data-vditor-value]")].map((textarea) => textarea.value).join("\n\n");
|
|
3891
|
+
await cleanupPendingMarkdownAttachments(markdown);
|
|
3584
3892
|
$("#form-dialog").close();
|
|
3585
3893
|
} catch (error) {
|
|
3586
3894
|
toast(error.message, "error");
|
|
@@ -3652,12 +3960,21 @@ function bindWorkCoverControls(work) {
|
|
|
3652
3960
|
function openWorkSettingsDialog(work) {
|
|
3653
3961
|
if (!work) return;
|
|
3654
3962
|
const canManageAccess = ["admin", "owner"].includes(String(work.accessRole));
|
|
3963
|
+
const isCurrentWork = state.work?.id === work.id;
|
|
3964
|
+
const canOpenImportHistory = isCurrentWork && canReplaceProse(work);
|
|
3965
|
+
const importHistoryAction = isCurrentWork
|
|
3966
|
+
? canOpenImportHistory ? "查看导入历史" : "需要完整编辑权限"
|
|
3967
|
+
: "打开作品后可用";
|
|
3655
3968
|
const accessField = `<section class="work-access-field" aria-labelledby="work-access-title">
|
|
3656
3969
|
<div><strong id="work-access-title">成员权限</strong><small>选择成员后,可为每个作品模块单独设置无权限、只读或可编辑。</small><div class="work-access-options" aria-label="成员权限配置方式"><span>按成员配置</span><span>按模块授权</span><span>读写分离</span></div></div>
|
|
3657
3970
|
${canManageAccess ? '<button id="work-access-manage" class="ghost-button" type="button">配置成员权限</button>' : '<small>仅作品创建者或系统管理员可以调整访问权限。</small>'}
|
|
3658
3971
|
</section>`;
|
|
3972
|
+
const importHistoryField = `<section class="work-access-field" aria-labelledby="import-history-settings-title">
|
|
3973
|
+
<div><strong id="import-history-settings-title">正文导入历史</strong><small>查看导入前快照并恢复被覆盖的分卷、章节标题和正文;大纲、伏笔等章节关联资料不在快照中。</small></div>
|
|
3974
|
+
<button id="import-history-button" class="ghost-button" type="button" aria-controls="import-history-dialog" aria-haspopup="dialog" ${canOpenImportHistory ? "" : "disabled"}>${importHistoryAction}</button>
|
|
3975
|
+
</section>`;
|
|
3659
3976
|
openDialog("作品信息",
|
|
3660
|
-
workCoverFieldHtml(work) + field("title", "作品名称", "text", work.title) + field("author", "作者", "text", work.author) + field("description", "简介", "textarea", work.description) + accessField,
|
|
3977
|
+
workCoverFieldHtml(work) + field("title", "作品名称", "text", work.title) + field("author", "作者", "text", work.author) + field("description", "简介", "textarea", work.description) + accessField + importHistoryField,
|
|
3661
3978
|
async (form) => {
|
|
3662
3979
|
await api(`/api/works/${work.id}`, { method: "PATCH", body: { title: form.get("title"), author: form.get("author"), description: form.get("description") } });
|
|
3663
3980
|
state.works = (await apiPage("/api/works")).items;
|
|
@@ -3675,13 +3992,17 @@ function openWorkSettingsDialog(work) {
|
|
|
3675
3992
|
toast("作品信息已保存");
|
|
3676
3993
|
}, "作品设置");
|
|
3677
3994
|
bindWorkCoverControls(work);
|
|
3995
|
+
$("#import-history-button")?.addEventListener("click", () => {
|
|
3996
|
+
$("#form-dialog").close();
|
|
3997
|
+
void openImportHistory();
|
|
3998
|
+
});
|
|
3678
3999
|
$("#work-access-manage")?.addEventListener("click", () => {
|
|
3679
4000
|
$("#form-dialog").close();
|
|
3680
4001
|
openMembersDialog(work);
|
|
3681
4002
|
});
|
|
3682
4003
|
}
|
|
3683
4004
|
|
|
3684
|
-
async function openChapterDialog() {
|
|
4005
|
+
async function openChapterDialog(volumeId = null) {
|
|
3685
4006
|
if (!state.work) return openWorkDialog();
|
|
3686
4007
|
if (!canEditProse()) return toast("当前权限只能编辑设定资料,正文为只读", "error");
|
|
3687
4008
|
if (!state.work.volumes.length) {
|
|
@@ -3689,7 +4010,8 @@ async function openChapterDialog() {
|
|
|
3689
4010
|
state.work = await api(`/api/works/${state.work.id}`);
|
|
3690
4011
|
renderTree();
|
|
3691
4012
|
}
|
|
3692
|
-
|
|
4013
|
+
const selectedVolumeId = state.work.volumes.some((volume) => volume.id === volumeId) ? volumeId : state.work.volumes[0].id;
|
|
4014
|
+
openDialog("新建章节", field("title", "章节标题") + field("volumeId", "所属卷", "select", selectedVolumeId, state.work.volumes.map((volume) => [volume.id, volume.title])) + field("chapterType", "章节类型", "select", "正文", chapterTypes.map((value) => [value, value])), async (form) => {
|
|
3693
4015
|
const chapter = await api(`/api/works/${state.work.id}/chapters`, { method: "POST", body: { title: form.get("title"), volumeId: form.get("volumeId"), chapterType: form.get("chapterType"), content: "" } });
|
|
3694
4016
|
state.work = await api(`/api/works/${state.work.id}`);
|
|
3695
4017
|
await selectChapter(chapter.id);
|
|
@@ -3704,13 +4026,13 @@ function openVolumeDialog(item) {
|
|
|
3704
4026
|
field("title", "分卷名称", "text", item?.title) +
|
|
3705
4027
|
field("kind", "分卷类型", "select", item?.kind ?? "main", kindOptions) +
|
|
3706
4028
|
field("description", "分卷简介", "textarea", item?.description) +
|
|
3707
|
-
field("keywords", "
|
|
4029
|
+
field("keywords", "分卷关键词", "keyword-chips", item?.keywords ?? []),
|
|
3708
4030
|
async (form) => {
|
|
3709
4031
|
const body = {
|
|
3710
4032
|
title: form.get("title"),
|
|
3711
4033
|
kind: form.get("kind"),
|
|
3712
4034
|
description: form.get("description"),
|
|
3713
|
-
keywords: form.getAll("keywords").map(
|
|
4035
|
+
keywords: uniqueRelationshipKeywords(form.getAll("keywords").map(String))
|
|
3714
4036
|
};
|
|
3715
4037
|
await api(item ? `/api/volumes/${item.id}` : `/api/works/${state.work.id}/volumes`, { method: item ? "PATCH" : "POST", body });
|
|
3716
4038
|
state.work = await api(`/api/works/${state.work.id}`);
|
|
@@ -3720,9 +4042,10 @@ function openVolumeDialog(item) {
|
|
|
3720
4042
|
}
|
|
3721
4043
|
|
|
3722
4044
|
function openSettingEditor(item = null) {
|
|
4045
|
+
destroyVditorEditor(settingEditorVditor);
|
|
4046
|
+
settingEditorVditor = null;
|
|
3723
4047
|
settingEditorItem = item;
|
|
3724
4048
|
$("#setting-editor-eyebrow").textContent = item ? "人工修正" : "作者事实";
|
|
3725
|
-
$("#setting-editor-title").textContent = item ? `编辑“${item.title}”` : "新建设定";
|
|
3726
4049
|
$("#setting-editor-name").value = item?.title ?? "";
|
|
3727
4050
|
$("#setting-editor-category").value = item?.category ?? "世界规则";
|
|
3728
4051
|
$("#setting-editor-locked").checked = Boolean(item?.locked);
|
|
@@ -3741,16 +4064,29 @@ function openSettingEditor(item = null) {
|
|
|
3741
4064
|
const submit = $("#setting-editor-submit");
|
|
3742
4065
|
submit.disabled = true;
|
|
3743
4066
|
try {
|
|
4067
|
+
const title = String(form.get("title") ?? "").trim();
|
|
4068
|
+
if (!title) {
|
|
4069
|
+
toast("请填写设定标题", "error");
|
|
4070
|
+
$("#setting-editor-name").focus();
|
|
4071
|
+
return;
|
|
4072
|
+
}
|
|
4073
|
+
const content = String(form.get("content") ?? "");
|
|
4074
|
+
if (!content.trim()) {
|
|
4075
|
+
toast("请填写设定正文", "error");
|
|
4076
|
+
$("#setting-editor-body").focus();
|
|
4077
|
+
return;
|
|
4078
|
+
}
|
|
3744
4079
|
const locked = form.get("locked") === "on";
|
|
3745
4080
|
const body = {
|
|
3746
|
-
title
|
|
4081
|
+
title,
|
|
3747
4082
|
category: String(form.get("category") ?? "世界规则"),
|
|
3748
|
-
content
|
|
4083
|
+
content,
|
|
3749
4084
|
locked,
|
|
3750
4085
|
status: locked ? "confirmed" : (item?.status ?? "draft"),
|
|
3751
4086
|
...(item ? { changeNote: String(form.get("changeNote") ?? "").trim() } : {})
|
|
3752
4087
|
};
|
|
3753
4088
|
await api(item ? `/api/settings/${item.id}` : `/api/works/${state.work.id}/settings`, { method: item ? "PATCH" : "POST", body });
|
|
4089
|
+
await cleanupPendingMarkdownAttachments(body.content);
|
|
3754
4090
|
entityEditorDirty = false;
|
|
3755
4091
|
await loadAiReferences();
|
|
3756
4092
|
await closeEntityEditor({ force: true });
|
|
@@ -3762,6 +4098,10 @@ function openSettingEditor(item = null) {
|
|
|
3762
4098
|
}
|
|
3763
4099
|
};
|
|
3764
4100
|
showEntityEditorPage("setting");
|
|
4101
|
+
settingEditorVditor = createVditorEditor($("#setting-editor-markdown"), item?.content ?? "", {
|
|
4102
|
+
onInput: (markdown) => { $("#setting-editor-body").value = markdown; markEntityEditorDirty(); },
|
|
4103
|
+
readOnly: viewOnly
|
|
4104
|
+
});
|
|
3765
4105
|
$("#setting-editor-name").focus();
|
|
3766
4106
|
}
|
|
3767
4107
|
|
|
@@ -3887,22 +4227,165 @@ async function discardPendingCharacterAttachments() {
|
|
|
3887
4227
|
}));
|
|
3888
4228
|
}
|
|
3889
4229
|
|
|
3890
|
-
function
|
|
3891
|
-
|
|
3892
|
-
|
|
3893
|
-
|
|
3894
|
-
|
|
3895
|
-
const content = $("#character-section-markdown");
|
|
3896
|
-
if (preview && content) preview.innerHTML = renderMarkdown(content.value) || '<p class="character-markdown-empty">预览区域暂无内容。</p>';
|
|
3897
|
-
}, 260);
|
|
4230
|
+
async function discardPendingMarkdownAttachments() {
|
|
4231
|
+
const pending = markdownEditorPendingAttachments.splice(0);
|
|
4232
|
+
await Promise.all(pending.map(async (attachmentId) => {
|
|
4233
|
+
try { await api(`/api/attachments/${attachmentId}`, { method: "DELETE" }); } catch { /* 已被引用的附件由正常引用生命周期管理。 */ }
|
|
4234
|
+
}));
|
|
3898
4235
|
}
|
|
3899
4236
|
|
|
3900
|
-
async function
|
|
3901
|
-
|
|
3902
|
-
|
|
3903
|
-
|
|
3904
|
-
|
|
4237
|
+
async function cleanupPendingMarkdownAttachments(contentMarkdown) {
|
|
4238
|
+
const referenced = new Set([...String(contentMarkdown).matchAll(/attachment:\/\/([A-Za-z0-9_-]{1,300})/gu)].map((match) => String(match[1])));
|
|
4239
|
+
const pending = markdownEditorPendingAttachments.splice(0);
|
|
4240
|
+
await Promise.all(pending.filter((attachmentId) => !referenced.has(attachmentId)).map((attachmentId) => api(`/api/attachments/${attachmentId}`, { method: "DELETE" }).catch(() => null)));
|
|
4241
|
+
}
|
|
4242
|
+
|
|
4243
|
+
function markdownImageLabel(file, fallback = "图片附件") {
|
|
4244
|
+
return String(file?.name ?? "").replace(/[\[\]\r\n]/gu, "").trim() || fallback;
|
|
4245
|
+
}
|
|
4246
|
+
|
|
4247
|
+
async function uploadMarkdownAttachment(file) {
|
|
4248
|
+
const body = new FormData();
|
|
4249
|
+
body.append("file", file);
|
|
4250
|
+
const attachment = await api(`/api/works/${state.work.id}/attachments`, { method: "POST", body });
|
|
4251
|
+
if (!attachment.deduplicated) markdownEditorPendingAttachments.push(String(attachment.id));
|
|
4252
|
+
return { attachment, imageLabel: markdownImageLabel(file) };
|
|
4253
|
+
}
|
|
4254
|
+
|
|
4255
|
+
function createVditorUploadHandler(uploadAttachment, getEditor) {
|
|
4256
|
+
return async (files) => {
|
|
4257
|
+
const selection = window.getSelection();
|
|
4258
|
+
const range = selection?.rangeCount ? selection.getRangeAt(0).cloneRange() : null;
|
|
4259
|
+
const insertions = [];
|
|
4260
|
+
for (const file of files) {
|
|
4261
|
+
try {
|
|
4262
|
+
const { attachment, imageLabel } = await uploadAttachment(file);
|
|
4263
|
+
insertions.push(``);
|
|
4264
|
+
} catch (error) {
|
|
4265
|
+
toast(error.message, "error");
|
|
4266
|
+
}
|
|
4267
|
+
}
|
|
4268
|
+
const editor = getEditor();
|
|
4269
|
+
if (editor && insertions.length > 0) {
|
|
4270
|
+
if (range && editor.vditor?.element?.contains(range.startContainer)) {
|
|
4271
|
+
selection?.removeAllRanges();
|
|
4272
|
+
selection?.addRange(range);
|
|
4273
|
+
}
|
|
4274
|
+
editor.insertMD(insertions.join("\n\n"));
|
|
4275
|
+
normalizeVditorAttachmentImages(editor);
|
|
4276
|
+
}
|
|
4277
|
+
return null;
|
|
4278
|
+
};
|
|
4279
|
+
}
|
|
4280
|
+
|
|
4281
|
+
function createVditorEditor(host, value, { onInput = () => {}, uploadAttachment = uploadMarkdownAttachment, placeholder = "", readOnly = false } = {}) {
|
|
4282
|
+
if (!window.Vditor) {
|
|
4283
|
+
toast("Markdown 编辑器资源加载失败,请刷新页面后重试", "error");
|
|
4284
|
+
return null;
|
|
3905
4285
|
}
|
|
4286
|
+
ensureVditorIconScript();
|
|
4287
|
+
let editor = null;
|
|
4288
|
+
editor = new window.Vditor(host, {
|
|
4289
|
+
cdn: "/vendor/vditor",
|
|
4290
|
+
lang: "zh_CN",
|
|
4291
|
+
theme: currentColorTheme() === "dark" ? "dark" : "classic",
|
|
4292
|
+
mode: "ir",
|
|
4293
|
+
value: String(value ?? ""),
|
|
4294
|
+
height: "100%",
|
|
4295
|
+
minHeight: 260,
|
|
4296
|
+
placeholder,
|
|
4297
|
+
preview: { transform: transformVditorPreview },
|
|
4298
|
+
cache: { enable: false },
|
|
4299
|
+
toolbar: ["headings", "bold", "italic", "strike", "|", "line", "quote", "list", "ordered-list", "check", "|", "code", "inline-code", "link", "table", "upload", "|", "undo", "redo", "edit-mode", "fullscreen"],
|
|
4300
|
+
upload: {
|
|
4301
|
+
accept: "image/*",
|
|
4302
|
+
max: 10 * 1024 * 1024,
|
|
4303
|
+
multiple: true,
|
|
4304
|
+
handler: createVditorUploadHandler(uploadAttachment, () => editor)
|
|
4305
|
+
},
|
|
4306
|
+
input: (markdown) => {
|
|
4307
|
+
normalizeVditorAttachmentImages(editor);
|
|
4308
|
+
onInput(markdown);
|
|
4309
|
+
},
|
|
4310
|
+
after: () => {
|
|
4311
|
+
normalizeVditorAttachmentImages(editor);
|
|
4312
|
+
if (readOnly) editor?.disabled();
|
|
4313
|
+
}
|
|
4314
|
+
});
|
|
4315
|
+
const attachmentObserver = new MutationObserver(() => normalizeVditorAttachmentImages(editor));
|
|
4316
|
+
attachmentObserver.observe(host, { subtree: true, childList: true, attributes: true, attributeFilter: ["src"] });
|
|
4317
|
+
editor.__attachmentObserver = attachmentObserver;
|
|
4318
|
+
host.__vditor = editor;
|
|
4319
|
+
if (readOnly) editor.disabled();
|
|
4320
|
+
return editor;
|
|
4321
|
+
}
|
|
4322
|
+
|
|
4323
|
+
function transformVditorPreview(html) {
|
|
4324
|
+
return String(html ?? "").replace(/(<img\b[^>]*\bsrc\s*=\s*)(["'])attachment:\/\/([A-Za-z0-9_-]{1,300})\2/giu, (_match, prefix, quote, attachmentId) => `${prefix}${quote}/api/attachments/${encodeURIComponent(attachmentId)}/content${quote}`);
|
|
4325
|
+
}
|
|
4326
|
+
|
|
4327
|
+
function normalizeVditorAttachmentImages(editor) {
|
|
4328
|
+
const root = editor?.vditor?.element;
|
|
4329
|
+
if (!root) return;
|
|
4330
|
+
root.querySelectorAll('img[src^="attachment://"]').forEach((image) => {
|
|
4331
|
+
const target = image.getAttribute("src")?.trim() ?? "";
|
|
4332
|
+
const attachment = target.match(/^attachment:\/\/([A-Za-z0-9_-]{1,300})$/u);
|
|
4333
|
+
if (attachment) image.setAttribute("src", `/api/attachments/${encodeURIComponent(attachment[1])}/content`);
|
|
4334
|
+
});
|
|
4335
|
+
}
|
|
4336
|
+
|
|
4337
|
+
function ensureVditorIconScript() {
|
|
4338
|
+
if (document.getElementById("vditorIconScript")) return;
|
|
4339
|
+
const script = document.createElement("script");
|
|
4340
|
+
script.id = "vditorIconScript";
|
|
4341
|
+
script.src = "/vendor/vditor/dist/js/icons/ant.js?v=3.11.2";
|
|
4342
|
+
document.body.appendChild(script);
|
|
4343
|
+
}
|
|
4344
|
+
|
|
4345
|
+
function destroyVditorEditor(editor) {
|
|
4346
|
+
if (!editor) return;
|
|
4347
|
+
editor.__attachmentObserver?.disconnect();
|
|
4348
|
+
const host = editor.vditor?.element;
|
|
4349
|
+
editor.destroy();
|
|
4350
|
+
if (host) delete host.__vditor;
|
|
4351
|
+
}
|
|
4352
|
+
|
|
4353
|
+
function bindVditorEditors(container) {
|
|
4354
|
+
container.querySelectorAll("[data-vditor-editor]").forEach((host) => {
|
|
4355
|
+
const valueField = host.parentElement?.querySelector("[data-vditor-value]");
|
|
4356
|
+
const editor = createVditorEditor(host, valueField?.value ?? "", {
|
|
4357
|
+
onInput: (markdown) => {
|
|
4358
|
+
if (valueField) valueField.value = markdown;
|
|
4359
|
+
markEntityEditorDirty();
|
|
4360
|
+
},
|
|
4361
|
+
placeholder: "",
|
|
4362
|
+
readOnly: Boolean(valueField?.readOnly)
|
|
4363
|
+
});
|
|
4364
|
+
});
|
|
4365
|
+
}
|
|
4366
|
+
|
|
4367
|
+
function characterSectionImageLabel(file, fallback = "图片附件") {
|
|
4368
|
+
return String(file?.name ?? "").replace(/[\[\]\r\n]/gu, "").trim() || fallback;
|
|
4369
|
+
}
|
|
4370
|
+
|
|
4371
|
+
async function uploadCharacterSectionAttachment(file) {
|
|
4372
|
+
const body = new FormData();
|
|
4373
|
+
body.append("file", file);
|
|
4374
|
+
const attachment = await api(`/api/works/${state.work.id}/attachments`, { method: "POST", body });
|
|
4375
|
+
if (!attachment.deduplicated) characterSectionPendingAttachments.push(String(attachment.id));
|
|
4376
|
+
return {
|
|
4377
|
+
attachment,
|
|
4378
|
+
imageLabel: characterSectionImageLabel(file)
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
|
|
4382
|
+
async function closeCharacterSectionEditor({ force = false } = {}) {
|
|
4383
|
+
if (!force && characterSectionEditorDirty && !(await confirmToast(
|
|
4384
|
+
"当前 Markdown 章节有未保存修改,返回人物档案将丢弃这些修改。是否继续?",
|
|
4385
|
+
{ title: "放弃未保存修改", confirmLabel: "放弃并继续", cancelLabel: "继续编辑" }
|
|
4386
|
+
))) return false;
|
|
4387
|
+
destroyVditorEditor(characterSectionVditor);
|
|
4388
|
+
characterSectionVditor = null;
|
|
3906
4389
|
await discardPendingCharacterAttachments();
|
|
3907
4390
|
characterSectionEditorDirty = false;
|
|
3908
4391
|
$("#character-section-editor-view").classList.add("hidden");
|
|
@@ -3912,6 +4395,82 @@ async function closeCharacterSectionEditor({ force = false } = {}) {
|
|
|
3912
4395
|
return true;
|
|
3913
4396
|
}
|
|
3914
4397
|
|
|
4398
|
+
function knowledgeSectionEditorHtml(section = null) {
|
|
4399
|
+
const label = knowledgeEditorKind === "race" ? "种族" : "组织";
|
|
4400
|
+
return `<div class="character-section-editor-shell knowledge-section-editor-shell">
|
|
4401
|
+
<header class="character-section-editor-header">
|
|
4402
|
+
<div><span class="eyebrow">${label} Markdown 设定</span><h2 id="knowledge-section-editor-title">${section ? `编辑“${esc(section.title)}”` : "新建设定"}</h2></div>
|
|
4403
|
+
<button class="entity-editor-back" type="button" data-knowledge-section-edit-close>返回设定列表</button>
|
|
4404
|
+
</header>
|
|
4405
|
+
<section class="character-markdown-editor" aria-label="${section ? "编辑" : "新建"}${label} Markdown 设定">
|
|
4406
|
+
<div class="character-markdown-editor-meta">
|
|
4407
|
+
<label>设定标题<input id="knowledge-section-title" maxlength="200" value="${esc(section?.title ?? "")}" placeholder="例如:组织章程、种族特征" required></label>
|
|
4408
|
+
</div>
|
|
4409
|
+
<div id="knowledge-section-markdown" class="vditor-editor-host" data-vditor-editor aria-label="Markdown 编辑器"></div>
|
|
4410
|
+
<div class="character-markdown-editor-footer">
|
|
4411
|
+
<div class="character-markdown-editor-actions"><button type="button" data-knowledge-section-edit-cancel>取消</button><button type="button" class="primary-button" data-knowledge-section-edit-save>${section ? "保存设定" : "添加设定"}</button></div>
|
|
4412
|
+
</div>
|
|
4413
|
+
</section>
|
|
4414
|
+
</div>`;
|
|
4415
|
+
}
|
|
4416
|
+
|
|
4417
|
+
async function closeKnowledgeSectionEditor({ force = false } = {}) {
|
|
4418
|
+
if (!force && knowledgeSectionEditorDirty && !(await confirmToast(
|
|
4419
|
+
"当前 Markdown 设定有未保存修改,返回设定列表将丢弃这些修改。是否继续?",
|
|
4420
|
+
{ title: "放弃未保存修改", confirmLabel: "放弃并继续", cancelLabel: "继续编辑" }
|
|
4421
|
+
))) return false;
|
|
4422
|
+
destroyVditorEditor(knowledgeSectionVditor);
|
|
4423
|
+
knowledgeSectionVditor = null;
|
|
4424
|
+
knowledgeSectionEditorDirty = false;
|
|
4425
|
+
knowledgeSectionEditorIndex = null;
|
|
4426
|
+
$("#knowledge-section-editor-view").classList.add("hidden");
|
|
4427
|
+
$("#knowledge-editor-form").classList.remove("hidden");
|
|
4428
|
+
$("#knowledge-section-editor-host").innerHTML = "";
|
|
4429
|
+
renderKnowledgeMarkdownSections();
|
|
4430
|
+
replacePageRoute(currentPageRoute());
|
|
4431
|
+
return true;
|
|
4432
|
+
}
|
|
4433
|
+
|
|
4434
|
+
async function openKnowledgeSectionEditor(index = null) {
|
|
4435
|
+
if (!canEditModule(knowledgeEditorKind === "race" ? "races" : "organizations")) return;
|
|
4436
|
+
destroyVditorEditor(knowledgeSectionVditor);
|
|
4437
|
+
knowledgeSectionVditor = null;
|
|
4438
|
+
const section = Number.isInteger(index) ? knowledgeEditorSections[index] : null;
|
|
4439
|
+
if (Number.isInteger(index) && !section) return;
|
|
4440
|
+
knowledgeSectionEditorIndex = Number.isInteger(index) ? index : null;
|
|
4441
|
+
knowledgeSectionEditorDirty = false;
|
|
4442
|
+
const host = $("#knowledge-section-editor-host");
|
|
4443
|
+
host.innerHTML = knowledgeSectionEditorHtml(section);
|
|
4444
|
+
$("#knowledge-editor-form").classList.add("hidden");
|
|
4445
|
+
$("#knowledge-section-editor-view").classList.remove("hidden");
|
|
4446
|
+
const titleInput = $("#knowledge-section-title");
|
|
4447
|
+
host.querySelectorAll("input, textarea").forEach((control) => control.addEventListener("input", () => { knowledgeSectionEditorDirty = true; }));
|
|
4448
|
+
knowledgeSectionVditor = createVditorEditor($("#knowledge-section-markdown"), section?.contentMarkdown ?? "", {
|
|
4449
|
+
onInput: () => { knowledgeSectionEditorDirty = true; }
|
|
4450
|
+
});
|
|
4451
|
+
host.querySelector("[data-knowledge-section-edit-close]").addEventListener("click", () => void closeKnowledgeSectionEditor());
|
|
4452
|
+
host.querySelector("[data-knowledge-section-edit-cancel]").addEventListener("click", () => void closeKnowledgeSectionEditor());
|
|
4453
|
+
host.querySelector("[data-knowledge-section-edit-save]").addEventListener("click", async (event) => {
|
|
4454
|
+
const button = event.currentTarget;
|
|
4455
|
+
const wasEditing = knowledgeSectionEditorIndex !== null;
|
|
4456
|
+
const title = titleInput.value.trim();
|
|
4457
|
+
if (!title) {
|
|
4458
|
+
toast("请填写设定标题", "error");
|
|
4459
|
+
titleInput.focus();
|
|
4460
|
+
return;
|
|
4461
|
+
}
|
|
4462
|
+
button.disabled = true;
|
|
4463
|
+
const nextSection = { title, contentMarkdown: knowledgeSectionVditor?.getValue() ?? "", summary: String(section?.summary ?? ""), sortOrder: knowledgeSectionEditorIndex ?? knowledgeEditorSections.length };
|
|
4464
|
+
if (knowledgeSectionEditorIndex === null) knowledgeEditorSections.push(nextSection);
|
|
4465
|
+
else knowledgeEditorSections[knowledgeSectionEditorIndex] = nextSection;
|
|
4466
|
+
knowledgeEditorSections.forEach((item, sortOrder) => { item.sortOrder = sortOrder; });
|
|
4467
|
+
markEntityEditorDirty();
|
|
4468
|
+
await closeKnowledgeSectionEditor({ force: true });
|
|
4469
|
+
toast(wasEditing ? "设定已更新" : "设定已添加");
|
|
4470
|
+
});
|
|
4471
|
+
titleInput.focus();
|
|
4472
|
+
}
|
|
4473
|
+
|
|
3915
4474
|
function characterSectionEditorHtml(section = null) {
|
|
3916
4475
|
const options = Object.entries(characterSectionTypeLabels).map(([value, label]) => `<option value="${value}" ${section?.sectionType === value ? "selected" : ""}>${esc(label)}</option>`).join("");
|
|
3917
4476
|
return `<div class="character-section-editor-shell">
|
|
@@ -3925,15 +4484,7 @@ function characterSectionEditorHtml(section = null) {
|
|
|
3925
4484
|
<label>章节标题<input id="character-section-title" maxlength="200" value="${esc(section?.title ?? "")}" placeholder="例如:背景故事" required></label>
|
|
3926
4485
|
<label class="character-markdown-summary-field">章节摘要<textarea id="character-section-summary" maxlength="20000" placeholder="用于角色列表和 AI 快速定位,不会替代正文">${esc(section?.summary ?? "")}</textarea></label>
|
|
3927
4486
|
</div>
|
|
3928
|
-
<div
|
|
3929
|
-
<label class="ghost-button" for="character-section-attachment">上传并插入图片</label>
|
|
3930
|
-
<input id="character-section-attachment" class="hidden" type="file" accept=".png,.jpg,.jpeg,.webp,.gif,image/png,image/jpeg,image/webp,image/gif">
|
|
3931
|
-
<span>图片会优先转换为体积更小的无损 WebP</span>
|
|
3932
|
-
</div>
|
|
3933
|
-
<div class="character-markdown-compose">
|
|
3934
|
-
<label>Markdown 原文<textarea id="character-section-markdown" maxlength="500000" spellcheck="true" placeholder="支持标题、列表、引用、表格、链接和图片">${esc(section?.contentMarkdown ?? "")}</textarea></label>
|
|
3935
|
-
<div><span class="character-markdown-preview-label">安全预览</span><article id="character-section-preview" class="character-markdown-document message-body">${renderMarkdown(section?.contentMarkdown ?? "") || '<p class="character-markdown-empty">预览区域暂无内容。</p>'}</article></div>
|
|
3936
|
-
</div>
|
|
4487
|
+
<div id="character-section-markdown" class="vditor-editor-host" data-vditor-editor aria-label="Markdown 编辑器"></div>
|
|
3937
4488
|
<div class="character-markdown-editor-footer">
|
|
3938
4489
|
<label class="character-markdown-change-note">版本说明<input id="character-section-change-note" maxlength="500" placeholder="可选,例如:补充远古时期经历"></label>
|
|
3939
4490
|
<div class="character-markdown-editor-actions"><button type="button" data-character-section-edit-cancel>取消</button><button type="button" class="primary-button" data-character-section-edit-save>${section ? "保存章节版本" : "创建章节"}</button></div>
|
|
@@ -3944,41 +4495,17 @@ function characterSectionEditorHtml(section = null) {
|
|
|
3944
4495
|
|
|
3945
4496
|
async function openCharacterSectionEditor(section = null) {
|
|
3946
4497
|
await discardPendingCharacterAttachments();
|
|
4498
|
+
destroyVditorEditor(characterSectionVditor);
|
|
4499
|
+
characterSectionVditor = null;
|
|
3947
4500
|
const host = $("#character-section-editor-host");
|
|
3948
4501
|
host.innerHTML = characterSectionEditorHtml(section);
|
|
3949
4502
|
characterSectionEditorDirty = false;
|
|
3950
4503
|
$("#character-editor-form").classList.add("hidden");
|
|
3951
4504
|
$("#character-section-editor-view").classList.remove("hidden");
|
|
3952
|
-
const textarea = $("#character-section-markdown");
|
|
3953
4505
|
host.querySelectorAll("input, textarea, select").forEach((control) => control.addEventListener("input", () => { characterSectionEditorDirty = true; }));
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
if (!file) return;
|
|
3958
|
-
const input = event.currentTarget;
|
|
3959
|
-
input.disabled = true;
|
|
3960
|
-
const body = new FormData();
|
|
3961
|
-
body.append("file", file);
|
|
3962
|
-
try {
|
|
3963
|
-
const attachment = await api(`/api/works/${state.work.id}/attachments`, { method: "POST", body });
|
|
3964
|
-
if (!attachment.deduplicated) characterSectionPendingAttachments.push(String(attachment.id));
|
|
3965
|
-
const imageLabel = file.name.replace(/[\]\r\n]/gu, "").trim() || "图片附件";
|
|
3966
|
-
const insertion = ``;
|
|
3967
|
-
const start = textarea.selectionStart ?? textarea.value.length;
|
|
3968
|
-
const end = textarea.selectionEnd ?? start;
|
|
3969
|
-
const prefix = start > 0 && !textarea.value.slice(0, start).endsWith("\n") ? "\n\n" : "";
|
|
3970
|
-
const suffix = end < textarea.value.length && !textarea.value.slice(end).startsWith("\n") ? "\n\n" : "";
|
|
3971
|
-
textarea.setRangeText(`${prefix}${insertion}${suffix}`, start, end, "end");
|
|
3972
|
-
textarea.focus();
|
|
3973
|
-
characterSectionEditorDirty = true;
|
|
3974
|
-
scheduleCharacterSectionPreview();
|
|
3975
|
-
toast(attachment.storedMimeType === "image/webp" ? "图片已转换为无损 WebP 并插入" : "图片已插入;转换后未变小,因此保留原格式");
|
|
3976
|
-
} catch (error) {
|
|
3977
|
-
toast(error.message, "error");
|
|
3978
|
-
} finally {
|
|
3979
|
-
input.disabled = false;
|
|
3980
|
-
input.value = "";
|
|
3981
|
-
}
|
|
4506
|
+
characterSectionVditor = createVditorEditor($("#character-section-markdown"), section?.contentMarkdown ?? "", {
|
|
4507
|
+
uploadAttachment: uploadCharacterSectionAttachment,
|
|
4508
|
+
onInput: () => { characterSectionEditorDirty = true; }
|
|
3982
4509
|
});
|
|
3983
4510
|
host.querySelector("[data-character-section-edit-close]").addEventListener("click", () => void closeCharacterSectionEditor());
|
|
3984
4511
|
host.querySelector("[data-character-section-edit-cancel]").addEventListener("click", () => void closeCharacterSectionEditor());
|
|
@@ -3991,7 +4518,7 @@ async function openCharacterSectionEditor(section = null) {
|
|
|
3991
4518
|
return;
|
|
3992
4519
|
}
|
|
3993
4520
|
button.disabled = true;
|
|
3994
|
-
const contentMarkdown =
|
|
4521
|
+
const contentMarkdown = characterSectionVditor?.getValue() ?? "";
|
|
3995
4522
|
try {
|
|
3996
4523
|
const saved = await api(section ? `/api/character-sections/${section.id}` : `/api/characters/${characterEditorItem.id}/sections`, {
|
|
3997
4524
|
method: section ? "PATCH" : "POST",
|
|
@@ -4049,7 +4576,7 @@ function renderCharacterMarkdownSections() {
|
|
|
4049
4576
|
const host = $("#character-markdown-sections");
|
|
4050
4577
|
if (!host) return;
|
|
4051
4578
|
if (!characterEditorItem?.id) {
|
|
4052
|
-
host.innerHTML = '<div class="character-editor-empty-field"><b
|
|
4579
|
+
host.innerHTML = '<div class="character-editor-empty-field" role="note" aria-label="保存角色提示"><b>请先保存当前角色</b><span>完成基础资料后,请点击页面底部的“创建人物档案”。保存成功后即可新建 Markdown 档案章节。</span></div>';
|
|
4053
4580
|
return;
|
|
4054
4581
|
}
|
|
4055
4582
|
const toolbar = `<div class="character-markdown-list-toolbar"><div><b>Markdown 档案章节</b><span>长篇内容独立保存、渲染、检索和版本管理。</span></div>${canEditModule("characters") ? '<button type="button" class="primary-button" data-character-section-create>新建章节</button>' : ""}</div>`;
|
|
@@ -4315,44 +4842,115 @@ async function openCharacterEditor(item = null) {
|
|
|
4315
4842
|
}
|
|
4316
4843
|
}
|
|
4317
4844
|
|
|
4318
|
-
|
|
4845
|
+
function knowledgeEditorSection(key, title, description, content) {
|
|
4846
|
+
return `<section class="character-editor-section${key === "basic" ? "" : " hidden"}" data-knowledge-editor-panel="${esc(key)}" role="tabpanel"><header><div><span class="eyebrow">${esc(title)}</span><h3>${esc(title)}</h3></div>${description ? `<p>${esc(description)}</p>` : ""}</header><div class="character-editor-section-fields">${content}</div></section>`;
|
|
4847
|
+
}
|
|
4848
|
+
|
|
4849
|
+
function activateKnowledgeEditorTab(key) {
|
|
4850
|
+
document.querySelectorAll("[data-knowledge-editor-tab]").forEach((button) => {
|
|
4851
|
+
const active = button.dataset.knowledgeEditorTab === key;
|
|
4852
|
+
button.setAttribute("aria-selected", String(active));
|
|
4853
|
+
button.tabIndex = active ? 0 : -1;
|
|
4854
|
+
});
|
|
4855
|
+
document.querySelectorAll("[data-knowledge-editor-panel]").forEach((panel) => panel.classList.toggle("hidden", panel.dataset.knowledgeEditorPanel !== key));
|
|
4856
|
+
}
|
|
4857
|
+
|
|
4858
|
+
function renderKnowledgeEditorFields(kind, item, memberOptions, parentOptions) {
|
|
4859
|
+
const isRace = kind === "race";
|
|
4860
|
+
const label = isRace ? "种族" : "组织";
|
|
4861
|
+
const title = isRace ? "种族共同设定" : "组织设定";
|
|
4862
|
+
const tabs = isRace
|
|
4863
|
+
? [["basic", "基础资料", "名称、层级与简介"], ["settings", "共同设定", "Markdown 设定章节"], ["members", "种族成员", "角色归属"]]
|
|
4864
|
+
: [["basic", "基础资料", "名称与简介"], ["settings", "组织设定", "Markdown 设定章节"], ["members", "组织成员", "角色归属"]];
|
|
4865
|
+
$("#knowledge-editor-nav").innerHTML = tabs.map(([key, tabTitle, description], index) => `<button type="button" role="tab" data-knowledge-editor-tab="${key}" aria-selected="${index === 0}" tabindex="${index === 0 ? "0" : "-1"}">${tabTitle}<small>${description}</small></button>`).join("");
|
|
4866
|
+
const basicFields = field("name", `${label}名称`, "text", item?.name, []) + (isRace ? field("parentRaceId", "父种族", "select", item?.parentRaceId ?? "", parentOptions) : "") + field("description", `${label}简介`, "textarea", item?.description, []);
|
|
4867
|
+
const memberField = memberOptions.length
|
|
4868
|
+
? field("memberIds", isRace ? "属于该种族的角色(可多选)" : "组织成员(可多选)", "chips", item?.memberIds ?? [], memberOptions)
|
|
4869
|
+
: `<div class="character-editor-empty-field"><strong>${isRace ? "种族成员" : "组织成员"}</strong><span>当前还没有可绑定的角色。</span></div>`;
|
|
4870
|
+
$("#knowledge-editor-fields").innerHTML = knowledgeEditorSection("basic", "基础资料", isRace ? "先定义名称、层级和简介,再补充共同设定。" : "先定义组织名称和简介,再补充完整的组织设定。", basicFields)
|
|
4871
|
+
+ knowledgeEditorSection("settings", title, "", '<div id="knowledge-markdown-sections" class="knowledge-markdown-sections"></div>')
|
|
4872
|
+
+ knowledgeEditorSection("members", isRace ? "种族成员" : "组织成员", "成员关系会同步到角色档案中。", memberField);
|
|
4873
|
+
document.querySelectorAll("[data-knowledge-editor-tab]").forEach((button) => {
|
|
4874
|
+
button.onclick = () => activateKnowledgeEditorTab(button.dataset.knowledgeEditorTab);
|
|
4875
|
+
});
|
|
4876
|
+
renderKnowledgeMarkdownSections();
|
|
4877
|
+
activateKnowledgeEditorTab("basic");
|
|
4878
|
+
}
|
|
4879
|
+
|
|
4880
|
+
async function openKnowledgeEditor(kind, item) {
|
|
4881
|
+
await discardPendingMarkdownAttachments();
|
|
4319
4882
|
state.characters = canReadModule("characters") ? await apiAllPages(`/api/works/${state.work.id}/characters`) : [];
|
|
4320
4883
|
const memberOptions = state.characters.map((character) => [character.id, `${character.name}${character.aliases.length ? `(${character.aliases.join("、")})` : ""}`]);
|
|
4321
|
-
const
|
|
4322
|
-
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
|
|
4334
|
-
|
|
4335
|
-
|
|
4884
|
+
const isRace = kind === "race";
|
|
4885
|
+
const module = isRace ? "races" : "organizations";
|
|
4886
|
+
const label = isRace ? "种族" : "组织";
|
|
4887
|
+
const parentOptions = isRace
|
|
4888
|
+
? [["", "无(根种族)"], ...eligibleRaceParents(state.races, item?.id)
|
|
4889
|
+
.sort((left, right) => racePathLabel(left).localeCompare(racePathLabel(right), "zh-CN"))
|
|
4890
|
+
.map((race) => [race.id, racePathLabel(race)])]
|
|
4891
|
+
: [];
|
|
4892
|
+
knowledgeEditorItem = item ?? null;
|
|
4893
|
+
knowledgeEditorKind = kind;
|
|
4894
|
+
knowledgeEditorSections = normalizeKnowledgeEditorSections(item);
|
|
4895
|
+
knowledgeSectionEditorIndex = null;
|
|
4896
|
+
knowledgeSectionEditorDirty = false;
|
|
4897
|
+
$("#knowledge-editor-eyebrow").textContent = item ? `${label}档案` : `建立${label}档案`;
|
|
4898
|
+
$("#knowledge-editor-title").textContent = item?.name || `新建${label}`;
|
|
4899
|
+
$("#knowledge-editor-version").textContent = item ? `v${item.versionNo ?? 1}` : "新档案";
|
|
4900
|
+
$("#knowledge-editor-header-note").textContent = isRace ? "层级、共同设定与角色归属" : "简介、组织设定与成员归属";
|
|
4901
|
+
$("#knowledge-editor-footer-note").textContent = `保存${label}档案后返回${isRace ? "种族" : "组织"}列表。`;
|
|
4902
|
+
$("#knowledge-editor-submit").textContent = item ? `保存${label}档案` : `创建${label}档案`;
|
|
4903
|
+
renderKnowledgeEditorFields(kind, item, memberOptions, parentOptions);
|
|
4904
|
+
const viewOnly = !canEditModule(module);
|
|
4905
|
+
if (viewOnly) {
|
|
4906
|
+
$("#knowledge-editor-eyebrow").textContent = `${label}档案`;
|
|
4907
|
+
$("#knowledge-editor-fields").querySelectorAll("input, textarea").forEach((control) => { control.readOnly = true; });
|
|
4908
|
+
$("#knowledge-editor-fields").querySelectorAll("select, input[type='checkbox']").forEach((control) => { control.disabled = true; });
|
|
4909
|
+
}
|
|
4910
|
+
$("#knowledge-editor-submit").classList.toggle("hidden", viewOnly);
|
|
4911
|
+
const form = $("#knowledge-editor-form");
|
|
4912
|
+
form.onsubmit = async (event) => {
|
|
4913
|
+
event.preventDefault();
|
|
4914
|
+
if (!canEditModule(module)) return;
|
|
4915
|
+
const submit = $("#knowledge-editor-submit");
|
|
4916
|
+
submit.disabled = true;
|
|
4917
|
+
try {
|
|
4918
|
+
const data = new FormData(form);
|
|
4919
|
+
const name = String(data.get("name") ?? "").trim();
|
|
4920
|
+
if (!name) throw new Error(`请填写${label}名称`);
|
|
4921
|
+
const settingsSections = knowledgeEditorSections
|
|
4922
|
+
.map((section, index) => ({ title: String(section.title ?? "").trim(), contentMarkdown: String(section.contentMarkdown ?? ""), summary: String(section.summary ?? "").trim(), sortOrder: index }))
|
|
4923
|
+
.filter((section) => section.title || section.contentMarkdown.trim());
|
|
4924
|
+
const untitled = settingsSections.findIndex((section) => !section.title);
|
|
4925
|
+
if (untitled >= 0) throw new Error(`请填写第 ${untitled + 1} 条 Markdown 设定的标题`);
|
|
4926
|
+
const settingsMarkdown = settingsSections.map((section) => section.contentMarkdown).join("\n\n");
|
|
4927
|
+
const body = isRace
|
|
4928
|
+
? { name, parentRaceId: data.get("parentRaceId") || null, description: data.get("description"), settingsMarkdown, settingsSections }
|
|
4929
|
+
: { name, description: data.get("description"), settingsMarkdown, settingsSections };
|
|
4930
|
+
if (canReadModule("characters")) body.memberIds = data.getAll("memberIds").map(String);
|
|
4931
|
+
const wasEditing = Boolean(knowledgeEditorItem);
|
|
4932
|
+
await api(wasEditing ? `/api/${module}/${knowledgeEditorItem.id}` : `/api/works/${state.work.id}/${module}`, { method: wasEditing ? "PATCH" : "POST", body });
|
|
4933
|
+
await cleanupPendingMarkdownAttachments(settingsMarkdown);
|
|
4934
|
+
entityEditorDirty = false;
|
|
4336
4935
|
await loadAiReferences();
|
|
4337
|
-
|
|
4936
|
+
await closeEntityEditor({ force: true });
|
|
4937
|
+
toast(wasEditing ? `${label}档案已保存` : `${label}档案已创建`);
|
|
4938
|
+
} catch (error) {
|
|
4939
|
+
toast(error.message, "error");
|
|
4940
|
+
} finally {
|
|
4941
|
+
submit.disabled = false;
|
|
4942
|
+
}
|
|
4943
|
+
};
|
|
4944
|
+
showEntityEditorPage(kind);
|
|
4945
|
+
$("#knowledge-editor-fields").querySelector("input:not([type='checkbox']), textarea")?.focus();
|
|
4946
|
+
}
|
|
4947
|
+
|
|
4948
|
+
async function openRaceDialog(item) {
|
|
4949
|
+
await openKnowledgeEditor("race", item);
|
|
4338
4950
|
}
|
|
4339
4951
|
|
|
4340
4952
|
async function openOrganizationDialog(item) {
|
|
4341
|
-
|
|
4342
|
-
const memberOptions = state.characters.map((character) => [character.id, `${character.name}${character.aliases.length ? `(${character.aliases.join("、")})` : ""}`]);
|
|
4343
|
-
openDialog(item ? "编辑组织" : "新建组织",
|
|
4344
|
-
field("name", "组织名称", "text", item?.name) +
|
|
4345
|
-
field("description", "组织简介", "textarea", item?.description) +
|
|
4346
|
-
field("settings", "组织设定(逐条填写)", "item-list", item?.settings ?? []) +
|
|
4347
|
-
(memberOptions.length ? field("memberIds", "组织成员(可多选)", "chips", item?.memberIds ?? [], memberOptions) : ""),
|
|
4348
|
-
async (form) => {
|
|
4349
|
-
const settings = form.getAll("settings").map((value) => String(value).trim()).filter(Boolean);
|
|
4350
|
-
const body = { name: form.get("name"), description: form.get("description"), settings };
|
|
4351
|
-
if (canReadModule("characters")) body.memberIds = form.getAll("memberIds").map(String);
|
|
4352
|
-
await api(item ? `/api/organizations/${item.id}` : `/api/works/${state.work.id}/organizations`, { method: item ? "PATCH" : "POST", body });
|
|
4353
|
-
await renderOrganizations();
|
|
4354
|
-
await loadAiReferences();
|
|
4355
|
-
}, item ? "组织档案" : "世界内组织");
|
|
4953
|
+
await openKnowledgeEditor("organization", item);
|
|
4356
4954
|
}
|
|
4357
4955
|
|
|
4358
4956
|
function openTimelineTrackDialog(item) {
|
|
@@ -4493,16 +5091,29 @@ function openProviderDialog(item) {
|
|
|
4493
5091
|
|
|
4494
5092
|
function openModelDialog(providerId, item = null) {
|
|
4495
5093
|
const values = modelFormValues(item);
|
|
4496
|
-
|
|
5094
|
+
const temperatureField = `<div class="form-field model-temperature-field"><label for="model-temperature">默认温度<input id="model-temperature" name="temperature" type="number" value="${esc(values.temperature)}" step="any" aria-describedby="model-temperature-hint"></label><small id="model-temperature-hint" class="model-temperature-hint" hidden>Kimi 模型必须设置温度为 1。</small></div>`;
|
|
5095
|
+
openDialog(item ? "编辑模型" : "添加模型", field("displayName", "显示名称", "text", values.displayName) + field("modelId", "模型标识符", "text", values.modelId) + field("purposes", "支持用途(可多选)", "chips", values.purposes, MODEL_PURPOSE_OPTIONS) + field("contextWindow", "模型上下文总量(Token)", "number", values.contextWindow) + temperatureField + field("maxTokens", "默认 max_tokens", "number", values.maxTokens) + field("thinkingEnabled", "开启 Thinking(供应商需支持 thinking 参数)", "checkbox", values.thinkingEnabled) + field("enabled", "启用模型", "checkbox", values.enabled), async (form) => {
|
|
4497
5096
|
const body = modelPayload({ displayName: form.get("displayName"), modelId: form.get("modelId"), purposes: form.getAll("purposes"), contextWindow: form.get("contextWindow"), temperature: form.get("temperature"), maxTokens: form.get("maxTokens"), thinkingEnabled: form.get("thinkingEnabled") === "on", enabled: form.get("enabled") === "on" }, item?.preset);
|
|
4498
5097
|
await api(item ? `/api/models/${item.id}` : `/api/providers/${providerId}/models`, { method: item ? "PATCH" : "POST", body });
|
|
4499
5098
|
await renderPlatformAiConfig();
|
|
4500
5099
|
await loadModels();
|
|
4501
5100
|
}, item ? "模型配置" : "供应商模型");
|
|
5101
|
+
const modelIdInput = $("#dialog-fields input[name='modelId']");
|
|
5102
|
+
const temperatureInput = $("#dialog-fields input[name='temperature']");
|
|
5103
|
+
const temperatureHint = $("#model-temperature-hint");
|
|
5104
|
+
const syncKimiTemperature = () => {
|
|
5105
|
+
const isKimi = isKimiModelId(modelIdInput.value);
|
|
5106
|
+
temperatureHint.hidden = !isKimi;
|
|
5107
|
+
};
|
|
5108
|
+
modelIdInput.addEventListener("input", () => {
|
|
5109
|
+
if (isKimiModelId(modelIdInput.value)) temperatureInput.value = "1";
|
|
5110
|
+
syncKimiTemperature();
|
|
5111
|
+
});
|
|
5112
|
+
syncKimiTemperature();
|
|
4502
5113
|
}
|
|
4503
5114
|
|
|
4504
5115
|
async function sendAi() {
|
|
4505
|
-
if (!state.work
|
|
5116
|
+
if (!state.work) return toast("请先选择作品", "error");
|
|
4506
5117
|
try {
|
|
4507
5118
|
await Promise.all([ensureAiModelsLoaded(), ensureAiConversationsLoaded()]);
|
|
4508
5119
|
} catch (error) {
|
|
@@ -4758,7 +5369,10 @@ async function showVersions() {
|
|
|
4758
5369
|
const versions = await api(`/api/chapters/${state.chapter.id}/versions`);
|
|
4759
5370
|
$("#versions-list").innerHTML = versions.map((version) => `<div class="version-row"><div><b>v${version.versionNo}</b><small>${esc(version.source)} · ${esc(version.actor || "历史数据")}</small></div><p>${esc(version.content.slice(0, 300) || "空白章节")}</p>${canEditProse() ? `<button class="ghost-button" data-restore-version="${version.versionNo}">恢复</button>` : ""}</div>`).join("");
|
|
4760
5371
|
$("#versions-list").querySelectorAll("[data-restore-version]").forEach((button) => button.addEventListener("click", async () => {
|
|
4761
|
-
if (!
|
|
5372
|
+
if (!(await confirmToast(
|
|
5373
|
+
`将版本 v${button.dataset.restoreVersion} 恢复为一个新的保存版本?`,
|
|
5374
|
+
{ title: "恢复历史版本", confirmLabel: "确认恢复" }
|
|
5375
|
+
))) return;
|
|
4762
5376
|
state.chapter = await api(`/api/chapters/${state.chapter.id}/restore`, { method: "POST", body: { versionNo: Number(button.dataset.restoreVersion) } });
|
|
4763
5377
|
lastSavedChapterSnapshot = { chapterId: state.chapter.id, title: state.chapter.title, content: state.chapter.content };
|
|
4764
5378
|
$("#chapter-title").value = state.chapter.title;
|
|
@@ -4820,7 +5434,7 @@ function renderImportHistory(versions, nextPage = null) {
|
|
|
4820
5434
|
}, 5000);
|
|
4821
5435
|
return;
|
|
4822
5436
|
}
|
|
4823
|
-
if (!confirmDiscardChanges("当前章节有未保存修改,恢复正文会丢弃这些本地修改。是否继续?")) return;
|
|
5437
|
+
if (!(await confirmDiscardChanges("当前章节有未保存修改,恢复正文会丢弃这些本地修改。是否继续?"))) return;
|
|
4824
5438
|
button.disabled = true;
|
|
4825
5439
|
cancelChapterAutoSave();
|
|
4826
5440
|
const workId = state.work.id;
|
|
@@ -4901,11 +5515,13 @@ async function showChapterInsight() {
|
|
|
4901
5515
|
panel.innerHTML = `<strong>章节概览${esc(stale)}</strong>${esc(insight.summary || "暂无梗概")}${eventNames.length ? `<br><strong>事件</strong>${esc(eventNames.join(";"))}` : ""}${insight.uncertainties.length ? `<br><strong>待确认</strong>${esc(insight.uncertainties.map((item) => typeof item === "string" ? item : JSON.stringify(item)).join(";"))}` : ""}`;
|
|
4902
5516
|
}
|
|
4903
5517
|
|
|
4904
|
-
$("#home-button").addEventListener("click", () => {
|
|
4905
|
-
if (!confirmDiscardChanges()) return;
|
|
5518
|
+
$("#home-button").addEventListener("click", async () => {
|
|
5519
|
+
if (!(await confirmDiscardChanges())) return;
|
|
4906
5520
|
loadWorks().catch((error) => toast(error.message, "error"));
|
|
4907
5521
|
});
|
|
4908
|
-
$("#settings-button").addEventListener("click",
|
|
5522
|
+
$("#settings-button").addEventListener("click", () => {
|
|
5523
|
+
void showSettingsHub();
|
|
5524
|
+
});
|
|
4909
5525
|
$("#account-button").addEventListener("click", () => {
|
|
4910
5526
|
const expanded = $("#account-menu").classList.toggle("hidden") === false;
|
|
4911
5527
|
$("#account-button").setAttribute("aria-expanded", String(expanded));
|
|
@@ -4986,7 +5602,7 @@ $("#avatar-file").addEventListener("change", async (event) => {
|
|
|
4986
5602
|
}
|
|
4987
5603
|
});
|
|
4988
5604
|
$("#avatar-remove-button").addEventListener("click", async () => {
|
|
4989
|
-
if (!state.user?.avatarUrl || !
|
|
5605
|
+
if (!state.user?.avatarUrl || !(await confirmToast("确定移除当前头像吗?", { title: "移除头像", confirmLabel: "确认移除" }))) return;
|
|
4990
5606
|
$("#avatar-upload-button").disabled = true;
|
|
4991
5607
|
$("#avatar-remove-button").disabled = true;
|
|
4992
5608
|
try {
|
|
@@ -5014,14 +5630,35 @@ $("#profile-form").addEventListener("submit", async (event) => {
|
|
|
5014
5630
|
$("#password-form").addEventListener("submit", async (event) => {
|
|
5015
5631
|
event.preventDefault();
|
|
5016
5632
|
const form = new FormData(event.currentTarget);
|
|
5633
|
+
const newPassword = String(form.get("newPassword") ?? "");
|
|
5634
|
+
const passwordConfirmation = String(form.get("passwordConfirmation") ?? "");
|
|
5635
|
+
if (newPassword !== passwordConfirmation) {
|
|
5636
|
+
const confirmationInput = $("#password-form input[name='passwordConfirmation']");
|
|
5637
|
+
confirmationInput.setCustomValidity("两次输入的密码不一致");
|
|
5638
|
+
confirmationInput.reportValidity();
|
|
5639
|
+
confirmationInput.focus();
|
|
5640
|
+
return;
|
|
5641
|
+
}
|
|
5017
5642
|
try {
|
|
5018
|
-
await api("/api/auth/password", { method: "PATCH", body: { currentPassword: form.get("currentPassword"), newPassword
|
|
5643
|
+
await api("/api/auth/password", { method: "PATCH", body: { currentPassword: form.get("currentPassword"), newPassword, passwordConfirmation } });
|
|
5019
5644
|
event.currentTarget.reset();
|
|
5020
5645
|
toast("密码已更新,其他设备的会话已退出");
|
|
5021
5646
|
} catch (error) { toast(error.message, "error"); }
|
|
5022
5647
|
});
|
|
5648
|
+
function validatePasswordChangeConfirmation() {
|
|
5649
|
+
const newPasswordInput = $("#password-form input[name='newPassword']");
|
|
5650
|
+
const confirmationInput = $("#password-form input[name='passwordConfirmation']");
|
|
5651
|
+
const matches = !confirmationInput.value || newPasswordInput.value === confirmationInput.value;
|
|
5652
|
+
confirmationInput.setCustomValidity(matches ? "" : "两次输入的密码不一致");
|
|
5653
|
+
return matches;
|
|
5654
|
+
}
|
|
5655
|
+
$("#password-form input[name='newPassword']").addEventListener("input", validatePasswordChangeConfirmation);
|
|
5656
|
+
$("#password-form input[name='passwordConfirmation']").addEventListener("input", validatePasswordChangeConfirmation);
|
|
5023
5657
|
$("#api-key-reset-button").addEventListener("click", async () => {
|
|
5024
|
-
if ($("#api-key-reset-button").textContent.includes("重置") && !
|
|
5658
|
+
if ($("#api-key-reset-button").textContent.includes("重置") && !(await confirmToast(
|
|
5659
|
+
"重置后,所有使用旧 API Key 的 CLI 会立刻退出登录。确定继续吗?",
|
|
5660
|
+
{ title: "重置 API Key", confirmLabel: "确认重置" }
|
|
5661
|
+
))) return;
|
|
5025
5662
|
try {
|
|
5026
5663
|
const result = await api("/api/auth/api-key/reset", { method: "POST", body: {} });
|
|
5027
5664
|
$("#api-key-status").textContent = `已配置 ${result.prefix}…,尚未使用`;
|
|
@@ -5195,24 +5832,26 @@ $("#member-permission-form").addEventListener("submit", async (event) => {
|
|
|
5195
5832
|
$("#platform-new-provider").addEventListener("click", () => openProviderDialog());
|
|
5196
5833
|
$("#shelf-new-work").addEventListener("click", openWorkDialog);
|
|
5197
5834
|
$("#welcome-new-work").addEventListener("click", () => state.work ? openChapterDialog() : openWorkDialog());
|
|
5198
|
-
$("#new-chapter-button").addEventListener("click", openChapterDialog);
|
|
5199
5835
|
$("#save-button").addEventListener("click", saveChapter);
|
|
5200
5836
|
$("#tidy-blank-lines-button").addEventListener("click", tidyChapterBlankLines);
|
|
5201
5837
|
$("#new-volume-button").addEventListener("click", () => openVolumeDialog());
|
|
5202
5838
|
$("#insight-button").addEventListener("click", () => showChapterInsight().catch((error) => toast(error.message, "error")));
|
|
5203
5839
|
$("#versions-button").addEventListener("click", showVersions);
|
|
5204
5840
|
$("#versions-close").addEventListener("click", () => $("#versions-dialog").close());
|
|
5205
|
-
$("#import-history-button").addEventListener("click", () => openImportHistory());
|
|
5206
5841
|
$("#import-history-close").addEventListener("click", () => $("#import-history-dialog").close());
|
|
5207
5842
|
$("#entity-history-close").addEventListener("click", () => $("#entity-history-dialog").close());
|
|
5208
5843
|
$("#ai-tool-call-close").addEventListener("click", () => $("#ai-tool-call-dialog").close());
|
|
5209
5844
|
$("#setting-editor-back").addEventListener("click", () => { void closeEntityEditor(); });
|
|
5210
5845
|
$("#character-editor-close").addEventListener("click", () => { void closeEntityEditor(); });
|
|
5211
5846
|
$("#character-editor-cancel").addEventListener("click", () => { void closeEntityEditor(); });
|
|
5847
|
+
$("#knowledge-editor-close").addEventListener("click", () => { void closeEntityEditor(); });
|
|
5848
|
+
$("#knowledge-editor-cancel").addEventListener("click", () => { void closeEntityEditor(); });
|
|
5212
5849
|
$("#setting-editor-form").addEventListener("input", markEntityEditorDirty);
|
|
5213
5850
|
$("#setting-editor-form").addEventListener("change", markEntityEditorDirty);
|
|
5214
5851
|
$("#character-editor-form").addEventListener("input", markEntityEditorDirty);
|
|
5215
5852
|
$("#character-editor-form").addEventListener("change", markEntityEditorDirty);
|
|
5853
|
+
$("#knowledge-editor-form").addEventListener("input", markEntityEditorDirty);
|
|
5854
|
+
$("#knowledge-editor-form").addEventListener("change", markEntityEditorDirty);
|
|
5216
5855
|
$("#character-editor-fields").addEventListener("click", (event) => {
|
|
5217
5856
|
if (event.target.closest("[data-item-list-add], [data-structured-list-add], [data-item-list-remove], [data-structured-list-remove]")) markEntityEditorDirty();
|
|
5218
5857
|
});
|
|
@@ -5339,6 +5978,13 @@ $("#ai-mention-menu").addEventListener("click", (event) => {
|
|
|
5339
5978
|
const button = event.target.closest("[data-ai-reference-id]");
|
|
5340
5979
|
if (button) selectAiMention(button);
|
|
5341
5980
|
});
|
|
5981
|
+
$("#import-file-button").addEventListener("click", (event) => {
|
|
5982
|
+
if (!state.work || canEditProse()) return;
|
|
5983
|
+
event.preventDefault();
|
|
5984
|
+
event.stopPropagation();
|
|
5985
|
+
$("#import-file").value = "";
|
|
5986
|
+
toast("当前权限只能编辑设定资料,不能导入正文", "error");
|
|
5987
|
+
});
|
|
5342
5988
|
$("#import-file").addEventListener("change", async (event) => {
|
|
5343
5989
|
const file = event.target.files[0];
|
|
5344
5990
|
if (!state.work || !file) return;
|
|
@@ -5352,6 +5998,13 @@ $("#import-file").addEventListener("change", async (event) => {
|
|
|
5352
5998
|
event.target.value = "";
|
|
5353
5999
|
return;
|
|
5354
6000
|
}
|
|
6001
|
+
if (mode === "overwrite" && !(await confirmToast(
|
|
6002
|
+
`当前选项:覆盖正文。确认后将使用“${file.name}”替换《${state.work.title}》的现有正文目录。`,
|
|
6003
|
+
{ title: "覆盖正文二次确认", confirmLabel: "确认覆盖" }
|
|
6004
|
+
))) {
|
|
6005
|
+
event.target.value = "";
|
|
6006
|
+
return;
|
|
6007
|
+
}
|
|
5355
6008
|
cancelChapterAutoSave();
|
|
5356
6009
|
const body = new FormData();
|
|
5357
6010
|
body.append("file", file);
|