pull/7988/head
vickydotbat 3 weeks ago
parent ded41321f2
commit 07aefe4268

@ -0,0 +1,200 @@
# AGENTS.md
## Project
This repository is the Wiki.js application used for the Shadows Over Westgate wiki.
The active custom theme is `westgate`, located under `client/themes/westgate`. The goal is to match the Shadows Over Westgate NodeBB theme as closely as Wiki.js permits, ideally 1:1 in visual language, interaction polish, and atmosphere while respecting Wiki.js architecture.
## Local Environment
- Wiki repo: `/home/vicky/Projects/nwnee-shadowsoverwestgate/wiki`
- NodeBB theme reference repo: `/home/vicky/Projects/nodebb-dev/nodebb-theme-westgate`
- NodeBB theme instructions: `/home/vicky/Projects/nodebb-dev/nodebb-theme-westgate/AGENTS.md`
- This Wiki.js checkout targets Wiki.js 2.x and Node `>=20`, as declared in `package.json`.
- Wiki.js assets are built through webpack from the repo root.
## Theme Direction
Use the NodeBB Westgate theme as the source of truth.
The visual target is black velvet silk, darkness, vampirism, decadence, and decay.
Use this palette direction:
- Plum and near-black as the dominant base.
- Muted gold for accents, borders, small highlights, and important affordances.
- Red only sparingly, preferably as a subtle detail or state color.
- Avoid bright, playful, clinical, or flat SaaS-style treatment.
- Favor depth, restraint, texture, and rich contrast over loud ornament.
Before adding new visual language, inspect the NodeBB reference files in `/home/vicky/Projects/nodebb-dev/nodebb-theme-westgate`, especially:
- `____live-copy.css`
- `____wikijs-live-copy.scss`
- `____bootstrap-overrides.scss`
`____live-copy.css` is the clearest reference for the slick gradient treatment. Port that direction into Wiki.js foreground elements such as page chrome, navigation, panels, buttons, cards, tables, forms, alerts, active states, borders, and links. Keep the wiki readable and usable; do not turn the whole page into a decorative gradient background.
## Wiki.js Theme Rules
Follow the current Wiki.js theme model:
- Theme metadata lives in `client/themes/westgate/theme.yml`.
- Theme styles live in `client/themes/westgate/scss/app.scss`.
- Theme-specific JavaScript lives in `client/themes/westgate/js/app.js`.
- Theme Vue components live in `client/themes/westgate/components/`.
- `client/themes/westgate/components/page.vue` is the mandatory theme entry component.
- `client/themes/westgate/js/app.js` and `client/themes/westgate/scss/app.scss` are mandatory entry files, even if one is temporarily empty.
- `client/themes/westgate/thumbnail.png` is the theme preview image.
- The active theme components are bundled from `client/themes/<siteConfig.theme>/components/` by `client/client-app.js`.
- `Page` and `NavFooter` are registered as theme components in `client/client-app.js`.
- Other page-level behavior and app chrome may come from shared Wiki.js components under `client/components/`.
- Folder names for themes must be lowercase and avoid spaces or special characters. Keep this theme folder named `westgate`.
- This checkout's page component and admin UI use `off` as the hidden table-of-contents value, even though some upstream theme metadata examples use `hidden`.
Official Wiki.js theme docs: https://docs.requarks.io/dev/themes
The theme must be built together with the application components. Styling only the theme SCSS is often insufficient because Wiki.js renders a mix of:
- Theme components in `client/themes/westgate/components/`.
- Shared Vue components in `client/components/`.
- Server Pug views in `server/views/`.
- Global SCSS in `client/scss/`.
- Vuetify-generated markup and utility classes.
Prefer changes in `client/themes/westgate` when the behavior or markup is theme-owned. Touch shared `client/components`, `client/scss`, or `server/views` only when the rendered Wiki.js surface cannot be matched from the theme layer alone.
Wiki.js officially treats custom themes as a developer workflow: they are manually compiled as part of the application bundle, not installed or switched like downloadable packages.
The top black navigation header is shared across the wiki, admin, editor, profile, and other app surfaces. Official docs say the header cannot be customized as part of a normal content-page theme. Prefer styling it from SCSS only when needed for visual parity; do not assume it can be replaced from `client/themes/westgate/components`.
When editing `client/themes/westgate/components/page.vue`, keep the `props` object and `created()` method intact. Wiki.js uses them to persist page data to the store, and changing them can break page rendering across the application.
Do not modify `client/themes/default` unless the task explicitly asks to update the default theme or compare behavior. Use it as a reference for expected component structure.
## Current Files Of Interest
- `client/themes/westgate/theme.yml`: Westgate theme metadata and theme props.
- `client/themes/westgate/scss/app.scss`: main Westgate visual layer.
- `client/themes/westgate/js/app.js`: theme-specific browser behavior.
- `client/themes/westgate/components/page.vue`: main wiki page layout for the theme.
- `client/themes/westgate/components/nav-sidebar.vue`: page contents/sidebar navigation component.
- `client/themes/westgate/components/nav-footer.vue`: footer navigation component.
- `client/themes/westgate/components/tabset.vue`: theme tabset component.
- `client/client-app.js`: global Vue registration, including theme component imports.
- `client/scss/`: global Wiki.js and Vuetify styling layers.
- `server/views/`: Pug views that host rendered Vue app pages.
- `dev/webpack/webpack.dev.js` and `dev/webpack/webpack.prod.js`: asset build configuration.
## Working Practices
- Keep edits scoped to the wiki project unless the task explicitly names the NodeBB theme repo.
- Treat `/home/vicky/Projects/nodebb-dev/nodebb-theme-westgate` as visual reference material, not as a target for wiki edits.
- Prefer existing Wiki.js, Vue 2, Vuetify, Pug, SCSS, and local theme patterns over introducing new frameworks.
- Keep selectors maintainable and tied to Wiki.js/Vuetify structure where possible.
- Avoid broad global overrides unless they are intentional theme-wide tokens, resets, or Vuetify corrections.
- Avoid unrelated formatting churn.
- Preserve accessibility: readable contrast, visible focus states, keyboard-usable controls, meaningful hover/active states, and no text hidden purely for visual effect unless there is an accessible alternative.
- When porting NodeBB styling, translate the visual intent to Wiki.js markup instead of copying selectors that only make sense in NodeBB.
- When editing Vue components, keep template, script, and style behavior aligned with the default Wiki.js component unless a Westgate-specific override is intentional.
- Keep `theme.yml` prop keys unique and meaningful; avoid duplicate YAML keys.
## Build And Validation
Run commands from `/home/vicky/Projects/nwnee-shadowsoverwestgate/wiki`.
Prefer the devcontainer / Docker Compose workflow for this project. Host-side Node commands may fail because dependencies are expected to live inside the `wiki-app` container.
Dev container workflow:
- Compose file: `dev/containers/docker-compose.yml`.
- App container: `wiki-app`.
- Database container: `wiki-db`.
- Adminer container: `wiki-adminer`.
- Workspace inside the app container: `/wiki`.
- The host repo is bind-mounted into `/wiki`.
- `node_modules` is container-local via the `/wiki/node_modules` volume and should not be expected on the host.
- Wiki.js is exposed at `http://localhost:3000`.
- Adminer is exposed at `http://localhost:3001`.
- Local Postgres is exposed on host port `15432`, container port `5432`.
- Container config lives at `dev/containers/config.yml`; copy it to `config.yml` inside `/wiki` if needed.
Common host commands:
- `docker compose -f dev/containers/docker-compose.yml up -d --build`: build and start the dev database, app, and Adminer containers.
- `docker exec -it wiki-app bash`: open a shell in the Wiki.js app container.
Common container commands from `/wiki`:
- `yarn install`: install dependencies into the container-local `node_modules` volume.
- `cp dev/containers/config.yml config.yml`: use the dev Postgres config.
- `yarn dev`: official Wiki.js development workflow; theme changes automatically rebuild client assets and live-reload while it is running. During active visual iteration, prefer this live reload loop instead of repeatedly running `yarn build`.
- `yarn build` or `npm run build`: production webpack build for app, legacy, setup, theme SCSS, theme JS, and Vue components.
- `yarn test` or `npm run test`: eslint, pug-lint, and jest.
Dev database settings from inside Docker:
- Type: `postgres`
- Host: `db`
- Port: `5432`
- Database: `wiki`
- User: `wikijs`
- Password: `wikijsrocks`
Common commands:
- `yarn dev`: official Wiki.js development workflow; theme changes automatically rebuild client assets.
- `npm run watch`: development webpack build for app, legacy, setup, theme SCSS, theme JS, and Vue components.
- `npm run build`: production webpack build for app, legacy, setup, theme SCSS, theme JS, and Vue components.
- `npm run dev`: Wiki.js development server workflow.
- `npm run start`: start the Wiki.js server.
- `npm run test`: eslint, pug-lint, and jest.
Important build note:
- The Westgate theme is not just a standalone stylesheet. It depends on the theme Vue components and shared Wiki.js components being bundled together. After changing `client/themes/westgate/components`, `client/themes/westgate/scss`, `client/themes/westgate/js`, `client/components`, `client/scss`, or `server/views`, rebuild the webpack assets.
- If `yarn dev` is already running, use its live reload / automatic rebuild capability during iteration; a separate `yarn build` is not normally needed until final production-style validation.
- Production build output includes the generated `assets` folder and generated views under `server/views`.
- The active custom theme is selected from the database `settings` table, `theming` row, JSON `theme` property. For this project it should be `westgate`; restart Wiki.js after changing it.
Validation checklist when practical:
- Build assets inside the `wiki-app` container with `yarn build`, or run `yarn dev` during iterative work.
- Verify that the active wiki theme is `westgate`.
- Check desktop and mobile widths.
- Check representative page content: headings, paragraphs, links, tables, code blocks, blockquotes, tags, page metadata, table of contents/sidebar, footer navigation, and edit controls.
- Check shared app surfaces affected by theme work: login, register, profile, search, not found, unauthorized, and editor/new-page flows when relevant.
- Use Playwright for rendered route checks and screenshots when the wiki server is running.
If validation cannot be run, report what changed and what still needs to be checked in the running wiki.
## Current Tasks
Complete and update as needed. Future-dated tasks and possibilities go into Future Tasks.
Statuses:
1. `[x]` marks total completion.
2. `[-]` marks partial completion: not exact specification.
3. `[ ]` marks incomplete.
4. `[?]` marks uncertainty or exception: treat as incomplete, expand as needed, and keep in scope.
Complete the following:
- [-] Bring the Wiki.js Westgate theme into close visual parity with the NodeBB Westgate theme. Continued with shared chrome, search, auth, menu, table, media, focus, content-surface polish, and multiple geometry cleanup passes in `client/themes/westgate/scss/app.scss`.
- [x] Build the theme together with Wiki.js components after theme/component changes. `yarn build` completed successfully on April 22, 2026.
- [ ] Validate the rendered wiki on desktop and mobile against the NodeBB visual direction. Pending because the Wiki.js server was not running on `localhost:3000` in the current container and Playwright is not installed here.
- [-] Reduce boxes-within-boxes across the Westgate theme. Removed card styling from structural page columns, softened sidebar cards/TOC rows, restored pilcrow anchors as overlay markers that do not indent headings, and simplified top nav/search/editor title input borders from SCSS. Continue checking the shared `nav-header` and editor title/search wrappers; exact 1:1 alignment may require editing shared Vue markup in `client/components/common/nav-header.vue` if SCSS cannot fully undo Vuetify wrapper nesting.
- [-] Improve editor/read-view parity. Markdown editor line-card striping was fixed by exempting CodeMirror line elements from global code-block styling. The WYSIWYG/preview pane still does not show the same gold heading/pilcrow marker behavior as the read view; treat this as optional unless visual parity there becomes a priority.
- [ ] Re-check body/header separation on real content pages. Current SCSS reduces page title height and tightens page body spacing, but screenshots should be reviewed after live reload to decide whether page title and article content should become one continuous component or remain separate Wiki.js regions.
- [-] Fix current layout overlap regression. Removed the negative sticky side-rail offset from `client/themes/westgate/components/page.vue`, because it caused the TOC/sidebar column to overlap the top page chrome and content region. Continue checking for remaining overlap after live reload.
- [-] Document VPS deployment. `README.md` was cleared and rewritten as a Wiki.js 2.x VPS deployment guide for this custom theme, using the repo's `dev/build/Dockerfile` custom image path. It references official Wiki.js installation, Docker, and requirements docs. Validate commands against the real production repo URL, hostname, and volume names before first live deployment.
- [-] Last geometry simplification pass before handoff. Added explicit Westgate classes to the breadcrumb toolbar/body container, removed inline page title height, flattened breadcrumb boxes, removed most side-rail/card borders, and simplified the content panel edges. Remaining navbar/search double-border issues likely come from shared Vuetify/nav-header wrapper markup and should be handled in `client/components/common/nav-header.vue` by the next pass rather than by piling on more SCSS overrides.
## Future Tasks
Tasks to be considered in the future, not implemented immediately. Keep them in scope when making decisions.
- [ ] Consider a light mode/dark mode toggle only if it can retain the core Westgate identity.
- [ ] Consider extracting repeated Westgate SCSS tokens/mixins if theme styling continues to grow.

@ -1,527 +1,303 @@
<div align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://static.requarks.io/logo/wikijs-full-darktheme.svg">
<img alt="Wiki.js" src="https://static.requarks.io/logo/wikijs-full.svg" width="600">
</picture>
[![Release](https://img.shields.io/github/release/Requarks/wiki.svg?style=flat&maxAge=3600)](https://github.com/Requarks/wiki/releases)
[![License](https://img.shields.io/badge/license-AGPLv3-blue.svg?style=flat)](https://github.com/requarks/wiki/blob/master/LICENSE)
[![Standard - JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-green.svg?style=flat&logo=javascript&logoColor=white)](http://standardjs.com/)
[![Build + Publish](https://github.com/Requarks/wiki/actions/workflows/build.yml/badge.svg)](https://github.com/Requarks/wiki/actions/workflows/build.yml)
[![GitHub Sponsors](https://img.shields.io/github/sponsors/ngpixel?logo=github&color=ea4aaa)](https://github.com/users/NGPixel/sponsorship)
[![Open Collective backers and sponsors](https://img.shields.io/opencollective/all/wikijs?label=backers&color=218bff&logo=opencollective&logoColor=white)](https://opencollective.com/wikijs)
[![Downloads](https://img.shields.io/github/downloads/Requarks/wiki/total.svg?style=flat&logo=github)](https://github.com/Requarks/wiki/releases)
[![Docker Pulls](https://img.shields.io/docker/pulls/requarks/wiki.svg?logo=docker&logoColor=white)](https://hub.docker.com/r/requarks/wiki/)
[![Chat on Discord](https://img.shields.io/badge/discord-join-8D96F6.svg?style=flat&logo=discord&logoColor=white)](https://discord.gg/rcxt9QS2jd)
[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40js.wiki-blue.svg?style=flat&logo=bluesky&logoColor=white)](https://bsky.app/profile/js.wiki)
[![Follow on Telegram](https://img.shields.io/badge/telegram-%40wiki__js-blue.svg?style=flat&logo=telegram)](https://t.me/wiki_js)
[![Reddit](https://img.shields.io/badge/reddit-%2Fr%2Fwikijs-orange?logo=reddit&logoColor=white)](https://www.reddit.com/r/wikijs/)
##### A modern, lightweight and powerful wiki app built on NodeJS
</div>
- **[Official Website](https://js.wiki/)**
- **[Documentation](https://docs.requarks.io/)**
- [Requirements](https://docs.requarks.io/install/requirements)
- [Installation](https://docs.requarks.io/install)
- [Demo](https://docs.requarks.io/demo)
- [Changelog](https://github.com/requarks/wiki/releases)
- [Feature Requests](https://feedback.js.wiki/wiki)
- Chat with us on [Discord](https://discord.gg/rcxt9QS2jd)
- [Translations](https://docs.requarks.io/dev/translations) *(We need your help!)*
- [Special Thanks](#special-thanks)
- [Contribute](#contributors)
[Follow our Twitter feed](https://twitter.com/requarks) to learn about upcoming updates and new releases!
<h2 align="center">Donate</h2>
<div align="center">
Wiki.js is an open source project that has been made possible due to the generous contributions by community [backers](https://js.wiki/about). If you are interested in supporting this project, please consider [becoming a sponsor](https://github.com/users/NGPixel/sponsorship), [becoming a patron](https://www.patreon.com/requarks), donating to our [OpenCollective](https://opencollective.com/wikijs), via [Paypal](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url) or via Ethereum (`0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5`).
[![Become a Sponsor](https://img.shields.io/badge/donate-github-ea4aaa.svg?style=popout&logo=github)](https://github.com/users/NGPixel/sponsorship)
[![Become a Patron](https://img.shields.io/badge/donate-patreon-orange.svg?style=popout&logo=patreon)](https://www.patreon.com/requarks)
[![Donate on OpenCollective](https://img.shields.io/badge/donate-open%20collective-blue.svg?style=popout&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIyNTZweCIgaGVpZ2h0PSIyNTZweCIgdmlld0JveD0iMCAwIDI1NiAyNTYiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayIgcHJlc2VydmVBc3BlY3RSYXRpbz0ieE1pZFlNaWQiPjxnPjxwYXRoIGQ9Ik0yMDkuNzY1MTQ0LDEyOC4xNDk5NzkgQzIwOS43NjUxNDQsMTQ0LjE2MzMgMjA0Ljg2NDM4MSwxNTkuNDg5ODkgMTk2LjQ5ODc0NywxNzIuNzI1MDcyIEwyMjkuOTQ1Njc1LDIwNi4xNzE5OTkgQzI0Ni42ODIxMDUsMTgzLjg1Njc1OSAyNTUuNzI5MzA3LDE1Ni43MTUxNTIgMjU1LjcyOTMwNywxMjguODIxMTAyIEMyNTUuNzI5MzA3LDk5LjU1Njk5MTcgMjQ1Ljk3NDYwMyw3My4wNzEwMjA3IDIyOS4yNTg5NDQsNTEuNDg1ODEyOCBMMTk2LjQ4MzE0LDg0LjIxNDc5NCBDMjA1LjEyMjU2MSw5Ny4yMjI0NjgzIDIwOS43MzY5MDcsMTEyLjQ4NzgxIDIwOS43NDk1MzcsMTI4LjEwMzE1NiBMMjA5Ljc2NTE0NCwxMjguMTQ5OTc5IFoiIGZpbGw9IiNCOEQzRjQiPjwvcGF0aD48cGF0aCBkPSJNMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEM4Mi4xNDYwODcyLDIxMC4yNjg5NTggNDUuMzg3NTA5NCwxNzMuNTE3MzU4IDQ1LjI5MzAzOTMsMTI4LjE0OTk3OSBDNDUuMzYxNzUwMiw4Mi43NjQzMTM4IDgyLjEyNzg0ODcsNDUuOTg0MjU3IDEyNy41MTM0ODQsNDUuODk4MzE4NiBDMTQ0LjI0NDc1Miw0NS44OTgzMTg2IDE1OS41NzEzNDIsNTAuNzk5MDgxNyAxNzIuMTE5NzkyLDU5LjE2NDcxNTQgTDIwNC44NjQzODEsMjYuMzg4OTExNiBDMTgyLjU0MzY1LDkuNjY2NjUxMjkgMTU1LjQwMzQyOSwwLjYzMDg2MzI5OCAxMjcuNTEzNDg0LDAuNjM2NDk0NDAzIEM1Ny4xMjM1NDM3LDAuNjM2NDk0NDAzIDAsNTcuNzYwMDM4MSAwLDEyOC4xNDk5NzkgQzAsMTk4LjUwODcwNCA1Ny4xMjM1NDM3LDI1NS42NjM0NjMgMTI3LjUxMzQ4NCwyNTUuNjYzNDYzIEMxNTUuNTM3MzUyLDI1NS43NDA4NzYgMTgyLjc3NTk4OSwyNDYuNDA4NTEgMjA0Ljg2NDM4MSwyMjkuMTYxODg0IEwxNzEuNDE3NDU0LDE5NS43MzA1NjQgQzE1OS41NTU3MzQsMjA1LjQ4NTI2OCAxNDQuMjYwMzU5LDIxMC4zNTQ4MTYgMTI3LjUxMzQ4NCwyMTAuMzU0ODE2IEwxMjcuNTEzNDg0LDIxMC4zNTQ4MTYgWiIgZmlsbD0iIzdGQURGMiI+PC9wYXRoPjwvZz48L3N2Zz4=)](https://opencollective.com/wikijs)
[![Donate via Paypal](https://img.shields.io/badge/donate-paypal-blue.svg?style=popout&logo=paypal)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=FLV5X255Z9CJU&source=url)
[![Donate via Ethereum](https://img.shields.io/badge/donate-ethereum-999.svg?style=popout&logo=ethereum&logoColor=CCC)](https://etherscan.io/address/0xe1d55c19ae86f6bcbfb17e7f06ace96bdbb22cb5)
[![Donate via Bitcoin](https://img.shields.io/badge/donate-bitcoin-ff9900.svg?style=popout&logo=bitcoin&logoColor=CCC)](https://checkout.opennode.com/p/2553c612-f863-4407-82b3-1a7685268747)
[![Buy a T-Shirt](https://img.shields.io/badge/buy-t--shirts-teal.svg?style=popout&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHg9IjBweCIgeT0iMHB4Igp3aWR0aD0iMjQiIGhlaWdodD0iMjQiCnZpZXdCb3g9IjAgMCAxOTIgMTkyIgpzdHlsZT0iIGZpbGw6IzAwMDAwMDsiPjxnIGZpbGw9Im5vbmUiIGZpbGwtcnVsZT0ibm9uemVybyIgc3Ryb2tlPSJub25lIiBzdHJva2Utd2lkdGg9IjEiIHN0cm9rZS1saW5lY2FwPSJidXR0IiBzdHJva2UtbGluZWpvaW49Im1pdGVyIiBzdHJva2UtbWl0ZXJsaW1pdD0iMTAiIHN0cm9rZS1kYXNoYXJyYXk9IiIgc3Ryb2tlLWRhc2hvZmZzZXQ9IjAiIGZvbnQtZmFtaWx5PSJub25lIiBmb250LXdlaWdodD0ibm9uZSIgZm9udC1zaXplPSJub25lIiB0ZXh0LWFuY2hvcj0ibm9uZSIgc3R5bGU9Im1peC1ibGVuZC1tb2RlOiBub3JtYWwiPjxwYXRoIGQ9Ik0wLDE5MnYtMTkyaDE5MnYxOTJ6IiBmaWxsPSJub25lIj48L3BhdGg+PGcgZmlsbD0iIzFhYmM5YyI+PGcgaWQ9InN1cmZhY2UxIj48cGF0aCBkPSJNOTYsMGMtMTUuMjE4NzUsMCAtMjQuNjg3NSwzLjY1NjI1IC0yNS41LDRsLTIyLjUsNy4yNWMtMTAuNDA2MjUsMy4xODc1IC0xOS4wOTM3NSw5LjQzNzUgLTI1LjUsMTguMjVsLTIyLjUsNDIuNWwyNy4yNSwxNi43NWwxMi43NSwtMjR2MTE5LjI1YzAsNC40MDYyNSAyNS4wNjI1LDggNTYsOGMzMC45Mzc1LDAgNTYsLTMuNTkzNzUgNTYsLTh2LTExOS4yNWwxMi43NSwyNGwyNy4yNSwtMTYuNzVsLTIyLjUsLTQyLjVjLTYuNDA2MjUsLTguODEyNSAtMTUuMTU2MjUsLTE1LjA2MjUgLTI0Ljc1LC0xOC4yNWwtMjIuMjUsLTcuMjVjLTAuMTg3NSwwIC0xLjAzMTI1LDEuMzEyNSAtMiwyLjc1bDEuMjUsLTIuNWMwLDAgLTkuODQzNzUsLTQuMjUgLTI1Ljc1LC00LjI1ek05Niw4YzExLjQwNjI1LDAgMTguNDM3NSwyLjI1IDIxLDMuMjVjLTQuNDY4NzUsNS43NSAtMTEuNDA2MjUsMTIuNzUgLTIxLDEyLjc1Yy05LjQwNjI1LDAgLTE2LjQwNjI1LC03LjA2MjUgLTIwLjc1LC0xMi43NWMyLjg3NSwtMS4wNjI1IDkuODc1LC0zLjI1IDIwLjc1LC0zLjI1eiI+PC9wYXRoPjwvZz48L2c+PC9nPjwvc3ZnPg==)](https://wikijs.threadless.com)
</div>
<h2 align="center">Gold Tier Sponsors</h2>
<div align="center">
<table>
<tbody>
<tr>
<td align="center" valign="middle" width="444">
<a href="https://trans-zero.com/" target="_blank">
<img src="https://cdn.js.wiki/images/sponsors/transzero.png">
</a>
</td>
</tr>
</tbody>
</table>
</div>
<h2 align="center">GitHub Sponsors</h2>
Support this project by becoming a sponsor. Your name will show up in the Contribute page of all Wiki.js installations as well as here with a link to your website! [[Become a sponsor](https://github.com/users/NGPixel/sponsorship)]
<div align="center">
<table>
<tbody>
<tr>
<td align="center" valign="middle" width="444">
<a href="https://www.stellarhosted.com/" target="_blank">
<img src="https://cdn.js.wiki/images/sponsors/stellarhosted.png">
</a>
</td>
</tr>
</tbody>
</table>
</div>
<div align="center">
<table>
<tbody>
<tr>
<td align="center" valign="middle" width="130">
<a href="https://acceleanation.com/" target="_blank">
<img src="https://avatars.githubusercontent.com/u/41210718?s=200&v=4">
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/alexksso" target="_blank">
Alexander Casassovici<br />(@alexksso)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/broxen" target="_blank">
Broxen<br />(@broxen)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/xDacon" target="_blank">
Dacon<br />(@xDacon)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/DonNabla" target="_blank">
Maxime Pierre<br />(@DonNabla)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/GigabiteLabs" target="_blank">
<img src="https://static.requarks.io/sponsors/gigabitelabs-148x129.png">
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://www.hostwiki.com/" target="_blank">
<img src="https://cdn.js.wiki/images/sponsors/hostwiki.png">
</a>
</td>
</tr>
<tr>
<td align="center" valign="middle" width="130">
<a href="https://github.com/JayDaley" target="_blank">
Jay Daley<br />(@JayDaley)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/idokka" target="_blank">
Oleksii<br />(@idokka)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://www.openhost-network.com/" target="_blank">
<img src="https://avatars.githubusercontent.com/u/114218287?s=200&v=4">
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://www.prevo.ch/" target="_blank">
<img src="https://avatars.githubusercontent.com/u/114394792?v=4">
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="https://github.com/shanekearney" target="_blank">
Shane Kearney<br />(@shanekearney)
</a>
</td>
<td align="center" valign="middle" width="130">
<a href="http://www.taicep.org/" target="_blank">
<img src="https://avatars.githubusercontent.com/u/160072306?v=4">
</a>
</td>
<td align="center" valign="middle" width="130"></td>
</tr>
</tbody>
</table>
<table><tbody><tr><td>
<img width="441" height="1" />
- Akira Suenami ([@a-suenami](https://github.com/a-suenami))
- Armin Reiter ([@arminreiter](https://github.com/arminreiter))
- Arnaud Marchand ([@snuids](https://github.com/snuids))
- Brian Douglass ([@bhdouglass](https://github.com/bhdouglass))
- Bryon Vandiver ([@asterick](https://github.com/asterick))
- Cameron Steele ([@ATechAdventurer](https://github.com/ATechAdventurer))
- Charlie Schliesser ([@charlie-s](https://github.com/charlie-s))
- Cloud Data Hosting LLC ([@CloudDataHostingLLC](https://github.com/CloudDataHostingLLC))
- Cole Manning ([@RVRX](https://github.com/RVRX))
- CrazyMarvin ([@CrazyMarvin](https://github.com/CrazyMarvin))
- Daniel Horner ([@danhorner](https://github.com/danhorner))
- David Christian Holin ([@SirGibihm](https://github.com/SirGibihm))
- Dragan Espenschied ([@despens](https://github.com/despens))
- Elijah Zobenko ([@he110](https://github.com/he110))
- Emerson-Perna ([@Emerson-Perna](https://github.com/Emerson-Perna))
- Ernie ([@iamernie](https://github.com/iamernie))
- Fabio Ferrari ([@devxops](https://github.com/devxops))
- Finsa S.p.A. ([@finsaspa](https://github.com/finsaspa))
- Florian Moss ([@florianmoss](https://github.com/florianmoss))
- GoodCorporateCitizen ([@GoodCorporateCitizen](https://github.com/GoodCorporateCitizen))
- HeavenBay ([@HeavenBay](https://github.com/heavenbay))
- HikaruEgashira ([@HikaruEgashira](https://github.com/HikaruEgashira))
- Ian Hyzy ([@ianhyzy](https://github.com/ianhyzy))
- Jaimyn Mayer ([@jabelone](https://github.com/jabelone))
- Jay Lee ([@polyglotm](https://github.com/polyglotm))
- Kelly Wardrop ([@dropcoded](https://github.com/dropcoded))
- Loki ([@binaryloki](https://github.com/binaryloki))
- MaFarine ([@MaFarine](https://github.com/MaFarine))
- Marcilio Leite Neto ([@marclneto](https://github.com/marclneto))
- Mattias Johnson ([@mattiasJohnson](https://github.com/mattiasJohnson))
- Max Ricketts-Uy ([@MaxRickettsUy](https://github.com/MaxRickettsUy))
- Mickael Asseline ([@PAPAMICA](https://github.com/PAPAMICA))
- Mitchell Rowton ([@mrowton](https://github.com/mrowton))
</td><td>
<img width="441" height="1" />
- M. Scott Ford ([@mscottford](https://github.com/mscottford))
- Nick Halase ([@nhalase](https://github.com/nhalase))
- Nick Price ([@DominoTree](https://github.com/DominoTree))
- Nina Reynolds ([@cutecycle](https://github.com/cutecycle))
- Noel Cower ([@nilium](https://github.com/nilium))
- Oleksandr Koltsov ([@crambo](https://github.com/crambo))
- Phi Zeroth ([@phizeroth](https://github.com/phizeroth))
- Philipp Schmitt ([@pschmitt](https://github.com/pschmitt))
- Robert Lanzke ([@winkelement](https://github.com/winkelement))
- Ruizhe Li ([@liruizhe1995](https://github.com/liruizhe1995))
- Sam Martin ([@ABitMoreDepth](https://github.com/ABitMoreDepth))
- Sean Coffey ([@seanecoffey](https://github.com/seanecoffey))
- Simon Ott ([@ottsimon](https://github.com/ottsimon))
- Stephan Kristyn ([@stevek-pro](https://github.com/stevek-pro))
- Theodore Chu ([@TheodoreChu](https://github.com/TheodoreChu))
- Tim Elmer ([@tim-elmer](https://github.com/tim-elmer))
- Tyler Denman ([@tylerguy](https://github.com/tylerguy))
- Victor Bilgin ([@vbilgin](https://github.com/vbilgin))
- VMO Solutions ([@vmosolutions](https://github.com/vmosolutions))
- YazMogg35 ([@YazMogg35](https://github.com/YazMogg35))
- Yu Yongwoo ([@uyu423](https://github.com/uyu423))
- ameyrakheja ([@ameyrakheja](https://github.com/ameyrakheja))
- aniketpanjwani ([@aniketpanjwani](https://github.com/aniketpanjwani))
- aytaa ([@aytaa](https://github.com/aytaa))
- cesar ([@cesarnr21](https://github.com/cesarnr21))
- chaee ([@chaee](https://github.com/chaee))
- lwileczek ([@lwileczek](https://github.com/lwileczek))
- magicpotato ([@fortheday](https://github.com/fortheday))
- motoacs ([@motoacs](https://github.com/motoacs))
- muzian666 ([@muzian666](https://github.com/muzian666))
- rburckner ([@rburckner](https://github.com/rburckner))
- scorpion ([@scorpion](https://github.com/scorpion))
- valantien ([@valantien](https://github.com/valantien))
</td></tr></tbody></table>
</div>
<h2 align="center">OpenCollective Sponsors</h2>
Support this project by becoming a sponsor. Your logo will show up in the Contribute page of all Wiki.js installations as well as here with a link to your website! [[Become a sponsor](https://opencollective.com/wikijs#sponsor)]
<div align="center">
<table>
<tbody>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/0/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/0/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/1/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/1/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/2/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/2/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/3/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/3/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/4/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/4/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/5/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/5/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/6/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/6/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/7/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/7/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/8/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/8/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/9/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/9/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/10/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/10/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/11/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/11/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/12/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/12/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/13/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/13/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/14/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/14/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/15/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/15/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/16/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/16/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/17/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/17/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/18/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/18/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/19/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/19/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/20/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/20/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/21/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/21/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/22/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/22/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/23/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/23/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/24/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/24/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/25/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/25/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/26/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/26/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/27/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/27/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/28/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/28/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/29/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/29/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/30/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/30/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/31/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/31/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/32/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/32/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/33/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/33/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/34/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/34/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/35/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/35/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/36/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/36/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/37/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/37/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/38/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/38/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/39/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/39/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/40/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/40/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/41/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/41/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/42/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/42/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/43/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/43/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/44/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/44/avatar.svg"></a>
</td>
</tr>
<tr>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/40/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/45/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/41/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/46/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/42/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/47/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/43/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/48/avatar.svg"></a>
</td>
<td align="center" valign="middle">
<a href="https://opencollective.com/wikijs/sponsor/44/website" target="_blank"><img src="https://opencollective.com/wikijs/sponsor/49/avatar.svg"></a>
</td>
</tr>
</tbody>
</table>
</div>
<h2 align="center">Patreon Backers</h2>
Thank you to all our patrons! 🙏 [[Become a patron](https://www.patreon.com/requarks)]
<div align="center">
<table><tbody><tr><td>
<img width="441" height="1" />
- Aeternum
- Al Romano
- Alex Balabanov
- Alex Milanov
- Alex Zen
- Arti Zirk
- Ave
- Brandon Curtis
- Damien Hottelier
- Daniel T. Holtzclaw
- Dave 'Sri' Seah
- djagoo
- dz
- Douglas Lassance
- Ergoflix
- Ernie Reid
- Etienne
- Flemis Jurgenheimer
- Florent
- Günter Pavlas
- hong
- Hope
- Ian
- Imari Childress
- Iskander Callos
</td><td>
<img width="441" height="1" />
- Josh Stewart
- Justin Dunsworth
- Keir
- Loïc CRAMPON
- Ludgeir Ibanez
- Lyn Matten
- Mads Rosendahl
- Mark Mansur
- Matt Gedigian
- Mike Ditton
- Nate Figz
- Patryk
- Paul O'Fallon
- Philipp Schürch
- Tracey Duffy
- Quaxim
- Richeir
- Sergio Navarro Fernández
- Shad Narcher
- ShadowVoyd
- SmartNET.works
- Stepan Sokolovskyi
- Zach Crawford
- Zach Maynard
- 张白驹
</td></tr></tbody></table>
</div>
<h2 align="center">OpenCollective Backers</h2>
Thank you to all our backers! 🙏 [[Become a backer](https://opencollective.com/wikijs#backer)]
<a href="https://opencollective.com/wikijs#backers" target="_blank"><img src="https://opencollective.com/wikijs/backers.svg?width=890"></a>
<h2 align="center">Contributors</h2>
This project exists thanks to all the people who contribute. [[Contribute]](https://github.com/Requarks/wiki/blob/master/.github/CONTRIBUTING.md).
<a href="https://github.com/Requarks/wiki/graphs/contributors"><img src="https://opencollective.com/wikijs/contributors.svg?width=890" /></a>
<h2 align="center">Special Thanks</h2>
![Browserstack](https://js.wiki/legacy/logo_browserstack.png)
[Browserstack](https://www.browserstack.com/) for providing access to their great cross-browser testing tools.
![Cloudflare](https://js.wiki/legacy/logo_cloudflare.png)
[Cloudflare](https://www.cloudflare.com/) for providing their great CDN, SSL and advanced networking services.
![DigitalOcean](https://js.wiki/legacy/logo_digitalocean.png)
[DigitalOcean](https://m.do.co/c/5f7445bfa4d0) for providing hosting of the Wiki.js documentation site and APIs.
![Icons8](https://static.requarks.io/logo/icons8-text-h40.png)
[Icons8](https://icons8.com/) for providing access to their beautiful icon sets.
![Lokalise](https://static.requarks.io/logo/lokalise-text-h40.png)
[Lokalise](https://lokalise.com/) for providing access to their great localization tool.
![MacStadium](https://static.requarks.io/logo/macstadium-h40.png)
[MacStadium](https://www.macstadium.com) for providing access to their Mac hardware in the cloud.
![Netlify](https://js.wiki/legacy/logo_netlify.png)
[Netlify](https://www.netlify.com) for providing hosting for our website.
![ngrok](https://static.requarks.io/logo/ngrok-h40.png)
[ngrok](https://ngrok.com) for providing access to their great HTTP tunneling services.
![Porkbun](https://static.requarks.io/logo/porkbun.png)
[Porkbun](https://www.porkbun.com) for providing domain registration services.
# Shadows Over Westgate Wiki.js Deployment Guide
This repository is a Wiki.js 2.x application checkout with the custom `westgate`
theme built into `client/themes/westgate`.
The important deployment detail: Wiki.js custom themes are compiled into the
application bundle. You cannot deploy the stock `ghcr.io/requarks/wiki:2` image
and expect this theme to appear. Build this repository into a custom Wiki.js
image, then run that image with PostgreSQL.
Official references:
- Wiki.js install overview: https://docs.requarks.io/s/en/install
- Wiki.js Docker install guide: https://docs.requarks.io/s/en/install/docker
- Wiki.js requirements: https://docs.requarks.io/s/en/install/requirements
## Recommended VPS Shape
For a small wiki, use a VPS with:
- Ubuntu 22.04 or 24.04 LTS.
- 2 CPU cores if possible. Wiki.js can run on 1 core, but 2 is better.
- At least 1 GB RAM; 2 GB is more comfortable.
- Enough disk for uploaded assets and database backups.
- A DNS record such as `wiki.example.com` pointed at the VPS.
Wiki.js requires a real domain or subdomain. It is not designed to live under a
subpath such as `example.com/wiki`.
## Install Docker On The VPS
Use Docker Compose so Wiki.js and PostgreSQL are managed together.
```bash
sudo apt update
sudo apt install -y ca-certificates curl git
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null
sudo apt update
sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
Optional, but convenient:
```bash
sudo usermod -aG docker "$USER"
newgrp docker
```
## Clone This Repository
```bash
sudo mkdir -p /opt/westgate
sudo chown "$USER":"$USER" /opt/westgate
cd /opt/westgate
git clone <YOUR_REPO_URL> wiki
cd wiki
```
Use the private Shadows Over Westgate repository URL in place of
`<YOUR_REPO_URL>`.
## Create Production Compose Files
Create a deployment directory outside the checkout:
```bash
sudo mkdir -p /opt/westgate/deploy
sudo chown "$USER":"$USER" /opt/westgate/deploy
cd /opt/westgate/deploy
```
Create `.env`:
```bash
cat > .env <<'EOF'
POSTGRES_DB=wiki
POSTGRES_USER=wikijs
POSTGRES_PASSWORD=change-this-long-random-password
WIKI_IMAGE=westgate-wikijs:latest
WIKI_HTTP_PORT=3000
EOF
```
Create `docker-compose.yml`:
```yaml
services:
db:
image: postgres:15-alpine
restart: unless-stopped
environment:
POSTGRES_DB: ${POSTGRES_DB}
POSTGRES_USER: ${POSTGRES_USER}
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
volumes:
- db-data:/var/lib/postgresql/data
wiki:
image: ${WIKI_IMAGE}
restart: unless-stopped
depends_on:
- db
environment:
DB_TYPE: postgres
DB_HOST: db
DB_PORT: 5432
DB_USER: ${POSTGRES_USER}
DB_PASS: ${POSTGRES_PASSWORD}
DB_NAME: ${POSTGRES_DB}
ports:
- "${WIKI_HTTP_PORT}:3000"
volumes:
- wiki-content:/wiki/data/content
volumes:
db-data:
wiki-content:
```
This keeps PostgreSQL data and Wiki.js local content in Docker volumes.
## Build The Custom Westgate Image
From the repository checkout:
```bash
cd /opt/westgate/wiki
docker build -f dev/build/Dockerfile -t westgate-wikijs:latest .
```
That Dockerfile runs the application build, including:
- `client/themes/westgate/scss/app.scss`
- `client/themes/westgate/js/app.js`
- `client/themes/westgate/components/`
- shared Wiki.js client components and generated server views
This is the step that makes the custom theme exist in production.
## Start Wiki.js
```bash
cd /opt/westgate/deploy
docker compose up -d
docker compose logs -f wiki
```
Open:
```text
http://YOUR_SERVER_IP:3000
```
Complete the first-run Wiki.js setup in the browser.
## Put It Behind HTTPS
For a public VPS, put a reverse proxy in front of Wiki.js. Caddy is the simplest:
```bash
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/gpg.key | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
curl -1sLf https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt | sudo tee /etc/apt/sources.list.d/caddy-stable.list
sudo apt update
sudo apt install -y caddy
```
Create `/etc/caddy/Caddyfile`:
```caddyfile
wiki.example.com {
reverse_proxy 127.0.0.1:3000
}
```
Reload Caddy:
```bash
sudo systemctl reload caddy
```
Caddy will request and renew TLS certificates automatically. Replace
`wiki.example.com` with the real wiki hostname.
If using Caddy, keep the compose port bound to localhost only:
```yaml
ports:
- "127.0.0.1:3000:3000"
```
Then restart:
```bash
cd /opt/westgate/deploy
docker compose up -d
```
## Select The Westgate Theme
After the first run, sign into Wiki.js as an administrator.
In the admin interface, choose the `Westgate` theme if it appears in the theme
settings. If the admin UI does not expose it cleanly, set it directly in the
database.
Enter PostgreSQL:
```bash
cd /opt/westgate/deploy
docker compose exec db psql -U wikijs -d wiki
```
Inspect the theming setting:
```sql
SELECT key, value FROM settings WHERE key = 'theming';
```
The `value` column is JSON. Make sure its `theme` property is `westgate`, then
restart Wiki.js:
```bash
docker compose restart wiki
```
The exact JSON shape can vary with Wiki.js settings, so inspect before updating.
Do not blindly overwrite unrelated theming settings.
## Updating The Site
When this repository changes:
```bash
cd /opt/westgate/wiki
git pull
docker build -f dev/build/Dockerfile -t westgate-wikijs:latest .
cd /opt/westgate/deploy
docker compose up -d
```
If the database schema changes during a Wiki.js upgrade, watch the logs:
```bash
docker compose logs -f wiki
```
## Backups
Back up PostgreSQL:
```bash
cd /opt/westgate/deploy
docker compose exec db pg_dump -U wikijs wiki > wiki-$(date +%F).sql
```
Back up Docker volumes if you use local file storage:
```bash
docker run --rm \
-v deploy_wiki-content:/data:ro \
-v "$PWD":/backup \
alpine tar czf /backup/wiki-content-$(date +%F).tar.gz -C /data .
```
Keep backups off the VPS as well as on it.
## Development Notes
Inside the development container, use:
```bash
yarn dev
```
`yarn dev` live-reloads theme and client changes. During visual iteration, do
not repeatedly run `yarn build`. Use `yarn build` for production-style validation
or when building the deploy image.
## Troubleshooting
If the site loads but does not look like Westgate:
- Confirm the deployed image was built from this repository.
- Confirm the active theme setting is `westgate`.
- Confirm the browser is not serving stale assets.
- Rebuild the image after theme changes.
If Wiki.js cannot connect to the database:
- Confirm `DB_HOST` matches the compose service name, usually `db`.
- Confirm `POSTGRES_USER`, `POSTGRES_PASSWORD`, and `POSTGRES_DB` match the
Wiki.js environment variables.
- Check `docker compose logs db` and `docker compose logs wiki`.
If HTTPS does not work:
- Confirm DNS points to the VPS.
- Confirm ports 80 and 443 are open in the VPS firewall/security group.
- Check `sudo journalctl -u caddy -f`.

@ -1,6 +1,6 @@
<template lang="pug">
v-footer.justify-center(:color='bgColor', inset)
.caption.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-1`')
v-footer.westgate-footer.justify-center(:color='bgColor', inset)
.caption
template(v-if='footerOverride')
span(v-html='footerOverrideRender + ` |&nbsp;`')
template(v-else-if='company && company.length > 0 && contentLicense !== ``')
@ -64,11 +64,11 @@ export default {
background: mc('theme', 'primary');
span {
color: mc('blue', '300');
color: #b9b2a6;
}
a {
color: mc('blue', '200');
color: #d8c28a;
}
}
}

@ -1,9 +1,9 @@
<template lang="pug">
div
.pa-3.d-flex(v-if='navMode === `MIXED`', :class='$vuetify.theme.dark ? `grey darken-5` : `blue darken-3`')
.westgate-sidebar-modebar.pa-3.d-flex(v-if='navMode === `MIXED`')
v-btn(
class='westgate-sidebar-modebtn'
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='min-width:0;'
@click='goHome'
:aria-label='$t(`common:header.home`)'
@ -11,8 +11,8 @@
v-icon(size='20') mdi-home
v-btn.ml-3(
v-if='currentMode === `custom`'
class='westgate-sidebar-modebtn'
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='flex: 1 1 100%;'
@click='switchMode(`browse`)'
)
@ -20,8 +20,8 @@
.body-2.text-none {{$t('common:sidebar.browse')}}
v-btn.ml-3(
v-else-if='currentMode === `browse`'
class='westgate-sidebar-modebtn'
depressed
:color='$vuetify.theme.dark ? `grey darken-4` : `blue darken-2`'
style='flex: 1 1 100%;'
@click='switchMode(`custom`)'
)

@ -1,5 +1,5 @@
<template lang="pug">
v-app(v-scroll='upBtnScroll', :dark='$vuetify.theme.dark', :class='$vuetify.rtl ? `is-rtl` : `is-ltr`')
v-app.westgate-theme(v-scroll='upBtnScroll', :dark='$vuetify.theme.dark', :class='$vuetify.rtl ? `is-rtl` : `is-ltr`')
nav-header(v-if='!printView')
v-navigation-drawer(
v-if='navMode !== `NONE` && !printView'
@ -11,9 +11,10 @@
:temporary='$vuetify.breakpoint.smAndDown'
v-model='navShown'
:right='$vuetify.rtl'
class='westgate-nav-drawer'
)
vue-scroll(:ops='scrollStyle')
nav-sidebar(:color='$vuetify.theme.dark ? `grey darken-4-d4` : `primary`', :items='sidebarDecoded', :nav-mode='navMode')
nav-sidebar(color='westgate-sidebar-list', :items='sidebarDecoded', :nav-mode='navMode')
v-fab-transition(v-if='navMode !== `NONE`')
v-btn(
@ -32,7 +33,7 @@
v-main(ref='content')
template(v-if='path !== `home`')
v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-d3` : `grey lighten-3`', flat, dense, v-if='$vuetify.breakpoint.smAndUp')
v-toolbar.westgate-breadcrumb-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-d3` : `grey lighten-3`', flat, dense, v-if='$vuetify.breakpoint.smAndUp')
//- v-btn.pl-0(v-if='$vuetify.breakpoint.xsOnly', flat, @click='toggleNavigation')
//- v-icon(color='grey darken-2', left) menu
//- span Navigation
@ -48,8 +49,8 @@
.caption.red--text {{$t('common:page.unpublished')}}
status-indicator.ml-3(negative, pulse)
v-divider
v-container.grey.pa-0(fluid, :class='$vuetify.theme.dark ? `darken-4-l3` : `lighten-4`')
v-row.page-header-section(no-gutters, align-content='center', style='height: 90px;')
v-container.westgate-page-title-band.pa-0(fluid)
v-row.page-header-section(no-gutters, align-content='center')
v-col.page-col-content.is-page-header(
:offset-xl='tocPosition === `left` ? 2 : 0'
:offset-lg='tocPosition === `left` ? 3 : 0'
@ -59,8 +60,8 @@
:class='$vuetify.rtl ? `pr-4` : `pl-4`'
)
.page-header-headings
.headline.grey--text(:class='$vuetify.theme.dark ? `text--lighten-2` : `text--darken-3`') {{title}}
.caption.grey--text.text--darken-1 {{description}}
.headline.westgate-page-title {{title}}
.caption.westgate-page-description {{description}}
.page-edit-shortcuts(
v-if='editShortcutsObj.editMenuBar'
:class='tocPosition === `right` ? `is-right` : ``'
@ -83,7 +84,7 @@
v-icon.mr-2(small) {{ editShortcutsObj.editMenuExternalIcon }}
span.text-none {{$t(`common:page.editExternal`, { name: editShortcutsObj.editMenuExternalName })}}
v-divider
v-container.pl-5.pt-4(fluid, grid-list-xl)
v-container.westgate-page-body(fluid, grid-list-xl)
v-layout(row)
v-flex.page-col-sd(
v-if='tocPosition !== `off` && $vuetify.breakpoint.lgAndUp'
@ -93,62 +94,49 @@
xl2
)
v-card.page-toc-card.mb-5(v-if='tocDecoded.length')
.overline.pa-5.pb-0(:class='$vuetify.theme.dark ? `blue--text text--lighten-2` : `primary--text`') {{$t('common:page.toc')}}
.overline.westgate-card-title.pa-5.pb-0 {{$t('common:page.toc')}}
v-list.pb-3(dense, nav, :class='$vuetify.theme.dark ? `darken-3-d3` : ``')
template(v-for='(tocItem, tocIdx) in tocDecoded')
v-list-item(@click='$vuetify.goTo(tocItem.anchor, scrollOpts)')
v-list-item(v-if='tocItem.title', @click='$vuetify.goTo(tocItem.anchor, scrollOpts)')
v-icon(color='grey', small) {{ $vuetify.rtl ? `mdi-chevron-left` : `mdi-chevron-right` }}
v-list-item-title.px-3 {{tocItem.title}}
//- v-divider(v-if='tocIdx < toc.length - 1 || tocItem.children.length')
template(v-for='tocSubItem in tocItem.children')
v-list-item(@click='$vuetify.goTo(tocSubItem.anchor, scrollOpts)')
v-list-item(v-if='tocSubItem.title', @click='$vuetify.goTo(tocSubItem.anchor, scrollOpts)')
v-icon.px-3(color='grey lighten-1', small) {{ $vuetify.rtl ? `mdi-chevron-left` : `mdi-chevron-right` }}
v-list-item-title.px-3.caption.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-1`') {{tocSubItem.title}}
v-list-item-title.px-3.caption.westgate-muted-text {{tocSubItem.title}}
//- v-divider(inset, v-if='tocIdx < toc.length - 1')
v-card.page-tags-card.mb-5(v-if='tags.length > 0')
.pa-5
.overline.teal--text.pb-2(:class='$vuetify.theme.dark ? `text--lighten-3` : ``') {{$t('common:page.tags')}}
.overline.westgate-card-title.pb-2 {{$t('common:page.tags')}}
v-chip.mr-1.mb-1(
label
:color='$vuetify.theme.dark ? `teal darken-1` : `teal lighten-5`'
v-for='(tag, idx) in tags'
:href='`/t/` + tag.tag'
:key='`tag-` + tag.tag'
)
v-icon(:color='$vuetify.theme.dark ? `teal lighten-3` : `teal`', left, small) mdi-tag
span(:class='$vuetify.theme.dark ? `teal--text text--lighten-5` : `teal--text text--darken-2`') {{tag.title}}
v-icon(left, small) mdi-tag
span {{tag.title}}
v-chip.mr-1.mb-1(
label
:color='$vuetify.theme.dark ? `teal darken-1` : `teal lighten-5`'
:href='`/t/` + tags.map(t => t.tag).join(`/`)'
:aria-label='$t(`common:page.tagsMatching`)'
)
v-icon(:color='$vuetify.theme.dark ? `teal lighten-3` : `teal`', size='20') mdi-tag-multiple
v-icon(size='20') mdi-tag-multiple
v-card.page-comments-card.mb-5(v-if='commentsEnabled && commentsPerms.read')
.pa-5
.overline.pb-2.blue-grey--text.d-flex.align-center(:class='$vuetify.theme.dark ? `text--lighten-3` : `text--darken-2`')
.overline.westgate-card-title.pb-2.d-flex.align-center
span {{$t('common:comments.sdTitle')}}
//- v-spacer
//- v-chip.text-center(
//- v-if='!commentsExternal'
//- label
//- x-small
//- :color='$vuetify.theme.dark ? `blue-grey darken-3` : `blue-grey darken-2`'
//- dark
//- style='min-width: 50px; justify-content: center;'
//- )
//- span {{commentsCount}}
.d-flex
v-btn.text-none(
@click='goToComments()'
:color='$vuetify.theme.dark ? `blue-grey` : `blue-grey darken-2`'
outlined
style='flex: 1 1 100%;'
small
)
span.blue-grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') {{$t('common:comments.viewDiscussion')}}
span {{$t('common:comments.viewDiscussion')}}
v-tooltip(right, v-if='commentsPerms.write')
template(v-slot:activator='{ on }')
v-btn.ml-2(
@ -156,15 +144,14 @@
v-on='on'
outlined
small
:color='$vuetify.theme.dark ? `blue-grey` : `blue-grey darken-2`'
:aria-label='$t(`common:comments.newComment`)'
)
v-icon(:color='$vuetify.theme.dark ? `blue-grey lighten-1` : `blue-grey darken-2`', dense) mdi-comment-plus
v-icon(dense) mdi-comment-plus
span {{$t('common:comments.newComment')}}
v-card.page-author-card.mb-5
.pa-5
.overline.indigo--text.d-flex(:class='$vuetify.theme.dark ? `text--lighten-3` : ``')
.overline.westgate-card-title.d-flex
span {{$t('common:page.lastEditedBy')}}
v-spacer
v-tooltip(right, v-if='isAuthenticated')
@ -177,10 +164,10 @@
v-if='hasReadHistoryPermission'
:aria-label='$t(`common:header.history`)'
)
v-icon(color='indigo', dense) mdi-history
v-icon(dense) mdi-history
span {{$t('common:header.history')}}
.page-author-card-name.body-2.grey--text(:class='$vuetify.theme.dark ? `` : `text--darken-3`') {{ authorName }}
.page-author-card-date.caption.grey--text.text--darken-1 {{ updatedAt | moment('calendar') }}
.page-author-card-name.body-2 {{ authorName }}
.page-author-card-date.caption.westgate-muted-text {{ updatedAt | moment('calendar') }}
//- v-card.mb-5
//- .pa-5
@ -196,7 +183,7 @@
//- .caption.grey--text 5 votes
v-card.page-shortcuts-card(flat)
v-toolbar(:color='$vuetify.theme.dark ? `grey darken-4-d3` : `grey lighten-3`', flat, dense)
v-toolbar.westgate-shortcuts-toolbar(flat, dense)
v-spacer
//- v-tooltip(bottom)
//- template(v-slot:activator='{ on }')
@ -517,9 +504,9 @@ export default {
},
bar: {
onlyShowBarOnScroll: false,
background: '#42A5F5',
background: '#c2a35a',
hoverStyle: {
background: '#64B5F6'
background: '#e0c878'
}
}
},
@ -607,7 +594,7 @@ export default {
},
mounted () {
if (this.$vuetify.theme.dark) {
this.scrollStyle.bar.background = '#424242'
this.scrollStyle.bar.background = '#a8893f'
}
// -> Check side navigation visibility
@ -734,7 +721,7 @@ export default {
}
.page-col-sd {
margin-top: -90px;
margin-top: 0;
align-self: flex-start;
position: sticky;
top: 64px;
@ -781,7 +768,7 @@ export default {
}
.v-icon {
color: mc('blue', '700');
color: #c2a35a;
}
&:first-child {

@ -104,101 +104,79 @@ export default {
</script>
<style lang="scss">
.tabset {
border-radius: 5px;
margin-top: 10px;
$wg-bg: #0f0d12;
$wg-panel: #141219;
$wg-panel-2: #110f15;
$wg-panel-3: #1a161d;
$wg-border: #2a252d;
$wg-gold: #c2a35a;
$wg-link-hover: #e0c878;
$wg-text: #e6e0d6;
$wg-text-soft: #b9b2a6;
$wg-text-muted: #9a9086;
@at-root .theme--dark & {
background-color: #292929;
}
.tabset {
background: linear-gradient(100deg, rgba(42, 18, 34, 0.38), rgba(18, 16, 23, 0.96) 58%, rgba(14, 13, 18, 0.98));
border: 1px solid rgba(194, 163, 90, 0.14);
border-radius: 8px;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.025),
inset 0 0 34px rgba(96, 32, 68, 0.1),
0 10px 26px rgba(0, 0, 0, 0.22);
margin-top: 16px;
overflow: hidden;
> .tabset-tabs {
padding-left: 0;
margin: 0;
display: flex;
align-items: stretch;
background: linear-gradient(to bottom, #FFF, #FAFAFA);
box-shadow: inset 0 -1px 0 0 #DDD;
border-radius: 5px 5px 0 0;
background: linear-gradient(to bottom, rgba(20, 18, 25, 0.96), rgba(16, 14, 20, 0.98));
border-bottom: 1px solid rgba(194, 163, 90, 0.16);
overflow: auto;
@at-root .theme--dark & {
background: linear-gradient(to bottom, #424242, #333);
box-shadow: inset 0 -1px 0 0 #555;
}
> li {
display: block;
padding: 16px;
padding: 14px 16px;
margin-top: 0;
cursor: pointer;
transition: color 1s ease;
border-right: 1px solid #FFF;
transition: background 0.2s ease, color 0.2s ease, box-shadow 0.2s ease;
border-right: 1px solid rgba(194, 163, 90, 0.1);
color: $wg-text-muted;
font-size: 14px;
font-weight: 500;
margin-bottom: 1px;
user-select: none;
@at-root .theme--dark & {
border-right-color: #555;
}
&.is-active {
background-color: #FFF;
margin-bottom: 0;
padding-bottom: 17px;
padding-top: 13px;
color: mc('blue', '700');
border-top: 3px solid mc('blue', '700');
@at-root .theme--dark & {
background-color: #292929;
color: mc('blue', '300');
}
background: linear-gradient(to bottom, rgba(194, 163, 90, 0.13), rgba(35, 27, 23, 0.48));
box-shadow: inset 0 3px 0 $wg-gold;
color: $wg-link-hover;
}
&:last-child {
border-right: none;
&.is-active {
border-right: 1px solid #EEE;
@at-root .theme--dark & {
border-right-color: #555;
}
}
}
&:hover {
background-color: rgba(#CCC, .1);
@at-root .theme--dark & {
background-color: rgba(#222, .25);
}
background: rgba(194, 163, 90, 0.08);
color: $wg-text;
&.is-active {
background-color: #FFF;
@at-root .theme--dark & {
background-color: #292929;
}
background: linear-gradient(to bottom, rgba(194, 163, 90, 0.16), rgba(35, 27, 23, 0.54));
}
}
& + li {
border-left: 1px solid #EEE;
@at-root .theme--dark & {
border-left-color: #222;
}
border-left: 1px solid rgba(0, 0, 0, 0.32);
}
}
}
> .tabset-content {
.tabset-panel {
padding: 2px 16px 16px;
color: $wg-text-soft;
display: none;
padding: 18px 18px 20px;
&.is-active {
display: block;

@ -57,7 +57,11 @@ $wg-velvet-border: rgba(194, 163, 90, 0.13);
$wg-velvet-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.025),
inset 0 0 34px rgba(96, 32, 68, 0.1),
0 10px 26px rgba(0, 0, 0, 0.22);
0 8px 20px rgba(0, 0, 0, 0.2);
$wg-silk-sheen:
linear-gradient(105deg, rgba(194, 163, 90, 0.08) 0%, transparent 18%, transparent 76%, rgba(74, 28, 54, 0.16) 100%),
linear-gradient(180deg, rgba(255, 255, 255, 0.018), rgba(0, 0, 0, 0.22));
@mixin velvet-panel {
background: $wg-velvet-panel !important;
@ -84,7 +88,7 @@ $wg-velvet-shadow:
@mixin display-heading {
font-family: $wg-font-display !important;
font-weight: 600 !important;
letter-spacing: 0.036em !important;
letter-spacing: 0 !important;
text-shadow:
0 1px 0 rgba(0, 0, 0, 0.9),
0 0 10px rgba(104, 32, 76, 0.18);
@ -145,9 +149,9 @@ h5,
color: $wg-text !important;
}
h1 { font-size: clamp(1.9rem, 2.7vw, 2.45rem); }
h2 { font-size: clamp(1.45rem, 2.1vw, 1.85rem); }
h3 { font-size: clamp(1.18rem, 1.6vw, 1.4rem); }
h1 { font-size: 2.3rem; }
h2 { font-size: 1.72rem; }
h3 { font-size: 1.32rem; }
p,
li,
@ -199,18 +203,104 @@ a {
.v-toolbar,
header,
nav {
background: linear-gradient(to bottom, rgba(20, 18, 25, 0.96), rgba(16, 14, 20, 0.98)) !important;
background:
$wg-silk-sheen,
linear-gradient(to bottom, rgba(20, 18, 25, 0.96), rgba(16, 14, 20, 0.98)) !important;
border-bottom: 1px solid $wg-velvet-border !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.02),
0 8px 18px rgba(0, 0, 0, 0.18) !important;
}
.nav-header,
.nav-header .nav-header-inner,
.nav-header .v-toolbar__content,
.nav-header .v-toolbar__extension {
background:
linear-gradient(90deg, rgba(44, 18, 36, 0.34), rgba(17, 15, 22, 0.96) 34%, rgba(12, 11, 16, 0.98)) !important;
border-color: rgba(194, 163, 90, 0.16) !important;
}
.nav-header {
border-bottom: 1px solid rgba(194, 163, 90, 0.18) !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.02),
0 6px 14px rgba(0, 0, 0, 0.22) !important;
.org-logo {
filter: drop-shadow(0 0 8px rgba(194, 163, 90, 0.18));
}
.v-toolbar__content,
.v-toolbar__extension,
.nav-header-inner {
border: 0 !important;
box-shadow: none !important;
}
.nav-header-inner {
background: transparent !important;
}
.v-toolbar__title {
font-family: $wg-font-ui !important;
}
.v-btn {
border-radius: 0 !important;
color: $wg-text-soft !important;
&:hover,
&:focus {
background: rgba(194, 163, 90, 0.08) !important;
border-color: rgba(194, 163, 90, 0.18) !important;
}
}
.v-icon {
color: $wg-gold !important;
}
.v-divider {
border-color: rgba(194, 163, 90, 0.14) !important;
}
.navHeaderLoading .v-progress-circular {
color: $wg-gold !important;
}
.v-text-field.v-text-field--solo {
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
box-shadow: none !important;
}
.v-text-field.v-text-field--solo,
.v-text-field.v-text-field--solo .v-input__control,
.v-text-field.v-text-field--solo .v-input__slot,
.v-text-field.v-text-field--solo fieldset {
outline: 0 !important;
}
.v-text-field.v-text-field--solo .v-input__slot {
min-height: 42px !important;
background: rgba(10, 9, 13, 0.56) !important;
border: 1px solid rgba(194, 163, 90, 0.16) !important;
border-radius: 5px !important;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.02) !important;
}
.v-text-field.v-text-field--solo fieldset {
border: 0 !important;
}
}
.v-toolbar__title,
nav .title,
header .title {
color: $wg-text !important;
letter-spacing: 0.04em;
letter-spacing: 0;
}
.v-navigation-drawer,
@ -235,7 +325,7 @@ aside .menu,
aside .menu li,
.sidebar .menu li,
.sidebar a {
border-radius: 8px;
border-radius: 5px;
transition:
background 0.2s ease,
border-color 0.2s ease,
@ -245,9 +335,9 @@ aside .menu li,
.v-navigation-drawer .v-list-item,
.v-list-item {
margin-bottom: 0.35rem;
background: $wg-panel-3 !important;
border: 1px solid $wg-border !important;
margin-bottom: 0.25rem;
background: rgba(26, 22, 29, 0.52) !important;
border: 1px solid rgba(194, 163, 90, 0.08) !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.03),
inset 0 -1px 0 rgba(0, 0, 0, 0.4);
@ -271,11 +361,16 @@ aside .menu li,
color: $wg-gold !important;
}
.v-navigation-drawer .v-list-item--active,
.v-navigation-drawer .v-list-item--active:hover {
background: linear-gradient(90deg, rgba(194, 163, 90, 0.16), rgba(35, 28, 37, 0.84)) !important;
border-color: rgba(194, 163, 90, 0.28) !important;
}
/* =========================================================
PAGE SURFACES
========================================================= */
.page-col-content,
.page-content,
.page-editor,
.page-history,
@ -290,7 +385,7 @@ aside .menu li,
.page-block,
.page-section {
@include velvet-panel;
border-radius: 10px !important;
border-radius: 6px !important;
color: $wg-text-soft !important;
}
@ -301,11 +396,17 @@ aside .menu li,
@include velvet-panel-hover;
}
.page-col-content,
.westgate-theme {
.page-col-sd .v-card:hover,
.comments-container:hover {
@include velvet-panel;
}
}
.page-content,
.editor-markdown-container,
.editor-visual-container {
padding: clamp(1rem, 1.6vw, 1.35rem) !important;
padding: 1.2rem !important;
}
/* =========================================================
@ -316,9 +417,9 @@ aside .menu li,
.breadcrumbs,
.page-header,
.page-meta {
background: linear-gradient(90deg, rgba(18, 16, 23, 0.42), rgba(23, 20, 29, 0.24), transparent 82%) !important;
border: 1px solid rgba(194, 163, 90, 0.08) !important;
border-radius: 6px !important;
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
}
.page-header,
@ -359,6 +460,15 @@ button:hover,
color: $wg-text !important;
}
.v-btn:focus-visible,
button:focus-visible,
.btn:focus-visible,
a:focus-visible,
[role="button"]:focus-visible {
outline: 2px solid rgba(224, 200, 120, 0.72) !important;
outline-offset: 2px;
}
.v-btn.primary,
.v-btn.v-btn--has-bg.primary,
.v-btn.blue,
@ -405,6 +515,20 @@ button.primary,
}
}
.v-pagination .v-pagination__item,
.v-pagination .v-pagination__navigation {
background: $wg-panel-3 !important;
border: 1px solid rgba(194, 163, 90, 0.16) !important;
color: $wg-text-soft !important;
box-shadow: none !important;
}
.v-pagination .v-pagination__item--active {
background: linear-gradient(to bottom, #5a4a1a 0%, #473914 100%) !important;
border-color: $wg-gold !important;
color: #fff4dd !important;
}
/* =========================================================
TABLES / LISTS
========================================================= */
@ -415,6 +539,9 @@ table,
.theme--light.v-data-table {
background: transparent !important;
color: $wg-text-soft !important;
border: 1px solid rgba(194, 163, 90, 0.12) !important;
border-radius: 8px !important;
overflow: hidden;
}
thead th,
@ -464,7 +591,7 @@ select,
background: linear-gradient(to bottom, $wg-panel, $wg-panel-2) !important;
border: 1px solid $wg-border !important;
color: $wg-text !important;
border-radius: 8px !important;
border-radius: 5px !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.025),
0 4px 12px rgba(0, 0, 0, 0.15) !important;
@ -505,7 +632,7 @@ select:focus {
font-family: $wg-font-reading !important;
font-size: 1.02rem;
line-height: 1.68;
letter-spacing: 0.005em;
letter-spacing: 0;
}
.page-content p,
@ -525,7 +652,7 @@ select:focus {
.markdown-body h4 {
font-family: $wg-font-display !important;
color: #e8ded0 !important;
letter-spacing: 0.03em;
letter-spacing: 0;
}
blockquote,
@ -554,10 +681,39 @@ pre,
.cm-scroller {
background: #151219 !important;
border: 1px solid $wg-border !important;
border-radius: 8px !important;
border-radius: 5px !important;
color: $wg-text-soft !important;
}
.CodeMirror pre,
.CodeMirror pre.CodeMirror-line,
.CodeMirror pre.CodeMirror-line-like,
.editor-markdown .CodeMirror pre {
background: transparent !important;
border: 0 !important;
border-radius: 0 !important;
box-shadow: none !important;
color: inherit !important;
}
.page-content img,
.markdown-body img,
.contents img {
border: 1px solid rgba(194, 163, 90, 0.14);
border-radius: 6px;
box-shadow: 0 10px 24px rgba(0, 0, 0, 0.26);
}
.page-content kbd,
.markdown-body kbd,
kbd {
background: linear-gradient(to bottom, #211b23, #151219) !important;
border: 1px solid rgba(194, 163, 90, 0.22) !important;
border-radius: 4px;
color: $wg-link-hover !important;
box-shadow: inset 0 -1px 0 rgba(0, 0, 0, 0.55);
}
/* =========================================================
TOC / RIGHT RAIL / INDEX BLOCKS
========================================================= */
@ -569,7 +725,7 @@ pre,
.contents-sidebar,
.page-col-sd .v-card {
@include velvet-panel;
border-radius: 8px !important;
border-radius: 6px !important;
}
.toc a,
@ -597,7 +753,7 @@ pre,
.alert,
.message,
.notification {
border-radius: 8px !important;
border-radius: 6px !important;
box-shadow:
inset 0 1px 0 rgba(255, 255, 255, 0.025),
0 4px 12px rgba(0, 0, 0, 0.18) !important;
@ -631,7 +787,7 @@ pre,
.v-sheet.v-card,
.v-menu__content .v-list {
@include velvet-panel;
border-radius: 8px !important;
border-radius: 6px !important;
color: $wg-text-soft !important;
}
@ -640,6 +796,24 @@ pre,
background: rgba(255, 255, 255, 0.018) !important;
}
.v-menu__content {
.overline,
.v-subheader {
color: $wg-gold !important;
letter-spacing: 0 !important;
}
.v-list-item__title,
.v-list-item__subtitle {
color: $wg-text-soft !important;
}
.v-list-item__avatar .v-icon,
.v-list-item__action .v-icon {
color: $wg-gold !important;
}
}
/* =========================================================
DIVIDERS / HR
========================================================= */
@ -660,7 +834,7 @@ hr,
.admin-area .v-card,
.page-admin .v-card {
@include velvet-panel;
border-radius: 10px !important;
border-radius: 6px !important;
}
.login-screen .headline,
@ -670,6 +844,197 @@ hr,
color: $wg-text !important;
}
.login {
background:
linear-gradient(110deg, rgba(15, 13, 18, 0.98) 0%, rgba(20, 14, 21, 0.92) 42%, rgba(6, 5, 8, 0.98) 100%) !important;
&-sd {
background:
$wg-silk-sheen,
linear-gradient(to bottom, rgba(23, 20, 29, 0.94), rgba(14, 12, 18, 0.96)) !important;
border-left: 1px solid rgba(194, 163, 90, 0.22) !important;
border-right: 1px solid rgba(194, 163, 90, 0.18) !important;
box-shadow: 18px 0 42px rgba(0, 0, 0, 0.36) !important;
}
&-logo {
background: #111016 !important;
border: 1px solid rgba(194, 163, 90, 0.2);
border-top: 0;
}
&-title,
&-subtitle {
color: $wg-text !important;
text-shadow: 0 1px 0 rgba(0, 0, 0, 0.85) !important;
}
&-subtitle {
background: linear-gradient(to bottom, transparent, rgba(194, 163, 90, 0.08)) !important;
border-bottom: 1px solid rgba(194, 163, 90, 0.16) !important;
}
&-info,
&-form,
&-list {
border-color: rgba(194, 163, 90, 0.14) !important;
color: $wg-text-soft !important;
}
&-tfa {
background: $wg-panel !important;
border-color: rgba(194, 163, 90, 0.22) !important;
color: $wg-text-soft !important;
}
}
.search-results {
background:
linear-gradient(to bottom, rgba(10, 9, 13, 0.96), rgba(14, 10, 16, 0.98)),
linear-gradient(105deg, rgba(56, 22, 44, 0.24), transparent 44%, rgba(194, 163, 90, 0.08)) !important;
&-container {
@include velvet-panel;
border-radius: 6px;
padding: 14px;
}
&-help,
&-loader,
&-none {
color: $wg-text-soft !important;
}
&-items,
&-suggestions {
background: transparent !important;
.v-list-item {
background: rgba(26, 22, 29, 0.58) !important;
&.highlighted,
&:hover {
background: linear-gradient(to right, rgba(194, 163, 90, 0.12), rgba(43, 35, 45, 0.92)) !important;
box-shadow: inset 3px 0 0 rgba(194, 163, 90, 0.5);
}
}
}
}
.editor,
.editor-markdown,
.editor-visual,
.editor-redirect,
.newpage-content,
.profile,
.tags,
.history,
.source {
background: $wg-bg !important;
color: $wg-text-soft !important;
}
.editor-markdown {
&-toolbar {
background:
linear-gradient(90deg, rgba(86, 72, 24, 0.88), rgba(74, 61, 19, 0.94)) !important;
border-bottom: 1px solid rgba(194, 163, 90, 0.28) !important;
box-shadow: none !important;
.v-btn {
border-radius: 0 !important;
}
}
.nav-header .v-text-field.v-text-field--solo {
background: transparent !important;
border: 0 !important;
}
&-main {
background: #0d0c11 !important;
}
&-sidebar {
background: #101015 !important;
border-right: 1px solid rgba(194, 163, 90, 0.12);
.v-btn {
border-radius: 5px !important;
color: $wg-text-soft !important;
.v-icon {
color: $wg-text-soft !important;
}
&:hover .v-icon {
color: $wg-gold !important;
}
}
}
&-editor,
&-preview {
background: #121015 !important;
}
&-preview {
border-left: 1px solid rgba(194, 163, 90, 0.12);
padding: 0 !important;
&-content {
background:
linear-gradient(100deg, rgba(32, 18, 29, 0.22), rgba(15, 13, 19, 0.9)) !important;
color: $wg-text-soft !important;
padding: 1.35rem 1.55rem !important;
}
}
&-sysbar {
background: #1b1920 !important;
border-top: 1px solid rgba(194, 163, 90, 0.12);
color: $wg-text-muted !important;
}
.CodeMirror {
background: #121015 !important;
color: $wg-text-soft !important;
font-family: "Roboto Mono", "SFMono-Regular", Consolas, monospace !important;
}
.CodeMirror-gutters {
background: #0d0c11 !important;
border-right: 1px solid rgba(194, 163, 90, 0.1) !important;
}
.CodeMirror-linenumber {
color: rgba(185, 178, 166, 0.28) !important;
}
.CodeMirror-activeline-background {
background: rgba(194, 163, 90, 0.055) !important;
}
.CodeMirror-selected {
background: rgba(194, 163, 90, 0.18) !important;
}
.CodeMirror-cursor {
border-left-color: $wg-link-hover !important;
}
.cm-header {
color: $wg-text !important;
font-family: $wg-font-display !important;
}
.cm-quote,
.cm-link,
.cm-url {
color: $wg-link !important;
}
}
/* =========================================================
OPTIONAL WESTGATE NOTE BLOCK
========================================================= */
@ -691,7 +1056,6 @@ hr,
========================================================= */
@media (max-width: 959px) {
.page-col-content,
.page-content,
.editor-markdown-container,
.editor-visual-container {
@ -716,3 +1080,329 @@ hr,
padding: 0.65rem 0.75rem !important;
}
}
/* =========================================================
WESTGATE CONTENT PAGE FIRST PASS
========================================================= */
.westgate-theme {
.v-main {
background:
linear-gradient(120deg, rgba(84, 28, 62, 0.08), transparent 34%),
$wg-bg !important;
}
.westgate-page-title-band {
background: rgba(16, 14, 20, 0.42) !important;
border-bottom: 0;
box-shadow: none !important;
}
.page-header-section {
min-height: 64px !important;
height: auto !important;
}
.westgate-page-body {
background: transparent !important;
padding: 0.75rem 1rem 2rem !important;
> .layout {
align-items: flex-start;
}
}
.is-page-header.page-col-content,
.page-col-content {
background: transparent !important;
border: 0 !important;
box-shadow: none !important;
border-radius: 0 !important;
color: $wg-text-soft !important;
padding-top: 0 !important;
padding-bottom: 0 !important;
}
.is-page-header.page-col-content {
display: flex;
align-items: center;
justify-content: center;
}
.westgate-page-title {
@include display-heading;
color: $wg-text !important;
font-size: 1.72rem !important;
line-height: 1.16;
}
.westgate-page-description,
.westgate-muted-text {
color: $wg-text-muted !important;
}
.westgate-card-title {
color: $wg-gold !important;
font-family: $wg-font-ui !important;
letter-spacing: 0 !important;
}
.westgate-nav-drawer {
background:
linear-gradient(to bottom, #151219, #100e14 72%, #0d0b10) !important;
border-right: 1px solid rgba(194, 163, 90, 0.09) !important;
box-shadow: none !important;
}
.westgate-sidebar-list {
background: transparent !important;
.v-list-item {
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
margin: 0.15rem 0.45rem;
&:hover,
&:focus,
&.v-list-item--active {
background: rgba(194, 163, 90, 0.08) !important;
border-color: rgba(194, 163, 90, 0.14) !important;
box-shadow: inset 2px 0 0 rgba(194, 163, 90, 0.32) !important;
}
}
}
.westgate-sidebar-modebar {
background: transparent !important;
border-bottom: 1px solid rgba(194, 163, 90, 0.14);
padding: 0.75rem !important;
}
.westgate-sidebar-modebtn {
background: rgba(31, 25, 34, 0.76) !important;
border: 1px solid rgba(194, 163, 90, 0.14) !important;
border-radius: 5px !important;
color: $wg-text-soft !important;
.v-icon {
color: $wg-gold !important;
}
}
.page-col-sd {
background: transparent !important;
border: 0 !important;
box-shadow: none !important;
padding-right: 1rem !important;
}
.page-toc-card,
.page-tags-card,
.page-comments-card,
.page-author-card,
.page-shortcuts-card {
background: transparent !important;
border: 0 !important;
border-top: 1px solid rgba(194, 163, 90, 0.12) !important;
border-radius: 0 !important;
box-shadow: none !important;
overflow: hidden;
.v-list-item {
background: rgba(13, 12, 17, 0.42) !important;
min-height: 40px !important;
}
}
.page-col-content > .contents {
@include velvet-panel;
border-radius: 0 !important;
border-left: 0 !important;
border-right: 0 !important;
margin-bottom: 1.35rem;
margin-top: 0;
min-height: 34vh;
padding: 1.55rem 1.55rem !important;
}
.page-toc-card {
border-top: 0 !important;
.v-list {
padding: 0.45rem !important;
}
.westgate-card-title {
padding: 1rem 1.2rem 0.45rem !important;
}
.v-list-item {
background: transparent !important;
border-color: transparent !important;
box-shadow: none !important;
margin-bottom: 0.1rem;
min-height: 34px !important;
&:hover,
&:focus {
background: rgba(194, 163, 90, 0.07) !important;
border-color: rgba(194, 163, 90, 0.12) !important;
box-shadow: inset 2px 0 0 rgba(194, 163, 90, 0.34) !important;
}
.v-icon {
margin-right: 0.45rem;
}
.v-list-item__title {
padding-left: 0 !important;
padding-right: 0 !important;
}
}
}
.page-comments-card,
.page-author-card,
.page-shortcuts-card {
background: transparent !important;
box-shadow: none !important;
}
.page-shortcuts-card {
background: transparent !important;
.v-toolbar__content {
justify-content: center;
}
}
.page-tags-card .v-chip {
background: $wg-chip !important;
color: $wg-link !important;
border-color: rgba(194, 163, 90, 0.24) !important;
.v-icon {
color: $wg-gold !important;
}
}
.page-comments-card .v-btn,
.page-shortcuts-card .v-btn,
.page-edit-shortcuts .v-btn {
background: rgba(31, 25, 34, 0.76) !important;
border: 1px solid rgba(194, 163, 90, 0.18) !important;
color: $wg-text-soft !important;
}
.page-edit-shortcuts {
.v-btn {
border-radius: 6px !important;
margin-left: 4px;
.v-icon {
color: $wg-gold !important;
}
}
}
.westgate-shortcuts-toolbar {
background: transparent !important;
border: 0 !important;
box-shadow: none !important;
}
.btn-animate-edit.primary,
.v-speed-dial .primary,
.v-btn.primary {
background: linear-gradient(to bottom, #5a4a1a 0%, #473914 100%) !important;
border: 1px solid #a8893f !important;
color: #f0e6d8 !important;
}
.comments-container {
@include velvet-panel;
border-radius: 6px;
margin-top: 2rem;
overflow: hidden;
}
.comments-header {
align-items: center;
background: transparent;
border-bottom: 1px solid rgba(194, 163, 90, 0.14);
color: $wg-gold;
display: flex;
font-family: $wg-font-ui;
font-weight: 600;
letter-spacing: 0;
padding: 0.8rem 1rem;
text-transform: uppercase;
.v-icon {
color: $wg-gold !important;
}
}
.comments-main {
padding: 1rem 1rem 1.15rem;
}
.westgate-footer {
background: linear-gradient(to bottom, rgba(20, 18, 25, 0.94), rgba(15, 13, 19, 0.98)) !important;
border-top: 1px solid rgba(194, 163, 90, 0.14);
color: $wg-text-muted !important;
a {
color: $wg-link !important;
}
}
}
.v-main .contents {
> div > *:first-child {
margin-top: 0;
}
h1,
h2,
h3,
h4,
h5,
h6 {
position: relative;
.toc-anchor {
color: rgba(194, 163, 90, 0.7) !important;
text-decoration: none !important;
opacity: 0.72;
position: absolute;
left: -0.78em;
width: 0.7em;
transition: opacity 0.16s ease, color 0.16s ease;
}
&:hover .toc-anchor,
.toc-anchor:focus {
opacity: 1;
color: $wg-link-hover !important;
}
}
h1,
h2 {
padding-bottom: 0.25rem;
&::after {
content: "";
display: block;
height: 1px;
margin-top: 0.35rem;
background: linear-gradient(to right, rgba(194, 163, 90, 0.42), rgba(194, 163, 90, 0.08), transparent 72%);
}
}
a.is-external-link::after {
color: $wg-text-muted !important;
}
}

@ -6,17 +6,16 @@ requirements:
minimum: '>= 2.0.0'
maximum: '< 3.0.0'
props:
sdPosition:
tocPosition:
type: String
default: 'left'
title: Table of Contents Position
hint: Should the content sidebar be shown on the left or right.
enum:
- 'hidden'
- 'left'
- 'right'
hint: Select whether the table of contents is shown on the left, right or not at all.
order: 1
icon: mdi-border-vertical
default: left
enum:
- left
- right
- off
showTOC:
type: Boolean
default: true
@ -27,12 +26,12 @@ props:
default: true
title: Display the Page Tags
order: 3
showTags:
showAuthor:
type: Boolean
default: true
title: Display the Page Author and Date
order: 4
showTags:
showRating:
type: Boolean
default: true
title: Display the Page Rating
@ -48,4 +47,3 @@ props:
title: Display the Edit Speed Dial
hint: Shown in the lower right corner of the page.
order: 7

Loading…
Cancel
Save