0
0
mirror of https://github.com/naturalcrit/homebrewery.git synced 2026-01-23 20:53:05 +00:00

Compare commits

..

285 Commits

Author SHA1 Message Date
Trevor Buckner
aa945c4177 Up to v3.14.1 2024-08-13 17:08:59 -04:00
Trevor Buckner
cbc6dcdc35 Merge pull request #3625 from G-Ambatte/fixVarNameAsParam-#3622
Tweak variable math split regex
2024-08-11 23:01:38 -04:00
Trevor Buckner
5894dc5a7a Merge branch 'master' into fixVarNameAsParam-#3622 2024-08-11 21:59:58 -04:00
Trevor Buckner
284bfe565b Merge pull request #3624 from G-Ambatte/fixVarNameSubstringClash-#3613
Fix var name substring clash #3613
2024-08-11 21:49:31 -04:00
G.Ambatte
075c8805e0 Merge branch 'fixVarNameAsParam-#3622' of https://github.com/G-Ambatte/homebrewery into fixVarNameAsParam-#3622 2024-08-10 08:24:23 +12:00
G.Ambatte
5c4187cd06 Add variable names as function parameter tests 2024-08-10 08:24:16 +12:00
G.Ambatte
1719cc68fa Merge branch 'master' into fixVarNameAsParam-#3622 2024-08-10 08:23:23 +12:00
G.Ambatte
3ba67fb3d0 Merge branch 'master' into fixVarNameSubstringClash-#3613 2024-08-10 07:56:04 +12:00
G.Ambatte
f58d52c4b6 Add comma to var math split regex 2024-08-10 07:54:50 +12:00
Trevor Buckner
e1ad6f8114 Merge pull request #3588 from G-Ambatte/fixInfoPanel-#3586
Add disableMeta prop to SharePage
2024-08-09 10:55:27 -04:00
G.Ambatte
22257a95e0 Merge branch 'master' into fixInfoPanel-#3586 2024-08-09 18:06:07 +12:00
G.Ambatte
713c978f08 Add disableMeta to /migrate 2024-08-09 18:05:36 +12:00
G.Ambatte
3eb0c7acfe Add variable name checks 2024-08-09 17:42:15 +12:00
G.Ambatte
c3e6c01ec1 Fix typo in regex 2024-08-09 15:33:28 +12:00
G.Ambatte
5bb5cdec05 Change replaceAll to use RegEx from string 2024-08-09 13:10:30 +12:00
Trevor Buckner
f8e68c1485 Merge pull request #3620 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-jest-28.8.0
Bump eslint-plugin-jest from 28.7.0 to 28.8.0
2024-08-08 18:14:24 -04:00
Trevor Buckner
60ccd08bce Merge branch 'master' into dependabot/npm_and_yarn/eslint-plugin-jest-28.8.0 2024-08-08 17:53:01 -04:00
Trevor Buckner
f241024167 Merge pull request #3585 from dbolack-ab/CSSFolding
Add Code folding on CSS style tab
2024-08-08 17:51:43 -04:00
Trevor Buckner
f1fd75574d Merge branch 'master' into CSSFolding 2024-08-08 17:51:05 -04:00
Trevor Buckner
31352e417f Simplify folding logic 2024-08-08 17:48:32 -04:00
Trevor Buckner
643af98ca3 Add comment 2024-08-08 17:44:16 -04:00
Trevor Buckner
3b61cd355f Fix edge case where string was emitting data: twice.
Also, a case where the input is right around 50 chars. It can be truncated twice, leading to a long `.....`
2024-08-08 17:43:57 -04:00
Trevor Buckner
8df19e3b8f Merge branch 'master' into dependabot/npm_and_yarn/eslint-plugin-jest-28.8.0 2024-08-08 11:14:13 -04:00
Trevor Buckner
91d6548822 Merge pull request #3621 from naturalcrit/dependabot/npm_and_yarn/marked-emoji-1.4.2
Bump marked-emoji from 1.4.1 to 1.4.2
2024-08-08 11:13:55 -04:00
dependabot[bot]
ef797b2a69 Bump marked-emoji from 1.4.1 to 1.4.2
Bumps [marked-emoji](https://github.com/UziTech/marked-emoji) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/UziTech/marked-emoji/releases)
- [Changelog](https://github.com/UziTech/marked-emoji/blob/main/release.config.cjs)
- [Commits](https://github.com/UziTech/marked-emoji/compare/v1.4.1...v1.4.2)

---
updated-dependencies:
- dependency-name: marked-emoji
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 03:05:41 +00:00
dependabot[bot]
33a62a0ac7 Bump eslint-plugin-jest from 28.7.0 to 28.8.0
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 28.7.0 to 28.8.0.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v28.7.0...v28.8.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-08 03:05:17 +00:00
Trevor Buckner
5a9025f555 Merge pull request #3618 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-jest-28.7.0
Bump eslint-plugin-jest from 28.6.0 to 28.7.0
2024-08-07 13:15:48 -04:00
Trevor Buckner
0a494633bb Merge branch 'master' into dependabot/npm_and_yarn/eslint-plugin-jest-28.7.0 2024-08-07 12:00:40 -04:00
Trevor Buckner
aeaea5b5e0 Merge pull request #3516 from dbolack-ab/Issue_2563
Update table of content list-item breakage rules for wide table of contents
2024-08-07 12:00:23 -04:00
Trevor Buckner
e03ec34104 Merge branch 'master' into Issue_2563 2024-08-07 11:59:32 -04:00
dependabot[bot]
c65ee59998 Bump eslint-plugin-jest from 28.6.0 to 28.7.0
Bumps [eslint-plugin-jest](https://github.com/jest-community/eslint-plugin-jest) from 28.6.0 to 28.7.0.
- [Release notes](https://github.com/jest-community/eslint-plugin-jest/releases)
- [Changelog](https://github.com/jest-community/eslint-plugin-jest/blob/main/CHANGELOG.md)
- [Commits](https://github.com/jest-community/eslint-plugin-jest/compare/v28.6.0...v28.7.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-jest
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-05 03:15:14 +00:00
Trevor Buckner
4d59a14f74 Merge pull request #3617 from naturalcrit/lint-metadataEditor.less
Lint metadataEditor.less
2024-08-03 17:29:47 -04:00
Trevor Buckner
3c8aaa7465 Update metadataEditor.less 2024-08-03 17:28:47 -04:00
Trevor Buckner
ac70403203 Merge pull request #3611 from naturalcrit/dependabot/npm_and_yarn/react-router-dom-6.26.0
Bump react-router-dom from 6.25.1 to 6.26.0
2024-08-03 02:51:13 -04:00
dependabot[bot]
a5e7ad882d Bump react-router-dom from 6.25.1 to 6.26.0
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.25.1 to 6.26.0.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.26.0/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-03 06:47:06 +00:00
Trevor Buckner
061925e89a Merge pull request #3616 from naturalcrit/Replace-stylistic-stylelint-with-@stylistic-stylelint
Migrate from stylelint-stylistic to @stylelint-stylistic
2024-08-03 02:46:04 -04:00
Trevor Buckner
5fc8b508d1 Merge branch 'master' into Replace-stylistic-stylelint-with-@stylistic-stylelint 2024-08-03 02:43:33 -04:00
Trevor Buckner
cb444bef9d stylelint-stylistic has a new owner; migrating to newer package 2024-08-03 02:42:22 -04:00
Trevor Buckner
481f2e7d39 Merge pull request #3587 from G-Ambatte/addSignedFunction-#3537
Add new var math functions
2024-08-03 02:12:23 -04:00
Trevor Buckner
7559652a32 Merge branch 'master' into addSignedFunction-#3537 2024-08-03 02:09:47 -04:00
Trevor Buckner
8e15466063 Merge pull request #3615 from G-Ambatte/fixSelectSelfInThemeSource-#3614
Prevent self-selection of User Theme
2024-08-03 02:07:33 -04:00
G.Ambatte
8ef319d2cd Merge branch 'master' into fixSelectSelfInThemeSource-#3614 2024-08-03 16:35:17 +12:00
G.Ambatte
1513a983f7 Skip self when generating metadata dropdown list 2024-08-03 16:32:49 +12:00
G.Ambatte
1b71bbaefb Remove escaping from new functions 2024-08-03 10:32:39 +12:00
G.Ambatte
fcd5279381 Enable built-in abs() 2024-08-03 10:32:18 +12:00
G.Ambatte
4276d38152 Merge branch 'master' into addSignedFunction-#3537 2024-08-03 09:47:00 +12:00
David Bolack
d6bf2dec7e Merge branch 'master' into CSSFolding 2024-08-02 15:27:04 -05:00
David Bolack
423a4a521a Correct truncation when looking for data: in css folding 2024-08-02 15:25:41 -05:00
David Bolack
05d4d5b1ff Revert "Correct truncation when looking for data: in css folding"
This reverts commit ad1e8d50d7.
2024-08-02 15:25:10 -05:00
David Bolack
ad1e8d50d7 Correct truncation when looking for data: in css folding 2024-08-02 15:24:14 -05:00
David Bolack
c926f0de79 Resolve import matching suggestion. 2024-08-02 15:10:39 -05:00
Víctor Losada Hernández
4337c67f69 Merge branch 'master' into Issue_2563 2024-08-01 23:37:43 +02:00
Trevor Buckner
bb06a3e4d4 Merge pull request #3526 from 5e-Cleric/fix-background-monster-in-safari
Fixing Monster Frame background in Safari for iphone and ipad
2024-08-01 17:23:37 -04:00
Trevor Buckner
6af9c9e432 Merge branch 'master' into Issue_2563 2024-08-01 17:16:18 -04:00
Trevor Buckner
84d0d15c5a Merge branch 'master' into fix-background-monster-in-safari 2024-08-01 17:07:18 -04:00
Trevor Buckner
b185fe8e35 Move Monster block changes under main monster block stuff 2024-08-01 17:06:18 -04:00
Trevor Buckner
adbb9e54c1 Merge branch 'master' into addSignedFunction-#3537 2024-08-01 16:12:46 -04:00
Trevor Buckner
1d3c2d7cd6 Merge branch 'master' into fixInfoPanel-#3586 2024-08-01 15:57:43 -04:00
Trevor Buckner
2dc397e9f1 Merge pull request #3610 from naturalcrit/MoveMigrateToSharePage
Move Migrate page to use Share component
2024-08-01 15:56:37 -04:00
Trevor Buckner
6465564b6f Add route to Homebrew.jsx 2024-08-01 15:37:00 -04:00
G.Ambatte
e1fe640e92 Update shared/naturalcrit/markdown.js
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2024-08-02 07:03:27 +12:00
G.Ambatte
6f99fe7455 Update shared/naturalcrit/markdown.js
Co-authored-by: Trevor Buckner <calculuschild@gmail.com>
2024-08-02 07:02:57 +12:00
David Bolack
342963dae6 Merge branch 'master' into Issue_2563 2024-08-01 10:33:34 -05:00
David Bolack
957b1ed9e7 Merge branch 'master' into CSSFolding 2024-08-01 10:09:56 -05:00
Trevor Buckner
423ed28fbd Merge pull request #3517 from dbolack-ab/Issue_2688
Solve for .descriptive and .wide not getting along
2024-08-01 00:59:03 -04:00
Trevor Buckner
a4f17259e1 Merge branch 'master' into Issue_2688 2024-08-01 00:58:06 -04:00
Trevor Buckner
c2678e5f2c Merge pull request #3564 from G-Ambatte/fixInkFriendlySnippet-#3563
Stop Ink Friendly snippet making text unselectable in output PDF
2024-08-01 00:48:21 -04:00
Trevor Buckner
d797333b97 Merge branch 'master' into fixInkFriendlySnippet-#3563 2024-08-01 00:48:14 -04:00
Trevor Buckner
40777a3794 Merge pull request #3580 from G-Ambatte/fixToCUpperCasing-#3572
Fix Table of Contents uppercasing
2024-08-01 00:47:23 -04:00
Trevor Buckner
ac1005c2b0 Merge branch 'master' into fixInkFriendlySnippet-#3563 2024-08-01 00:47:02 -04:00
Trevor Buckner
c77395149b Tweak shadow values to match 5ePHB
box-shadows are a little smaller than drop-shadow. This just uses the same values we already found previously so the shadow size is not affected.
2024-08-01 00:46:08 -04:00
Trevor Buckner
fb0580fff1 Merge branch 'master' into fixToCUpperCasing-#3572 2024-08-01 00:41:47 -04:00
Trevor Buckner
0e953d08b2 Merge pull request #3609 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.25.3
Bump @babel/preset-env from 7.25.2 to 7.25.3
2024-07-31 23:54:21 -04:00
dependabot[bot]
7fce362a52 Bump @babel/preset-env from 7.25.2 to 7.25.3
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.25.2 to 7.25.3.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.25.3/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-08-01 03:47:39 +00:00
Trevor Buckner
3e7844ba6d Merge pull request #3591 from naturalcrit/dependabot/npm_and_yarn/eslint-plugin-react-7.35.0
Bump eslint-plugin-react from 7.34.3 to 7.35.0
2024-07-31 23:45:53 -04:00
Trevor Buckner
7619f8f420 Merge branch 'master' into dependabot/npm_and_yarn/eslint-plugin-react-7.35.0 2024-07-31 22:43:52 -04:00
Trevor Buckner
bac52f8376 Merge pull request #3582 from naturalcrit/dependabot/npm_and_yarn/react-router-dom-6.25.1
Bump react-router-dom from 6.24.1 to 6.25.1
2024-07-31 22:43:39 -04:00
Trevor Buckner
5c72cd9d47 Merge branch 'master' into dependabot/npm_and_yarn/react-router-dom-6.25.1 2024-07-31 21:54:53 -04:00
Trevor Buckner
89b59a52bc Merge pull request #3608 from G-Ambatte/fixTests
Fix tests post v3.14.0
2024-07-31 21:54:32 -04:00
Trevor Buckner
a6f2a1a4c8 Merge branch 'master' into fixTests 2024-07-31 21:51:46 -04:00
Trevor Buckner
e9e9fbe21c Merge pull request #3592 from dbolack-ab/UpdateDocker
Update Node version for Docker
2024-07-31 21:51:16 -04:00
G.Ambatte
e7108947d6 Fix expected values for pageCount and renderer 2024-08-01 13:24:23 +12:00
G.Ambatte
8b68f24135 Merge branch 'master' into fixInkFriendlySnippet-#3563 2024-08-01 12:43:20 +12:00
G.Ambatte
e32ae9a792 Merge branch 'master' into fixToCUpperCasing-#3572 2024-08-01 12:42:08 +12:00
G.Ambatte
758a951bf5 Merge branch 'master' into fixInfoPanel-#3586 2024-08-01 12:33:07 +12:00
G.Ambatte
21ac50cd27 Merge branch 'master' into addSignedFunction-#3537 2024-08-01 12:32:07 +12:00
dependabot[bot]
fc67a40167 Bump eslint-plugin-react from 7.34.3 to 7.35.0
Bumps [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) from 7.34.3 to 7.35.0.
- [Release notes](https://github.com/jsx-eslint/eslint-plugin-react/releases)
- [Changelog](https://github.com/jsx-eslint/eslint-plugin-react/blob/master/CHANGELOG.md)
- [Commits](https://github.com/jsx-eslint/eslint-plugin-react/compare/v7.34.3...v7.35.0)

---
updated-dependencies:
- dependency-name: eslint-plugin-react
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-31 23:35:07 +00:00
dependabot[bot]
7949df1865 Bump react-router-dom from 6.24.1 to 6.25.1
Bumps [react-router-dom](https://github.com/remix-run/react-router/tree/HEAD/packages/react-router-dom) from 6.24.1 to 6.25.1.
- [Release notes](https://github.com/remix-run/react-router/releases)
- [Changelog](https://github.com/remix-run/react-router/blob/main/packages/react-router-dom/CHANGELOG.md)
- [Commits](https://github.com/remix-run/react-router/commits/react-router-dom@6.25.1/packages/react-router-dom)

---
updated-dependencies:
- dependency-name: react-router-dom
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-31 23:35:06 +00:00
Trevor Buckner
8ed013cbb2 Merge pull request #3603 from naturalcrit/dependabot/npm_and_yarn/mongoose-8.5.2
Bump mongoose from 8.4.5 to 8.5.2
2024-07-31 19:34:29 -04:00
Trevor Buckner
0138c27863 Merge branch 'master' into dependabot/npm_and_yarn/mongoose-8.5.2 2024-07-31 19:34:00 -04:00
Trevor Buckner
7930c209ff Merge pull request #3599 from naturalcrit/dependabot/npm_and_yarn/babel/preset-env-7.25.2
Bump @babel/preset-env from 7.24.7 to 7.25.2
2024-07-31 19:33:51 -04:00
dependabot[bot]
26fdc1ba91 Bump mongoose from 8.4.5 to 8.5.2
Bumps [mongoose](https://github.com/Automattic/mongoose) from 8.4.5 to 8.5.2.
- [Release notes](https://github.com/Automattic/mongoose/releases)
- [Changelog](https://github.com/Automattic/mongoose/blob/master/CHANGELOG.md)
- [Commits](https://github.com/Automattic/mongoose/compare/8.4.5...8.5.2)

---
updated-dependencies:
- dependency-name: mongoose
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-31 23:33:27 +00:00
Trevor Buckner
8681994747 Merge branch 'master' into dependabot/npm_and_yarn/babel/preset-env-7.25.2 2024-07-31 19:32:52 -04:00
Trevor Buckner
0e684b14a7 Merge pull request #3598 from naturalcrit/dependabot/npm_and_yarn/babel/core-7.25.2
Bump @babel/core from 7.24.7 to 7.25.2
2024-07-31 19:32:44 -04:00
Trevor Buckner
974f84d49f Merge branch 'master' into dependabot/npm_and_yarn/babel/core-7.25.2 2024-07-31 19:32:28 -04:00
Trevor Buckner
5a6abef4fb Merge pull request #3562 from naturalcrit/dependabot/npm_and_yarn/dompurify-3.1.6
Bump dompurify from 3.1.5 to 3.1.6
2024-07-31 19:32:09 -04:00
Trevor Buckner
15200e0c6e Merge branch 'master' into dependabot/npm_and_yarn/dompurify-3.1.6 2024-07-31 19:31:25 -04:00
Trevor Buckner
79c22f383f Allow renderer and pagecount into stubs
At this point, only text and textbin contents are still in google drive. Which is the majority of the file still. Renderer and Pagecount may become useful for Vault page, and Renderer is needed for themes.
2024-07-31 18:13:31 -04:00
Trevor Buckner
915f9aafa8 restore snippetBundle field somehow lost from /new and /home 2024-07-31 17:27:58 -04:00
Trevor Buckner
11f8809c5e Merge pull request #3606 from naturalcrit/FixUserThemesCrashes
Fix user themes crashes
2024-07-31 17:05:52 -04:00
Trevor Buckner
27a4831ea0 Add a theme to our reference pages (changelog, FAQ, migrate) 2024-07-31 16:53:54 -04:00
Trevor Buckner
1abced20d6 Do not fetch user themes if there is no user. 2024-07-31 16:53:34 -04:00
G.Ambatte
251d03b7be Merge branch 'master' into addSignedFunction-#3537 2024-07-31 20:58:40 +12:00
David Bolack
29bbf3fef9 Merge branch 'master' into Issue_2563 2024-07-30 10:59:43 -05:00
dependabot[bot]
fac0d151b6 Bump @babel/preset-env from 7.24.7 to 7.25.2
Bumps [@babel/preset-env](https://github.com/babel/babel/tree/HEAD/packages/babel-preset-env) from 7.24.7 to 7.25.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.25.2/packages/babel-preset-env)

---
updated-dependencies:
- dependency-name: "@babel/preset-env"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 03:27:30 +00:00
dependabot[bot]
4f950b6024 Bump @babel/core from 7.24.7 to 7.25.2
Bumps [@babel/core](https://github.com/babel/babel/tree/HEAD/packages/babel-core) from 7.24.7 to 7.25.2.
- [Release notes](https://github.com/babel/babel/releases)
- [Changelog](https://github.com/babel/babel/blob/main/CHANGELOG.md)
- [Commits](https://github.com/babel/babel/commits/v7.25.2/packages/babel-core)

---
updated-dependencies:
- dependency-name: "@babel/core"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-30 03:27:04 +00:00
Trevor Buckner
1c1d331df9 Merge pull request #3597 from naturalcrit/v3.14.0
Up to v3.14.0
2024-07-29 21:55:19 -04:00
Trevor Buckner
8609925da8 Merge branch 'master' into v3.14.0 2024-07-29 21:54:38 -04:00
Trevor Buckner
607244d6e1 Merge pull request #3321 from dbolack-ab/brew_themes_user_selection
Enable User Brew theme selection
2024-07-29 21:54:28 -04:00
Trevor Buckner
9cc81d2ff9 Up to v3.14.0 2024-07-29 21:52:09 -04:00
Trevor Buckner
32fa50d608 Fallback to showing "Blank" theme if themes fail to load. 2024-07-29 12:30:13 -04:00
Trevor Buckner
8221579b6a Linting 2024-07-28 18:03:25 -04:00
Trevor Buckner
88eaebfd49 Raise test coverage threshold
This PR adds tests which means we are now covering a larger % of the codebase. Raise the coverage thresholds to match.
2024-07-28 18:00:33 -04:00
Trevor Buckner
ee9f2c8c83 Remove unused CSS endpoints in favor of #3075
Now that we have a dedicated /theme/ route for the recursive theming, the CSS endpoint can be simpler for only getting the `style` of a single brew. #3075 already has this simpler version, but no testing, so I have copied this into a comment there for implementation when it is ready.
2024-07-28 17:53:25 -04:00
Trevor Buckner
2870caaae6 Clean up metadataEditor theme dropdown 2024-07-28 17:18:30 -04:00
Trevor Buckner
e0425ec6c0 Simplify API call url 2024-07-28 16:47:16 -04:00
Trevor Buckner
8aa88a2e45 Add proper error popup when theme fails to load 2024-07-28 16:45:01 -04:00
Trevor Buckner
edec9369ec Finish adding test cases 2024-07-27 19:17:19 -04:00
Trevor Buckner
f2d933410e Add error handling for missing themes 2024-07-27 19:17:05 -04:00
Trevor Buckner
b64a0c5200 Start adding tests for /theme/ endpoint 2024-07-27 03:30:51 -04:00
Trevor Buckner
113f9b3fe3 No need to stringify Theme Bundle object 2024-07-27 02:00:38 -04:00
David Bolack
d2afa7adea Move fetchThemeBundle into /shared/helpers
This might not be the best rework - I was unsure if the *this* that would be available when called would see the appropriate object so I assumed not and pass it as a parameter.
Works, but may be bad form.
2024-07-23 22:17:52 -05:00
Trevor Buckner
8e7baca47d Fix tests 2024-07-23 17:40:32 -04:00
Trevor Buckner
ddc5693778 revert package-lock 2024-07-23 17:31:07 -04:00
Trevor Buckner
82f73fb21d cleanup 2024-07-23 17:24:50 -04:00
Trevor Buckner
27c52fc244 Fix loading CSS for Legacy 2024-07-23 17:11:48 -04:00
Trevor Buckner
ac82e3ecb2 Add to home page 2024-07-23 16:50:29 -04:00
Trevor Buckner
22b6aa14f0 Add to /new page 2024-07-23 16:43:23 -04:00
Trevor Buckner
24ab3d3392 Merge branch 'brew_themes_user_selection' of https://github.com/dbolack-ab/homebrewery into pr/3321 2024-07-23 16:26:35 -04:00
Trevor Buckner
0b01f27d11 Load theme bundles on /share page 2024-07-23 16:26:33 -04:00
David Bolack
d640ad6bb7 Update Node version for Docker 2024-07-22 19:13:38 -05:00
Víctor Losada Hernández
270aa9e0f9 Merge branch 'master' into brew_themes_user_selection 2024-07-22 22:46:12 +02:00
Trevor Buckner
6ae249a527 Lint 2024-07-22 02:46:26 -04:00
Trevor Buckner
c0123b96eb Support snippet compilation
Original handling of snippets only worked if the current selected theme was a staticTheme. This now fully merges all snippets through the theme chain no matter what the top-level theme is. So user themes built on 5ePHB can benefit from 5ePHB snippets too.

User input of user snippets will be a later PR, but merging them into static snippets is now supported.
2024-07-22 02:44:41 -04:00
Trevor Buckner
45f7080afd Move loadAllBrewStylesAndSnippets to the parent page component
Themes contain both CSS and Snippets. The brewRenderer only cares about the CSS, but other components need the Snippets. Better to have the parent "editPage", etc. load the theme bundles and pass them down to each child that needs it, rather than trying to pass from the child up.

This also fixes the `metadataEditor.jsx` not being able to change themes live; A new theme bundle is now loaded when a new theme is selected, instead of only the first time the BrewRenderer mounts.

Also renamed to "fetchThemeBundle"
2024-07-21 16:25:24 -04:00
David Bolack
2af2ad629d Try to account for situations where the URL folding is past the max length for the truncation so the user can see why this is a fold.
Remove missed debug messages.
2024-07-21 12:58:21 -05:00
David Bolack
2fc7aa454f Add data: URL folding for CSS.
Regex might need tweaking to catch all cases, but it catches the basics.
2024-07-21 12:40:49 -05:00
David Bolack
fde797c044 Rename prevLine to activeLine for better reading clarity. 2024-07-20 21:51:03 -05:00
David Bolack
6693fb1c13 reduce redundant trim()s 2024-07-20 21:40:22 -05:00
G.Ambatte
17f8de48a8 Add disableMeta prop to SharePage 2024-07-21 13:33:23 +12:00
Trevor Buckner
0a5ff213de use same theme endpoint for user and static themes
`getThemeBundle()` rework no longer needs two separate endpoints
2024-07-20 11:39:23 -04:00
G.Ambatte
aebfcc7885 Add new var math functions 2024-07-21 00:18:06 +12:00
David Bolack
2c4f3473e5 Trim the trailing { on a CSS fold. 2024-07-20 00:43:02 -05:00
G.Ambatte
9a4cc5f63e Add @import folding 2024-07-20 14:57:09 +12:00
David Bolack
c82b62f953 Add Code folding on CSS style tab 2024-07-19 16:03:44 -05:00
Trevor Buckner
f364f054f8 restore renderStyle
`renderStyle` is still necessary; it allows us to update the style live in the component render step as the user types into the style tab. Otherwise the style is only rendered once and never updates.

React also discourages directly editing the DOM ourselves, because it makes changes to the DOM that react cannot track; we should aim to provide all DOM writes inside of the component render function instead of using `document.createElement`, etc.

Too that end, this commit reduces the `loadAllStylesAndSnippets` function to just fetch and parse the data; actual rendering is moved back to `renderStyle()`
2024-07-19 01:33:56 -04:00
Trevor Buckner
460358ce1f Simplify some logic 2024-07-19 00:09:21 -04:00
Trevor Buckner
0448f15322 Classify user brews as V3 if they use V3
Each theme in the theme chain, including user brews, must use the same renderer. When moving to V4 or future versions, it will be important to distinguish which themes are compatible with each other
2024-07-19 00:05:45 -04:00
Trevor Buckner
d741878f78 Also remove userthemes from Brew object in sharePage 2024-07-19 00:00:06 -04:00
G.Ambatte
f830104531 Use textContent instead of innerText 2024-07-17 13:59:00 +12:00
Trevor Buckner
d22cd88446 fix crash in metadataeditor 2024-07-15 23:47:19 -04:00
Trevor Buckner
1444581c86 pass userThemes prop to Editor -> MetadataEditor 2024-07-15 23:44:07 -04:00
Trevor Buckner
dfbd85a8ce pass userThemes as a new prop, rather than inside of the brew 2024-07-15 23:29:16 -04:00
Trevor Buckner
af5434c9b7 cleanup 2024-07-15 16:45:55 -04:00
Trevor Buckner
484b0a6dff simplify getThemeBundle() by using just one loop
Also, removes need for special handling of the "first" theme.
2024-07-15 16:38:19 -04:00
Trevor Buckner
4951b9bf1a Add async error handler to /edit and /new
Since /edit and /new endpoints now have an `await` inside that could return an error (`getUsersBrewThemes()`), asyncHandler must be added to pass errors along instead of just crashing
2024-07-13 19:46:12 -04:00
Trevor Buckner
62c619de24 userThemes need not be nested inside a Brew object 2024-07-13 19:38:51 -04:00
Trevor Buckner
44c96aad04 spacing 2024-07-13 18:11:04 -04:00
Trevor Buckner
f392216ff4 Spacing 2024-07-13 18:08:29 -04:00
Trevor Buckner
591cae0e8f more renaming engine to renderer 2024-07-13 18:08:00 -04:00
Trevor Buckner
e222811d03 Rename engine to renderer to unify naming
This value is named `renderer` everywhere else. Relabeling to a consistent name.
2024-07-13 18:06:46 -04:00
Trevor Buckner
c9b885f868 include theme as baseTheme when getting user brew themes
`baseTheme` for a user brew theme is just the `theme` value of that brew.
2024-07-13 18:01:50 -04:00
Trevor Buckner
fed65f5430 Merge branch 'master' into dependabot/npm_and_yarn/dompurify-3.1.6 2024-07-13 17:44:37 -04:00
Trevor Buckner
47f912750b Extract getting userThemes from getBrew()
`getBrew()` should do one thing only; retrieve a brew. UI elements like the list of themes available to the user are not part of a brew.

Moved into the handers for the `/edit/` and `/new/` endpoints
2024-07-13 17:44:23 -04:00
Trevor Buckner
f29a5e346e Remove id parameter from getUsersBrewThemes
Filtering out the current brew can be done later as needed; certain situations may call for retrieving the whole list.
2024-07-13 17:35:19 -04:00
Trevor Buckner
ee381c91fe Simplify getUserBrewThemes function a bit 2024-07-13 17:26:38 -04:00
Trevor Buckner
5f8d46f1b6 Reuse splitTextStyleAndMetadata from helpers.js 2024-07-13 17:09:45 -04:00
David Bolack
ade819c70c A not so light rework.
This removes the existing endpoints and replaces them with /theme.

/theme/:id - return a theme bundle containing all styling from this USER theme and any parents.
/theme/:engine/:id - return a theme bundle containing all styling from this STATIC theme and any parents

The theme bundle returns a marshalled JSON object containing:
  styles - an array of strings representing the collected styles in loading order
  snippets - an array ( currently empty ) of collected snippets.

The various bits of theme rendering code for <style> an style <link> have been swapped out with an 'onDidMount' call that loads the thendpoint and appends a series of <style> blocks to the brewRender's head.

This loses some caching advantages, but probably won't matter in the long run.
2024-07-13 12:12:05 -05:00
Trevor Buckner
c209a86f90 Merge branch 'master' into fixInkFriendlySnippet-#3563 2024-07-11 09:47:54 -04:00
Trevor Buckner
2fa3c0f311 themeClass is never used 2024-07-11 00:26:50 -04:00
Trevor Buckner
5c0a072115 userThemes passed to SnippetBar.jsx is never used 2024-07-11 00:25:11 -04:00
Trevor Buckner
29c2274a19 Unify some variable naming 2024-07-10 18:54:45 -04:00
Trevor Buckner
a6f787ea8f Remove getBrewThemeParentCSS 2024-07-10 17:56:39 -04:00
Trevor Buckner
24c86dd199 Remove unused test 2024-07-10 17:49:57 -04:00
Trevor Buckner
7eb96ee6be Simplify brewRenderer output to only emit current theme
Instead of Blank, Parent, and Theme, just make use of the @include chaining, to handle all parent themes down to and including Blank
2024-07-10 17:46:51 -04:00
Trevor Buckner
27aebf0e3b Give 5ePHB and Journal themes a baseTheme of "Blank" 2024-07-10 17:15:45 -04:00
Trevor Buckner
88578a3d16 Fix failing test 2024-07-10 14:22:42 -04:00
Trevor Buckner
28446d3ae2 Comments for theme CSS endpoints 2024-07-10 14:21:23 -04:00
Trevor Buckner
a247e50c9f renaming "get" functions
rename `getStaticTheme` to `getStaticThemeCSS`
rename `getBrewThemeWithCSS` to `getBrewThemeCSS`
rename `getBrewThemeParent` to `getBrewThemeParentCSS`

to avoid confusion with other "get" endpoints like `getBrew`, and unify naming for endpoint functions that return CSS.

Simplify `isStaticTheme` function (getting the parent theme is handled elsewhere)
2024-07-10 14:15:03 -04:00
David Bolack
656edb07ea Rework detection of user brews to look up themeid in static themes list before assuming is a user brew.
Ended up being a fairly straightforward change. A few ternaries got smooshed or inverted. Passes builtin and local tests. Need to compare on the test instance.
2024-07-08 18:12:58 -05:00
G.Ambatte
0cf79ceeb1 Merge branch 'master' into fixInkFriendlySnippet-#3563 2024-07-09 08:00:27 +12:00
G.Ambatte
7b9bd70554 Change filter to box-shadow 2024-07-08 19:57:21 +00:00
David Bolack
a413dc8d4f Merge branch 'master' into Issue_2563 2024-07-08 10:14:06 -05:00
David Bolack
74ee09397e Merge branch 'master' into Issue_2688 2024-07-08 10:08:54 -05:00
dependabot[bot]
a06aa2a103 Bump dompurify from 3.1.5 to 3.1.6
Bumps [dompurify](https://github.com/cure53/DOMPurify) from 3.1.5 to 3.1.6.
- [Release notes](https://github.com/cure53/DOMPurify/releases)
- [Commits](https://github.com/cure53/DOMPurify/compare/3.1.5...3.1.6)

---
updated-dependencies:
- dependency-name: dompurify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-07-08 03:17:11 +00:00
David Bolack
ea6595d4d6 Merge branch 'master' into brew_themes_user_selection
Fixes a regression for legacy brews.
2024-07-07 12:03:15 -05:00
David Bolack
16ca52756d Merge branch 'master' into brew_themes_user_selection 2024-07-05 16:55:14 -05:00
David Bolack
645da7ae5f Merge branch 'brew_themes_user_selection' of github.com:dbolack-ab/homebrewery into brew_themes_user_selection 2024-07-05 16:54:11 -05:00
David Bolack
8570335d79 Consolidate variable redundancy. 2024-07-05 16:53:21 -05:00
Trevor Buckner
e4bde91f6a Merge branch 'master' into brew_themes_user_selection 2024-07-02 12:04:17 -04:00
David Bolack
853f048812 Merge branch 'Issue_2563' of github.com:dbolack-ab/homebrewery into Issue_2563 2024-06-27 10:48:41 -05:00
David Bolack
4564066c63 Merge branch 'master' into Issue_2563 2024-06-27 10:44:32 -05:00
David Bolack
ba76c51da7 Merge branch 'master' into brew_themes_user_selection 2024-06-19 19:32:04 -05:00
David Bolack
435fb6ecfe Merge branch 'Issue_2688' of github.com:dbolack-ab/homebrewery into Issue_2688 2024-06-19 19:16:52 -05:00
David Bolack
8a10fac81e Update Style based on suggestions. 2024-06-19 19:16:19 -05:00
David Bolack
7f1949a7f4 Merge branch 'master' into Issue_2688 2024-06-19 19:16:12 -05:00
Víctor Losada Hernández
47b56398b1 Merge branch 'master' into Issue_2563 2024-06-15 18:09:55 +02:00
Víctor Losada Hernández
2ea2f41bd0 Merge branch 'master' into Issue_2688 2024-06-15 18:05:28 +02:00
David Bolack
7a349ae26d Remove weirdly redundant error box. 2024-06-13 18:13:32 -05:00
David Bolack
0945a5e47e Merge branch 'master' into brew_themes_user_selection 2024-06-13 15:15:30 -05:00
Víctor Losada Hernández
65d6eb11dd suggested changes 2024-06-10 22:09:25 +02:00
Víctor Losada Hernández
a591763d10 "Moved iOS-specific styles from .monster.frame to a separate @supports block" 2024-06-09 00:43:11 +02:00
Víctor Losada Hernández
62bf982a73 change to browser-specific solution 2024-06-08 23:40:11 +02:00
Víctor Losada Hernández
6294b12ad5 "Removed background-blend-mode from monster stat block and updated monsterBlockBackground image URL" 2024-06-08 22:59:12 +02:00
David Bolack
b7f88d53d0 Solve for .descriptive and .wide not getting along with suggested fix from Issue.
Uses more more direct target rather than rearrange the file.
2024-06-05 17:44:29 -05:00
David Bolack
cb8d98266c Update table of content list-item breakage rules for wide table of contents
Solves #2563
2024-06-05 17:17:49 -05:00
David Bolack
6464f35ce9 Forgot to run npm install after merge 2024-05-31 22:40:22 -05:00
David Bolack
5442f232d5 Merge branch 'master' into brew_themes_user_selection 2024-05-31 22:32:14 -05:00
David Bolack
54d2709d6a Merge 2024-05-20 17:58:22 -05:00
David Bolack
916bd5f4d6 Merge branch 'master' into brew_themes_user_selection 2024-05-20 17:56:21 -05:00
David Bolack
c6f62142e1 Change the ID used for User Brews to the shareId for future-proofing. 2024-05-17 20:53:06 -05:00
David Bolack
69f01b282a CSS Tweaks for Theme Selector
Add 5e-Cleric's suggestsions to acvoid the title overflowing over the preview.
2024-05-13 22:33:58 -05:00
David Bolack
66e39d9c65 Update Theme Selector display
For User/Brew Themes, display the first author instead of Brew/V3 in the first column.
2024-05-13 22:24:41 -05:00
David Bolack
8c5f4e0605 Brew Theme Fixes.
This adds the User Brew themes, where applicible, to the /new path.

This adds a semi-graceful failure to the metadata panel when a Brew Theme is declared as used but is not present.

More gracefully handles loading with themes not present.
2024-05-13 11:14:35 -05:00
David Bolack
ed210da4af Merge branch 'master' into brew_themes_user_selection 2024-05-12 12:08:16 -05:00
David Bolack
b6c2f96b82 Change tag filtering for theme detection to require meta prefix 2024-05-10 01:40:01 -04:00
David Bolack
6f7a657b59 Merge branch 'brew_themes_user_selection' of github.com:dbolack-ab/homebrewery into brew_themes_user_selection 2024-05-08 12:52:01 -05:00
David Bolack
65495b4e7c Prevent Legacy renderer brews from being listed as themes. 2024-05-08 12:51:10 -05:00
David Bolack
07ca134d98 Merge pull request #1 from Gazook89/dropdownTextures-User-Themes
Add dropdownTexture for user theme options
2024-05-07 16:39:13 -05:00
David Bolack
dde8e28d07 Merge branch 'brew_themes_user_selection' into dropdownTextures-User-Themes 2024-05-07 16:38:56 -05:00
David Bolack
872ee339da Clean up console logs
Eliminate erroronous theme pulldown texture load.
2024-05-07 12:13:57 -05:00
Gazook89
295fea7581 Add dropdownTexture for user theme options
If a user theme document has a thumbnail, this will include that thumbnail as a dropdown texture in the options.
2024-05-07 11:00:20 -05:00
David Bolack
f936b8b12b Update User brew endpoint tests 2024-05-06 20:28:46 -05:00
David Bolack
9f04c34b06 Missed $ 2024-05-06 20:07:33 -05:00
David Bolack
c9d416fec0 Small User Brew theme changes.
Move the Static Theme shortcut to getBrewThemeWithCSS to drop an unneeded URL load.

Change the comment in the CSS to refer to the shareURL for the theme
instead of its name if it is a user theme.
2024-05-06 19:39:27 -05:00
David Bolack
3dde6a098c Test code to reduce duplicate theme loading
This shorts out loading of 5ePHB and/or blank from getBrewThemeParent
2024-05-06 12:12:46 -05:00
David Bolack
ef25139ffe Merge branch 'master' into brew_themes_user_selection 2024-05-06 12:04:38 -05:00
David Bolack
88ccb955ce Merge branch 'master' into brew_themes_user_selection 2024-04-30 20:11:32 -05:00
David Bolack
980ed8e265 Merge branch 'master' into brew_themes_user_selection 2024-04-20 21:55:59 -05:00
David Bolack
1292d9ad9b Merge branch 'master' into brew_themes_user_selection 2024-04-18 21:13:01 -05:00
David Bolack
57f0aefbc8 Merge branch 'master' into brew_themes_user_selection 2024-03-25 20:38:28 -05:00
David Bolack
f2f32c35ea Ensure shared pages work with user themes. 2024-03-10 11:54:48 -05:00
David Bolack
d4770f16e3 Merge branch 'master' into brew_themes_user_selection 2024-03-09 20:25:24 -06:00
David Bolack
e487f9a951 Fix remaining jest issues 2024-03-06 23:27:43 -06:00
David Bolack
eb4ecf853b Fix Jest issues I was able to understand 2024-03-06 22:50:24 -06:00
David Bolack
54e2deaddc Merge branch 'master' into brew_themes_user_selection 2024-03-06 19:28:37 -06:00
David Bolack
1ac510af3d Heroku debug 2024-03-06 19:24:16 -06:00
David Bolack
a666c8def3 Heroku debug 2024-03-06 19:20:16 -06:00
David Bolack
33933ef212 Attempted fix on access 2024-03-06 19:14:16 -06:00
David Bolack
d9dade7181 Fix User Brew display label in metadata editor 2024-03-06 19:04:12 -06:00
David Bolack
87502f4249 Heavy rework for usertheme parents. 2024-03-06 18:55:12 -06:00
David Bolack
9adafbd473 more heroku debug 2024-03-06 14:12:13 -06:00
David Bolack
47ea2f6ed7 more heroku debug 2024-03-06 14:04:16 -06:00
David Bolack
e2ba0ec059 more heroku debug 2024-03-06 13:59:36 -06:00
David Bolack
870cbc103d more heroku debug 2024-03-06 13:57:32 -06:00
David Bolack
dfca664f6e more heroku debug 2024-03-06 13:53:54 -06:00
David Bolack
00cfd427b1 more heroku debug 2024-03-06 13:47:40 -06:00
David Bolack
e639a32822 more heroku debug 2024-03-06 13:41:58 -06:00
David Bolack
8765bc800d more heroku debug 2024-03-06 13:38:48 -06:00
David Bolack
1dc73a951e more heroku debug 2024-03-06 13:35:08 -06:00
David Bolack
317b80bf4d more heroku debug 2024-03-06 13:20:57 -06:00
David Bolack
2aaae95e89 more heroku debug 2024-03-06 13:16:57 -06:00
David Bolack
0580e45af9 more heroku debug 2024-03-06 13:11:49 -06:00
David Bolack
0dbf6453ac more heroku debug 2024-03-06 13:04:03 -06:00
David Bolack
695324832c more heroku debug 2024-03-06 12:56:33 -06:00
David Bolack
ac4c84e7a4 config file fix 2024-03-06 12:44:20 -06:00
David Bolack
18aa453bb0 Rearrange and leverage getBrew 2024-03-06 12:38:00 -06:00
David Bolack
17f78169f2 More debug 2024-03-06 12:03:27 -06:00
David Bolack
6f6a06c8c3 More debug 2024-03-06 11:00:43 -06:00
David Bolack
79a4291153 More debug 2024-03-06 10:37:10 -06:00
David Bolack
a54fb98d4e Additional debugging 2024-03-06 10:28:09 -06:00
David Bolack
a3549ae694 Merge branch 'master' into brew_themes_user_selection 2024-03-06 09:35:10 -06:00
David Bolack
42c441f534 Merge branch 'master' into brew_themes_user_selection 2024-03-04 16:54:13 -06:00
David Bolack
a924f53320 Merge branch 'master' into brew_themes_user_selection 2024-03-03 22:11:35 -06:00
David Bolack
4f90f92b38 Additional theme based error checking. 2024-02-28 15:08:00 -06:00
David Bolack
753b3befad Fix issue with empty theme ( /faq ) 2024-02-28 14:53:40 -06:00
David Bolack
544bc9bd01 Catch bad assumption in unlogged saves 2024-02-28 08:32:15 -06:00
David Bolack
1a467565c1 Merge branch 'master' into brew_themes_user_selection 2024-02-27 21:32:33 -06:00
David Bolack
562daf9b04 Handle some statics 2024-02-27 21:13:22 -06:00
David Bolack
8f15887c03 Cleanup of console logging 2024-02-27 20:51:59 -06:00
David Bolack
7384cdc241 My god it works 2024-02-27 20:20:43 -06:00
David Bolack
56851f2c2d Edit working - with noise. 2024-02-27 19:33:33 -06:00
David Bolack
50c9d95ce0 WIP trying to debug theme selection. 2024-02-27 17:30:14 -06:00
David Bolack
4f4659b0e2 Cleaned up noise in homebrew.api.js 2024-02-27 13:57:58 -06:00
David Bolack
7b3a1eb4ff Functional user theme loading though noising console 2024-02-27 13:41:51 -06:00
David Bolack
2456432844 Exclude self from brew themes list to prevent circular ref. 2024-02-23 17:12:54 -06:00
David Bolack
3e66647f9f Fix @import loading on Chrome. 2024-02-23 14:43:29 -06:00
David Bolack
6d6571be0b Merge branch 'master' into brew_themes_user_selection 2024-02-23 13:46:56 -06:00
David Bolack
f9307986cd WIP
@import statements are just not working. Uploaded for other eyes.
2024-02-22 23:06:40 -06:00
David Bolack
f60090e5fa Update Theme Picker to use Brews tagged as a theme
This updates the theme picker to include brews tagged as themes owned by
the user.

Some supporting functions were updated. User themes are loaded on /edit
and added to the request.
2024-02-22 21:12:56 -06:00
David Bolack
ae2bb3a028 Add missing style.css 2024-02-20 23:26:09 -06:00
David Bolack
c319d6bcfa Consolidate and add theme parent walking
This consolidates the style/theme endpoint to a singular method, adds
interpretation of static themes, and allow parent theme recursion.

I am not 100% sure this will order styles correctly.
2024-02-20 23:15:37 -06:00
David Bolack
e2ef9b8122 Report Theme title with CSS
This adds a comment/field ( depending on endpoint ) that reports the
name of the Brew being used as a theming source.
2024-02-20 16:44:17 -06:00
David Bolack
8e48df5de1 Partial Code coverage for new endpoints 2024-02-18 12:45:14 -06:00
David Bolack
a3b1d7fb7c Use a brew as a theme, three ways.
This has been implemented three different ways to allow for comparison
and discussion

- /api/css/:id : This returns the style frontmatter of the referenced
  document as a text/css document.
/api/theme/:id : This returns an object with the reference'd object's
theme and style frontmatter.
/api/csstheme/:id : This returns the stylye frontmatter of the
referenced document as a text/css document and adds the theme as an
@import ( if not using the legacy renderer )
2024-02-17 11:01:21 -06:00
38 changed files with 3425 additions and 2783 deletions

View File

@@ -3,7 +3,7 @@
"stylelint-config-recess-order",
"stylelint-config-recommended"],
"plugins": [
"stylelint-stylistic",
"@stylistic/stylelint-plugin",
"./stylelint_plugins/declaration-colon-align.js",
"./stylelint_plugins/declaration-colon-min-space-before",
"./stylelint_plugins/declaration-block-multi-line-min-declarations"
@@ -16,32 +16,32 @@
"font-family-no-missing-generic-family-keyword" : null,
"font-weight-notation" : "named-where-possible",
"font-family-name-quotes" : "always-unless-keyword",
"stylistic/indentation" : "tab",
"@stylistic/indentation" : "tab",
"no-duplicate-selectors" : true,
"stylistic/color-hex-case" : "upper",
"@stylistic/color-hex-case" : "upper",
"color-hex-length" : "long",
"stylistic/selector-combinator-space-after" : "always",
"stylistic/selector-combinator-space-before" : "always",
"stylistic/selector-attribute-operator-space-before" : "never",
"stylistic/selector-attribute-operator-space-after" : "never",
"stylistic/selector-attribute-brackets-space-inside" : "never",
"@stylistic/selector-combinator-space-after" : "always",
"@stylistic/selector-combinator-space-before" : "always",
"@stylistic/selector-attribute-operator-space-before" : "never",
"@stylistic/selector-attribute-operator-space-after" : "never",
"@stylistic/selector-attribute-brackets-space-inside" : "never",
"selector-attribute-quotes" : "always",
"selector-pseudo-element-colon-notation" : "double",
"stylistic/selector-pseudo-class-parentheses-space-inside" : "never",
"stylistic/block-opening-brace-space-before" : "always",
"@stylistic/selector-pseudo-class-parentheses-space-inside" : "never",
"@stylistic/block-opening-brace-space-before" : "always",
"naturalcrit/declaration-colon-min-space-before" : 1,
"stylistic/declaration-block-trailing-semicolon" : "always",
"stylistic/declaration-colon-space-after" : "always",
"stylistic/number-leading-zero" : "always",
"@stylistic/declaration-block-trailing-semicolon" : "always",
"@stylistic/declaration-colon-space-after" : "always",
"@stylistic/number-leading-zero" : "always",
"function-url-quotes" : ["always", { "except": ["empty"] }],
"function-url-scheme-disallowed-list" : ["data","http"],
"comment-whitespace-inside" : "always",
"stylistic/string-quotes" : "single",
"stylistic/media-feature-range-operator-space-before" : "always",
"stylistic/media-feature-range-operator-space-after" : "always",
"stylistic/media-feature-parentheses-space-inside" : "never",
"stylistic/media-feature-colon-space-before" : "always",
"stylistic/media-feature-colon-space-after" : "always",
"@stylistic/string-quotes" : "single",
"@stylistic/media-feature-range-operator-space-before" : "always",
"@stylistic/media-feature-range-operator-space-after" : "always",
"@stylistic/media-feature-parentheses-space-inside" : "never",
"@stylistic/media-feature-colon-space-before" : "always",
"@stylistic/media-feature-colon-space-after" : "always",
"naturalcrit/declaration-colon-align" : true,
"naturalcrit/declaration-block-multi-line-min-declarations": 1
}

View File

@@ -1,4 +1,4 @@
FROM node:18-alpine
FROM node:20-alpine
RUN apk --no-cache add git
ENV NODE_ENV=docker

View File

@@ -84,6 +84,93 @@ pre {
## changelog
For a full record of development, visit our [Github Page](https://github.com/naturalcrit/homebrewery).
### Tuesday 8/13/2024 - v3.14.1
{{taskList
##### abquintic
* [x] Allow Table of Contents to flow across columns
Fixes issues [#2563](https://github.com/naturalcrit/homebrewery/issues/2563)
* [x] Fix unusual margin spacing for adjacent `.descriptive` and `.wide` blocks
Fixes issues [#2688](https://github.com/naturalcrit/homebrewery/issues/2688)
* [x] Add code folding to :fas_paintbrush: {{openSans **STYLE**}} tab
##### G-Ambatte
* [x] Fix edge case where Table of Contents generator changed capitalization of headings
Fixes issues [#3572](https://github.com/naturalcrit/homebrewery/issues/3572)
* [x] Fix **Ink Friendly** snippet causing unselectable PDF text
Fixes issues [#3563](https://github.com/naturalcrit/homebrewery/issues/3563)
* [x] Prevent brews selecting themselves as a theme
Fixes issues [#3614](https://github.com/naturalcrit/homebrewery/issues/3614)
* [x] Fix info pages (`/faq`, `/migrate`, etc.) showing blank authorship info
Fixes issues [#3568](https://github.com/naturalcrit/homebrewery/issues/3568)
* [x] Add `abs()`, `sign()` and `signed()` functions to variable syntax math handler
Fixes issues [#3537](https://github.com/naturalcrit/homebrewery/issues/3537)
* [x] Fix variable math handler not processing commas (i.e., in `$[max(varA,varB)]`
Fixes issues [#3613](https://github.com/naturalcrit/homebrewery/issues/3613)
* [x] Fix variable math handler scrambling variables with names that are subsets of other variables
Fixes issues [#3622](https://github.com/naturalcrit/homebrewery/issues/3622)
##### calculuschild
* [x] Fix `/migrate` page using an editor context instead of share context
##### 5e-Cleric
* [x] Fix Monster Stat Blocks losing color in Safari
}}
\page
### Monday 7/29/2024 - v3.14.0
{{taskList
##### abquintic, calculuschild
* [x] Alternative Brew Themes, including importing other brews as a base theme.
- In the :fas_circle_info: **Properties** menu, find the new {{openSans **THEME**}} dropdown. It lists Brew Themes, including a new **Blank** theme as a simpler basis for custom styling.
- Brews tagged with `meta:theme` will appear in the Brew Themes list. Selecting one loads its :fas_paintbrush: **Style** tab contents as the CSS basis for the current brew, allowing one brew to style multiple documents.
- Brews with `meta:theme` can also select their own Theme, i.e. layering Themes on top of each other.
- The next goal is to make **Published** Themes shareable between users.
Fixes issues [#1899](https://github.com/naturalcrit/homebrewery/issues/1899), [#3085](https://github.com/naturalcrit/homebrewery/issues/3085)
##### G-Ambatte
* [x] Fix Drop-cap font becoming corrupted when Bold
Fixes issues [#3551](https://github.com/naturalcrit/homebrewery/issues/3551)
* [x] Fixes to UI styling
Fixes issues [#3568](https://github.com/naturalcrit/homebrewery/issues/3568)
}}
### Saturday 6/7/2024 - v3.13.1
{{taskList
@@ -131,8 +218,6 @@ Fixes issue [#3298](https://github.com/naturalcrit/homebrewery/issues/3298)
Fixes issue [#3397](https://github.com/naturalcrit/homebrewery/issues/3397)
}}
\column
### Monday 18/3/2024 - v3.12.0
{{taskList

View File

@@ -18,8 +18,6 @@ const { printCurrentBrew } = require('../../../shared/helpers.js');
const DOMPurify = require('dompurify');
const purifyConfig = { FORCE_BODY: true, SANITIZE_DOM: false };
const Themes = require('themes/themes.json');
const PAGE_HEIGHT = 1056;
const INITIAL_CONTENT = dedent`
@@ -57,6 +55,7 @@ const BrewRenderer = (props)=>{
lang : '',
errors : [],
currentEditorPage : 0,
themeBundle : {},
...props
};
@@ -125,10 +124,9 @@ const BrewRenderer = (props)=>{
};
const renderStyle = ()=>{
if(!props.style) return;
const cleanStyle = props.style; //DOMPurify.sanitize(props.style, purifyConfig);
//return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style>@layer styleTab {\n${sanitizeScriptTags(props.style)}\n} </style>` }} />;
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `<style> ${cleanStyle} </style>` }} />;
const themeStyles = props.themeBundle?.joinedStyles ?? '<style>@import url("/themes/V3/Blank/style.css");</style>';
return <div style={{ display: 'none' }} dangerouslySetInnerHTML={{ __html: `${themeStyles} \n\n <style> ${cleanStyle} </style>` }} />;
};
const renderPage = (pageText, index)=>{
@@ -188,10 +186,6 @@ const BrewRenderer = (props)=>{
document.dispatchEvent(new MouseEvent('click'));
};
const rendererPath = props.renderer == 'V3' ? 'V3' : 'Legacy';
const themePath = props.theme ?? '5ePHB';
const baseThemePath = Themes[rendererPath][themePath].baseTheme;
return (
<>
{/*render dummy page while iFrame is mounting.*/}
@@ -220,13 +214,6 @@ const BrewRenderer = (props)=>{
onKeyDown={handleControlKeys}
tabIndex={-1}
style={{ height: state.height }}>
<link href={`/themes/${rendererPath}/Blank/style.css`} type='text/css' rel='stylesheet'/>
{baseThemePath &&
<link href={`/themes/${rendererPath}/${baseThemePath}/style.css`} type='text/css' rel='stylesheet'/>
}
<link href={`/themes/${rendererPath}/${themePath}/style.css`} type='text/css' rel='stylesheet'/>
{/* Apply CSS from Style tab and render pages from Markdown tab */}
{state.isMounted
&&

View File

@@ -367,7 +367,7 @@ const Editor = createClass({
view={this.state.view}
value={this.props.brew.style ?? DEFAULT_STYLE_TEXT}
onChange={this.props.onStyleChange}
enableFolding={false}
enableFolding={true}
editorTheme={this.state.editorTheme}
rerenderParent={this.rerenderParent} />
</>;
@@ -381,7 +381,8 @@ const Editor = createClass({
<MetadataEditor
metadata={this.props.brew}
onChange={this.props.onMetaChange}
reportError={this.props.reportError}/>
reportError={this.props.reportError}
userThemes={this.props.userThemes}/>
</>;
}
},
@@ -424,6 +425,7 @@ const Editor = createClass({
historySize={this.historySize()}
currentEditorTheme={this.state.editorTheme}
updateEditorTheme={this.updateEditorTheme}
snippetBundle={this.props.snippetBundle}
cursorPos={this.codeEditor.current?.getCursorPosition() || {}} />
{this.renderEditor()}

View File

@@ -8,6 +8,7 @@ const Nav = require('naturalcrit/nav/nav.jsx');
const Combobox = require('client/components/combobox.jsx');
const StringArrayEditor = require('../stringArrayEditor/stringArrayEditor.jsx');
const Themes = require('themes/themes.json');
const validations = require('./validations.js');
@@ -27,6 +28,7 @@ const MetadataEditor = createClass({
return {
metadata : {
editId : null,
shareId : null,
title : '',
description : '',
thumbnail : '',
@@ -98,7 +100,7 @@ const MetadataEditor = createClass({
if(renderer == 'legacy')
this.props.metadata.theme = '5ePHB';
}
this.props.onChange(this.props.metadata);
this.props.onChange(this.props.metadata, 'renderer');
},
handlePublish : function(val){
this.props.onChange({
@@ -110,7 +112,7 @@ const MetadataEditor = createClass({
handleTheme : function(theme){
this.props.metadata.renderer = theme.renderer;
this.props.metadata.theme = theme.path;
this.props.onChange(this.props.metadata);
this.props.onChange(this.props.metadata, 'theme');
},
handleLanguage : function(languageCode){
@@ -191,37 +193,42 @@ const MetadataEditor = createClass({
renderThemeDropdown : function(){
if(!global.enable_themes) return;
const mergedThemes = _.merge(Themes, this.props.userThemes);
const listThemes = (renderer)=>{
return _.map(_.values(Themes[renderer]), (theme)=>{
return <div className='item' key={''} onClick={()=>this.handleTheme(theme)} title={''}>
{`${theme.renderer} : ${theme.name}`}
<img src={`/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`}/>
return _.map(_.values(mergedThemes[renderer]), (theme)=>{
if(theme.path == this.props.metadata.shareId) return;
const preview = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownPreview.png`;
const texture = theme.thumbnail || `/themes/${theme.renderer}/${theme.path}/dropdownTexture.png`;
return <div className='item' key={`${renderer}_${theme.name}`} onClick={()=>this.handleTheme(theme)} title={''}>
{theme.author ?? renderer} : {theme.name}
<div className='texture-container'>
<img src={texture}/>
</div>
<div className='preview'>
<h6>{`${theme.name}`} preview</h6>
<img src={`/themes/${theme.renderer}/${theme.path}/dropdownPreview.png`}/>
<h6>{theme.name} preview</h6>
<img src={preview}/>
</div>
</div>;
});
};
const currentTheme = Themes[`${_.upperFirst(this.props.metadata.renderer)}`][this.props.metadata.theme];
const currentRenderer = this.props.metadata.renderer;
const currentTheme = mergedThemes[`${_.upperFirst(this.props.metadata.renderer)}`][this.props.metadata.theme]
?? { name: `!!! THEME MISSING !!! ID=${this.props.metadata.theme}` };
let dropdown;
if(this.props.metadata.renderer == 'legacy') {
if(currentRenderer == 'legacy') {
dropdown =
<Nav.dropdown className='disabled value' trigger='disabled'>
<div>
{`Themes are not supported in the Legacy Renderer`} <i className='fas fa-caret-down'></i>
</div>
<div> {`Themes are not supported in the Legacy Renderer`} <i className='fas fa-caret-down'></i> </div>
</Nav.dropdown>;
} else {
dropdown =
<Nav.dropdown className='value' trigger='click'>
<div>
{`${_.upperFirst(currentTheme.renderer)} : ${currentTheme.name}`} <i className='fas fa-caret-down'></i>
</div>
{/*listThemes('Legacy')*/}
{listThemes('V3')}
<div> {currentTheme.author ?? _.upperFirst(currentRenderer)} : {currentTheme.name} <i className='fas fa-caret-down'></i> </div>
{listThemes(currentRenderer)}
</Nav.dropdown>;
}

View File

@@ -1,327 +1,309 @@
@import 'naturalcrit/styles/colors.less';
.metadataEditor{
.metadataEditor {
position : absolute;
z-index : 5;
box-sizing : border-box;
width : 100%;
padding : 25px;
background-color : #999;
height : calc(100vh - 54px); // 54px is the height of the navbar + snippet bar. probably a better way to dynamic get this.
padding : 25px;
overflow-y : auto;
background-color : #999999;
.sectionHead {
font-weight: 1000;
margin: 20px 0;
margin : 20px 0;
font-weight : 1000;
&:first-of-type {
margin-top: 0;
}
&:first-of-type { margin-top : 0; }
}
& > div {
margin-bottom: 10px;
}
& > div { margin-bottom : 10px; }
.field-group {
display: flex;
width: 100%;
flex-wrap: wrap;
gap: 10px;
display : flex;
flex-wrap : wrap;
gap : 10px;
width : 100%;
}
.field-column {
display: flex;
flex-direction: column;
flex: 5 0 200px;
gap: 10px;
display : flex;
flex : 5 0 200px;
flex-direction : column;
gap : 10px;
}
.field{
.field {
position : relative;
display : flex;
flex-wrap : wrap;
width : 100%;
min-width : 200px;
position : relative;
&>label{
& > label {
width : 80px;
font-size : 11px;
font-weight : 800;
line-height : 1.8em;
text-transform : uppercase;
}
&>.value{
& > .value {
flex : 1 1 auto;
width : 50px;
&:invalid {
background : #ffb9b9;
}
&:invalid { background : #FFB9B9; }
}
input[type='text'], textarea {
border : 1px solid gray;
&:focus {
outline: 1px solid #444;
}
&:focus { outline : 1px solid #444444; }
}
&.thumbnail{
&.thumbnail {
height : 1.4em;
label{
line-height: 2.0em;
label { line-height : 2.0em; }
.value {
overflow : hidden;
text-overflow : ellipsis;
}
.value{
overflow: hidden;
text-overflow: ellipsis;
}
button{
border: 1px solid #999;
color: white;
padding: 0px 5px;
background-color: black;
&:hover{
background-color: #777;
}
button {
padding : 0px 5px;
color : white;
background-color : black;
border : 1px solid #999999;
&:hover { background-color : #777777; }
}
}
&.description {
flex: 1;
flex : 1;
textarea.value {
resize : none;
height : auto;
font-family : 'Open Sans', sans-serif;
font-size : 0.8em;
resize : none;
}
}
&.language .language-dropdown {
max-width : 150px;
z-index : 200;
max-width : 150px;
}
small {
display : inline-block;
font-size : 0.6em;
font-style : italic;
line-height : 1.4em;
display : inline-block;
}
}
.thumbnail-preview {
position: relative;
justify-self: center;
width: 80px;
height: min-content;
flex: 1 1;
max-height: 115px;
aspect-ratio: 1 / 1;
object-fit: contain;
background-color: #AAA;
position : relative;
flex : 1 1;
justify-self : center;
width : 80px;
height : min-content;
max-height : 115px;
aspect-ratio : 1 / 1;
object-fit : contain;
background-color : #AAAAAA;
}
.systems.field .value{
label{
vertical-align : middle;
margin-right : 15px;
cursor : pointer;
font-size : 0.7em;
font-weight : 800;
user-select : none;
white-space : nowrap;
.systems.field .value {
label {
display : inline-flex;
align-items : center;
}
a {
font-size : 0.7em;
font-weight : 800;
display : inline-flex;
}
input{
margin-right : 15px;
font-size : 0.7em;
font-weight : 800;
white-space : nowrap;
vertical-align : middle;
cursor : pointer;
user-select : none;
}
a {
display : inline-flex;
font-size : 0.7em;
font-weight : 800;
}
input {
margin : 3px;
vertical-align : middle;
cursor : pointer;
}
}
.publish.field .value{
position : relative;
margin-bottom: 15px;
button{
width:100%;
}
button.publish{
.publish.field .value {
position : relative;
margin-bottom : 15px;
button { width : 100%; }
button.publish {
.button(@blueLight);
}
button.unpublish{
button.unpublish {
.button(@silver);
}
}
.delete.field .value{
button{
.delete.field .value {
button {
.button(@red);
}
}
.authors.field .value{
font-size: 0.8em;
.authors.field .value {
font-size : 0.8em;
line-height : 1.5em;
}
.themes.field{
.themes.field {
font-size : 13.33px;
.navDropdownContainer {
background-color : white;
position : relative;
z-index : 100;
position : relative;
z-index : 100;
background-color : white;
&.disabled {
font-style :italic;
font-style : italic;
background-color : darkgray;
color : dimgray;
font-style : italic;
color : dimgray;
background-color : darkgray;
}
&>div:first-child {
border : 2px solid rgb(118,118,118);
padding : 6px 3px;
background-color : inherit;
i {
float : right;
}
& > div:first-child {
padding : 6px 3px;
background-color : inherit;
border : 2px solid rgb(118,118,118);
i { float : right; }
&:hover {
background-color : @blue;
color : white;
color : white;
background-color : @blue;
}
}
.navDropdown .item > p {
width : 45%;
height : 1.1em;
overflow : hidden;
text-overflow : ellipsis;
white-space : nowrap;
}
.navDropdown {
box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3);
position : absolute;
width : 100%;
box-shadow : 0px 5px 10px rgba(0, 0, 0, 0.3);
.item {
padding : 3px 3px;
border-top : 1px solid rgb(118, 118, 118);
position : relative;
padding : 3px 3px;
overflow : visible;
background-color : white;
background-color : white;
border-top : 1px solid rgb(118, 118, 118);
.preview {
display : flex;
flex-direction: column;
background : #ccc;
border-radius : 5px;
box-shadow : 0 0 5px black;
width : 200px;
color :black;
position : absolute;
top : 0;
right : 0;
opacity : 0;
transition : opacity 250ms ease;
z-index : 1;
overflow :hidden;
position : absolute;
top : 0;
right : 0;
z-index : 1;
display : flex;
flex-direction : column;
width : 200px;
overflow : hidden;
color : black;
background : #CCCCCC;
border-radius : 5px;
box-shadow : 0 0 5px black;
opacity : 0;
transition : opacity 250ms ease;
h6 {
font-weight : 900;
padding-inline:1em;
padding-block :.5em;
border-bottom :2px solid hsl(0,0%,40%);
padding-block : 0.5em;
padding-inline : 1em;
font-weight : 900;
border-bottom : 2px solid hsl(0,0%,40%);
}
}
&:hover {
background-color : @blue;
color : white;
color : white;
background-color : @blue;
}
&:hover > .preview {
opacity: 1;
}
>img {
mask-image : linear-gradient(90deg, transparent, black 20%);
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
position : absolute;
right : 0;
top : 0px;
width : 50%;
height : 100%;
&:hover > .preview { opacity : 1; }
.texture-container {
position : absolute;
top : 0;
left : 0;
width : 100%;
height : 100%;
min-height : 100%;
overflow : hidden;
> img {
position : absolute;
top : 0px;
right : 0;
width : 50%;
min-height : 100%;
-webkit-mask-image : linear-gradient(90deg, transparent, black 20%);
mask-image : linear-gradient(90deg, transparent, black 20%);
}
}
}
}
}
}
.field .list {
display: flex;
flex: 1 0;
flex-wrap: wrap;
display : flex;
flex : 1 0;
flex-wrap : wrap;
> * {
flex: 0 0 auto;
}
> * { flex : 0 0 auto; }
#groupedIcon {
#backgroundColors;
display: inline-block;
height: ~"calc(100% + 0.6em)";
position: relative;
top: -0.3em;
right: -0.3em;
cursor: pointer;
min-width: 20px;
text-align: center;
color: white;
position : relative;
top : -0.3em;
right : -0.3em;
display : inline-block;
min-width : 20px;
height : ~'calc(100% + 0.6em)';
color : white;
text-align : center;
cursor : pointer;
i {
position: relative;
top: 50%;
transform: translateY(-50%);
position : relative;
top : 50%;
transform : translateY(-50%);
}
&:not(:last-child) {
border-right: 1px solid black;
}
&:not(:last-child) { border-right : 1px solid black; }
&:last-child {
border-radius: 0 0.5em 0.5em 0;
}
&:last-child { border-radius : 0 0.5em 0.5em 0; }
}
.badge {
background-color: #dddddd;
border-radius: .5em;
font-size: .9em;
margin: 2px;
padding: .3em;
padding : 0.3em;
margin : 2px;
font-size : 0.9em;
background-color : #DDDDDD;
border-radius : 0.5em;
.icon {
#groupedIcon
}
#groupedIcon; }
}
.input-group {
height: ~"calc(.9em + 4px + .6em)";
height : ~'calc(.9em + 4px + .6em)';
input {
border-radius: .5em 0 0 .5em;
}
input { border-radius : 0.5em 0 0 0.5em; }
input:last-child {
border-radius: .5em;
}
input:last-child { border-radius : 0.5em; }
.value {
width: 7.5vw;
min-width: 75px;
height: 100%;
width : 7.5vw;
min-width : 75px;
height : 100%;
}
.invalid:focus {
background-color: pink;
}
.invalid:focus { background-color : pink; }
.icon {
#groupedIcon;
height: 97%;
font-size: .8em;
right: 1px;
top: -.54em;
top : -0.54em;
right : 1px;
height : 97%;
font-size : 0.8em;
i {
font-size: 1.125em;
}
i { font-size : 1.125em; }
}
}
}

View File

@@ -6,9 +6,6 @@ const _ = require('lodash');
const cx = require('classnames');
//Import all themes
const Themes = require('themes/themes.json');
const ThemeSnippets = {};
ThemeSnippets['Legacy_5ePHB'] = require('themes/Legacy/5ePHB/snippets.js');
ThemeSnippets['V3_5ePHB'] = require('themes/V3/5ePHB/snippets.js');
@@ -40,7 +37,8 @@ const Snippetbar = createClass({
foldCode : ()=>{},
unfoldCode : ()=>{},
updateEditorTheme : ()=>{},
cursorPos : {}
cursorPos : {},
snippetBundle : []
};
},
@@ -53,21 +51,15 @@ const Snippetbar = createClass({
},
componentDidMount : async function() {
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
const themePath = this.props.theme ?? '5ePHB';
let snippets = _.cloneDeep(ThemeSnippets[`${rendererPath}_${themePath}`]);
snippets = this.compileSnippets(rendererPath, themePath, snippets);
const snippets = this.compileSnippets();
this.setState({
snippets : snippets
});
},
componentDidUpdate : async function(prevProps) {
if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme) {
const rendererPath = this.props.renderer == 'V3' ? 'V3' : 'Legacy';
const themePath = this.props.theme ?? '5ePHB';
let snippets = _.cloneDeep(ThemeSnippets[`${rendererPath}_${themePath}`]);
snippets = this.compileSnippets(rendererPath, themePath, snippets);
if(prevProps.renderer != this.props.renderer || prevProps.theme != this.props.theme || prevProps.snippetBundle != this.props.snippetBundle) {
const snippets = this.compileSnippets();
this.setState({
snippets : snippets
});
@@ -75,26 +67,26 @@ const Snippetbar = createClass({
},
mergeCustomizer : function(valueA, valueB, key) {
mergeCustomizer : function(oldValue, newValue, key) {
if(key == 'snippets') {
const result = _.reverse(_.unionBy(_.reverse(valueB), _.reverse(valueA), 'name')); // Join snippets together, with preference for the current theme over the base theme
const result = _.reverse(_.unionBy(_.reverse(newValue), _.reverse(oldValue), 'name')); // Join snippets together, with preference for the child theme over the parent theme
return _.filter(result, 'gen'); //Only keep snippets with a 'gen' property.
}
},
compileSnippets : function(rendererPath, themePath, snippets) {
let compiledSnippets = snippets;
const baseSnippetsPath = Themes[rendererPath][themePath].baseSnippets;
compileSnippets : function() {
let compiledSnippets = [];
const objB = _.keyBy(compiledSnippets, 'groupName');
let oldSnippets = _.keyBy(compiledSnippets, 'groupName');
if(baseSnippetsPath) {
const objA = _.keyBy(_.cloneDeep(ThemeSnippets[`${rendererPath}_${baseSnippetsPath}`]), 'groupName');
compiledSnippets = _.values(_.mergeWith(objA, objB, this.mergeCustomizer));
compiledSnippets = this.compileSnippets(rendererPath, baseSnippetsPath, _.cloneDeep(compiledSnippets));
} else {
const objA = _.keyBy(_.cloneDeep(ThemeSnippets[`${rendererPath}_Blank`]), 'groupName');
compiledSnippets = _.values(_.mergeWith(objA, objB, this.mergeCustomizer));
for (let snippets of this.props.snippetBundle) {
if(typeof(snippets) == 'string') // load staticThemes as needed; they were sent as just a file name
snippets = ThemeSnippets[snippets];
const newSnippets = _.keyBy(_.cloneDeep(snippets), 'groupName');
compiledSnippets = _.values(_.mergeWith(oldSnippets, newSnippets, this.mergeCustomizer));
oldSnippets = _.keyBy(compiledSnippets, 'groupName');
}
return compiledSnippets;
},

View File

@@ -66,13 +66,14 @@ const Homebrew = createClass({
<Router location={this.props.url}>
<div className='homebrew'>
<Routes>
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} />} />
<Route path='/edit/:id' element={<WithRoute el={EditPage} brew={this.props.brew} userThemes={this.props.userThemes}/>} />
<Route path='/share/:id' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} />} />
<Route path='/new' element={<WithRoute el={NewPage}/>} />
<Route path='/new/:id' element={<WithRoute el={NewPage} brew={this.props.brew} userThemes={this.props.userThemes}/>} />
<Route path='/new' element={<WithRoute el={NewPage} userThemes={this.props.userThemes}/> } />
<Route path='/user/:username' element={<WithRoute el={UserPage} brews={this.props.brews} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} />} />
<Route path='/changelog' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
<Route path='/faq' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
<Route path='/migrate' element={<WithRoute el={SharePage} brew={this.props.brew} disableMeta={true} />} />
<Route path='/account' element={<WithRoute el={AccountPage} brew={this.props.brew} accountDetails={this.props.brew.accountDetails} />} />
<Route path='/legacy' element={<WithRoute el={HomePage} brew={this.props.brew} />} />
<Route path='/error' element={<WithRoute el={ErrorPage} brew={this.props.brew} />} />

View File

@@ -104,6 +104,18 @@ const ErrorNavItem = createClass({
</Nav.item>;
}
if(HBErrorCode === '09') {
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
Oops!
<div className='errorContainer' onClick={clearError}>
Looks like there was a problem retreiving
the theme, or a theme that it inherits,
for this brew. Verify that brew <a className='lowercase' target='_blank' rel='noopener noreferrer' href={`/share/${response.body.brewId}`}>
{response.body.brewId}</a> still exists!
</div>
</Nav.item>;
}
return <Nav.item className='save error' icon='fas fa-exclamation-triangle'>
Oops!
<div className='errorContainer'>

View File

@@ -21,6 +21,9 @@
font-size : 10px;
font-weight : 800;
text-transform : uppercase;
.lowercase {
text-transform : none;
}
a{
color : @teal;
}

View File

@@ -25,7 +25,7 @@ const LockNotification = require('./lockNotification/lockNotification.jsx');
const Markdown = require('naturalcrit/markdown.js');
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
const { printCurrentBrew } = require('../../../../shared/helpers.js');
const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js');
const googleDriveIcon = require('../../googleDrive.svg');
@@ -55,7 +55,8 @@ const EditPage = createClass({
autoSaveWarning : false,
unsavedTime : new Date(),
currentEditorPage : 0,
displayLockMessage : this.props.brew.lock || false
displayLockMessage : this.props.brew.lock || false,
themeBundle : {}
};
},
@@ -87,6 +88,8 @@ const EditPage = createClass({
htmlErrors : Markdown.validate(prevState.brew.text)
}));
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
document.addEventListener('keydown', this.handleControlKeys);
},
componentWillUnmount : function() {
@@ -130,7 +133,10 @@ const EditPage = createClass({
}), ()=>{if(this.state.autoSave) this.trySave();});
},
handleMetaChange : function(metadata){
handleMetaChange : function(metadata, field=undefined){
if(field == 'theme' || field == 'renderer') // Fetch theme bundle only if theme or renderer was changed
fetchThemeBundle(this, metadata.renderer, metadata.theme);
this.setState((prevState)=>({
brew : {
...prevState.brew,
@@ -138,7 +144,6 @@ const EditPage = createClass({
},
isPending : true,
}), ()=>{if(this.state.autoSave) this.trySave();});
},
hasChanges : function(){
@@ -406,12 +411,15 @@ const EditPage = createClass({
onMetaChange={this.handleMetaChange}
reportError={this.errorReported}
renderer={this.state.brew.renderer}
userThemes={this.props.userThemes}
snippetBundle={this.state.themeBundle.snippets}
/>
<BrewRenderer
text={this.state.brew.text}
style={this.state.brew.style}
renderer={this.state.brew.renderer}
theme={this.state.brew.theme}
themeBundle={this.state.themeBundle}
errors={this.state.htmlErrors}
lang={this.state.brew.lang}
currentEditorPage={this.state.currentEditorPage}

View File

@@ -136,6 +136,19 @@ const errorIndex = (props)=>{
**Brew ID:** ${props.brew.brewId}`,
// Theme load error
'09' : dedent`
## No Homebrewery theme document could be found.
The server could not locate the Homebrewery document. It was likely deleted by
its owner.
:
**Requested access:** ${props.brew.accessType}
**Brew ID:** ${props.brew.brewId}`,
// Brew locked by Administrators error
'100' : dedent`
## This brew has been locked.

View File

@@ -13,6 +13,7 @@ const HelpNavItem = require('../../navbar/help.navitem.jsx');
const RecentNavItem = require('../../navbar/recent.navitem.jsx').both;
const AccountNavItem = require('../../navbar/account.navitem.jsx');
const ErrorNavItem = require('../../navbar/error-navitem.jsx');
const { fetchThemeBundle } = require('../../../../shared/helpers.js');
const SplitPane = require('naturalcrit/splitPane/splitPane.jsx');
@@ -34,12 +35,17 @@ const HomePage = createClass({
brew : this.props.brew,
welcomeText : this.props.brew.text,
error : undefined,
currentEditorPage : 0
currentEditorPage : 0,
themeBundle : {}
};
},
editor : React.createRef(null),
componentDidMount : function() {
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
},
handleSave : function(){
request.post('/api')
.send(this.state.brew)
@@ -89,12 +95,14 @@ const HomePage = createClass({
onTextChange={this.handleTextChange}
renderer={this.state.brew.renderer}
showEditButtons={false}
snippetBundle={this.state.themeBundle.snippets}
/>
<BrewRenderer
text={this.state.brew.text}
style={this.state.brew.style}
renderer={this.state.brew.renderer}
currentEditorPage={this.state.currentEditorPage}
themeBundle={this.state.themeBundle}
/>
</SplitPane>
</div>

View File

@@ -19,7 +19,7 @@ const Editor = require('../../editor/editor.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const { DEFAULT_BREW } = require('../../../../server/brewDefaults.js');
const { printCurrentBrew } = require('../../../../shared/helpers.js');
const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js');
const BREWKEY = 'homebrewery-new';
const STYLEKEY = 'homebrewery-new-style';
@@ -44,7 +44,8 @@ const NewPage = createClass({
saveGoogle : (global.account && global.account.googleId ? true : false),
error : null,
htmlErrors : Markdown.validate(brew.text),
currentEditorPage : 0
currentEditorPage : 0,
themeBundle : {}
};
},
@@ -77,6 +78,8 @@ const NewPage = createClass({
saveGoogle : (saveStorage == 'GOOGLE-DRIVE' && this.state.saveGoogle)
});
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
localStorage.setItem(BREWKEY, brew.text);
if(brew.style)
localStorage.setItem(STYLEKEY, brew.style);
@@ -122,7 +125,10 @@ const NewPage = createClass({
localStorage.setItem(STYLEKEY, style);
},
handleMetaChange : function(metadata){
handleMetaChange : function(metadata, field=undefined){
if(field == 'theme' || field == 'renderer') // Fetch theme bundle only if theme or renderer was changed
fetchThemeBundle(this, metadata.renderer, metadata.theme);
this.setState((prevState)=>({
brew : { ...prevState.brew, ...metadata },
}), ()=>{
@@ -142,8 +148,6 @@ const NewPage = createClass({
isSaving : true
});
console.log('saving new brew');
let brew = this.state.brew;
// Split out CSS to Style if CSS codefence exists
if(brew.text.startsWith('```css') && brew.text.indexOf('```\n\n') > 0) {
@@ -153,12 +157,10 @@ const NewPage = createClass({
}
brew.pageCount=((brew.renderer=='legacy' ? brew.text.match(/\\page/g) : brew.text.match(/^\\page$/gm)) || []).length + 1;
const res = await request
.post(`/api${this.state.saveGoogle ? '?saveToGoogle=true' : ''}`)
.send(brew)
.catch((err)=>{
console.log(err);
this.setState({ isSaving: false, error: err });
});
if(!res) return;
@@ -214,12 +216,15 @@ const NewPage = createClass({
onStyleChange={this.handleStyleChange}
onMetaChange={this.handleMetaChange}
renderer={this.state.brew.renderer}
userThemes={this.props.userThemes}
snippetBundle={this.state.themeBundle.snippets}
/>
<BrewRenderer
text={this.state.brew.text}
style={this.state.brew.style}
renderer={this.state.brew.renderer}
theme={this.state.brew.theme}
themeBundle={this.state.themeBundle}
errors={this.state.htmlErrors}
lang={this.state.brew.lang}
currentEditorPage={this.state.currentEditorPage}

View File

@@ -12,18 +12,27 @@ const Account = require('../../navbar/account.navitem.jsx');
const BrewRenderer = require('../../brewRenderer/brewRenderer.jsx');
const { DEFAULT_BREW_LOAD } = require('../../../../server/brewDefaults.js');
const { printCurrentBrew } = require('../../../../shared/helpers.js');
const { printCurrentBrew, fetchThemeBundle } = require('../../../../shared/helpers.js');
const SharePage = createClass({
displayName : 'SharePage',
getDefaultProps : function() {
return {
brew : DEFAULT_BREW_LOAD
brew : DEFAULT_BREW_LOAD,
disableMeta : false
};
},
getInitialState : function() {
return {
themeBundle : {}
};
},
componentDidMount : function() {
document.addEventListener('keydown', this.handleControlKeys);
fetchThemeBundle(this, this.props.brew.renderer, this.props.brew.theme);
},
componentWillUnmount : function() {
@@ -60,13 +69,21 @@ const SharePage = createClass({
},
render : function(){
const titleStyle = this.props.disableMeta ? { cursor: 'default' } : {};
const titleEl = <Nav.item className='brewTitle' style={titleStyle}>{this.props.brew.title}</Nav.item>;
return <div className='sharePage sitePage'>
<Meta name='robots' content='noindex, nofollow' />
<Navbar>
<Nav.section className='titleSection'>
<MetadataNav brew={this.props.brew}>
<Nav.item className='brewTitle'>{this.props.brew.title}</Nav.item>
</MetadataNav>
{
this.props.disableMeta ?
titleEl
:
<MetadataNav brew={this.props.brew}>
{titleEl}
</MetadataNav>
}
</Nav.section>
<Nav.section>
@@ -99,6 +116,7 @@ const SharePage = createClass({
style={this.props.brew.style}
renderer={this.props.brew.renderer}
theme={this.props.brew.theme}
themeBundle={this.state.themeBundle}
allowPrint={true}
/>
</div>

View File

@@ -4,6 +4,7 @@
"secret" : "secret",
"web_port" : 8000,
"enable_v3" : true,
"enable_themes" : true,
"local_environments" : ["docker", "local"],
"publicUrl" : "https://homebrewery.naturalcrit.com"
}

4996
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
{
"name": "homebrewery",
"description": "Create authentic looking D&D homebrews using only markdown",
"version": "3.13.1",
"version": "3.14.1",
"engines": {
"npm": "^10.2.x",
"node": "^20.8.x"
@@ -22,7 +22,8 @@
"circleci": "npm test && eslint **/*.{js,jsx} --max-warnings=0",
"verify": "npm run lint && npm test",
"test": "jest --runInBand",
"test:api-unit": "jest server/*.spec.js --verbose",
"test:api-unit": "jest \"server/.*.spec.js\" --verbose",
"test:api-unit:themes": "jest \"server/.*.spec.js\" -t \"theme bundle\" --verbose",
"test:coverage": "jest --coverage --silent --runInBand",
"test:dev": "jest --verbose --watch",
"test:basic": "jest tests/markdown/basic.test.js --verbose",
@@ -56,15 +57,15 @@
],
"coverageThreshold": {
"global": {
"statements": 25,
"branches": 10,
"functions": 22,
"lines": 25
"statements": 50,
"branches": 40,
"functions": 40,
"lines": 50
},
"server/homebrew.api.js": {
"statements": 65,
"statements": 70,
"branches": 50,
"functions": 60,
"functions": 65,
"lines": 70
}
},
@@ -82,9 +83,9 @@
]
},
"dependencies": {
"@babel/core": "^7.24.7",
"@babel/core": "^7.25.2",
"@babel/plugin-transform-runtime": "^7.24.7",
"@babel/preset-env": "^7.24.7",
"@babel/preset-env": "^7.25.3",
"@babel/preset-react": "^7.24.7",
"@googleapis/drive": "^8.11.0",
"body-parser": "^1.20.2",
@@ -93,7 +94,7 @@
"cookie-parser": "^1.4.6",
"create-react-class": "^15.7.0",
"dedent-tabs": "^0.10.3",
"dompurify": "^3.1.5",
"dompurify": "^3.1.6",
"expr-eval": "^2.0.2",
"express": "^4.19.2",
"express-async-handler": "^1.2.0",
@@ -104,34 +105,34 @@
"less": "^3.13.1",
"lodash": "^4.17.21",
"marked": "11.2.0",
"marked-emoji": "^1.4.1",
"marked-emoji": "^1.4.2",
"marked-extended-tables": "^1.0.8",
"marked-gfm-heading-id": "^3.2.0",
"marked-smartypants-lite": "^1.0.2",
"markedLegacy": "npm:marked@^0.3.19",
"moment": "^2.30.1",
"mongoose": "^8.4.5",
"mongoose": "^8.5.2",
"nanoid": "3.3.4",
"nconf": "^0.12.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-frame-component": "^4.1.3",
"react-router-dom": "6.24.1",
"react-router-dom": "6.26.0",
"sanitize-filename": "1.6.3",
"superagent": "^9.0.2",
"vitreum": "git+https://git@github.com/calculuschild/vitreum.git"
},
"devDependencies": {
"@stylistic/stylelint-plugin": "^3.0.0",
"eslint": "^8.57.0",
"eslint-plugin-jest": "^28.6.0",
"eslint-plugin-react": "^7.34.3",
"eslint-plugin-jest": "^28.8.0",
"eslint-plugin-react": "^7.35.0",
"jest": "^29.7.0",
"jest-expect-message": "^1.1.3",
"postcss-less": "^6.0.0",
"stylelint": "^15.11.0",
"stylelint-config-recess-order": "^4.6.0",
"stylelint-config-recommended": "^13.0.0",
"stylelint-stylistic": "^0.4.3",
"stylelint": "^16.8.0",
"stylelint-config-recess-order": "^5.0.1",
"stylelint-config-recommended": "^14.0.1",
"supertest": "^7.0.0"
}
}

View File

@@ -9,7 +9,7 @@ const yaml = require('js-yaml');
const app = express();
const config = require('./config.js');
const { homebrewApi, getBrew } = require('./homebrew.api.js');
const { homebrewApi, getBrew, getUsersBrewThemes } = require('./homebrew.api.js');
const GoogleActions = require('./googleActions.js');
const serveCompressedStaticAssets = require('./static-assets.mv.js');
const sanitizeFilename = require('sanitize-filename');
@@ -81,7 +81,8 @@ app.get('/robots.txt', (req, res)=>{
app.get('/', (req, res, next)=>{
req.brew = {
text : welcomeText,
renderer : 'V3'
renderer : 'V3',
theme : '5ePHB'
},
req.ogMeta = { ...defaultMetaTags,
@@ -97,7 +98,8 @@ app.get('/', (req, res, next)=>{
app.get('/legacy', (req, res, next)=>{
req.brew = {
text : welcomeTextLegacy,
renderer : 'legacy'
renderer : 'legacy',
theme : '5ePHB'
},
req.ogMeta = { ...defaultMetaTags,
@@ -113,7 +115,8 @@ app.get('/legacy', (req, res, next)=>{
app.get('/migrate', (req, res, next)=>{
req.brew = {
text : migrateText,
renderer : 'V3'
renderer : 'V3',
theme : '5ePHB'
},
req.ogMeta = { ...defaultMetaTags,
@@ -130,7 +133,8 @@ app.get('/changelog', async (req, res, next)=>{
req.brew = {
title : 'Changelog',
text : changelogText,
renderer : 'V3'
renderer : 'V3',
theme : '5ePHB'
},
req.ogMeta = { ...defaultMetaTags,
@@ -147,7 +151,8 @@ app.get('/faq', async (req, res, next)=>{
req.brew = {
title : 'FAQ',
text : faqText,
renderer : 'V3'
renderer : 'V3',
theme : '5ePHB'
},
req.ogMeta = { ...defaultMetaTags,
@@ -265,9 +270,11 @@ app.get('/user/:username', async (req, res, next)=>{
});
//Edit Page
app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{
app.get('/edit/:id', asyncHandler(getBrew('edit')), asyncHandler(async(req, res, next)=>{
req.brew = req.brew.toObject ? req.brew.toObject() : req.brew;
req.userThemes = await(getUsersBrewThemes(req.account?.username));
req.ogMeta = { ...defaultMetaTags,
title : req.brew.title || 'Untitled Brew',
description : req.brew.description || 'No description.',
@@ -279,10 +286,10 @@ app.get('/edit/:id', asyncHandler(getBrew('edit')), (req, res, next)=>{
splitTextStyleAndMetadata(req.brew);
res.header('Cache-Control', 'no-cache, no-store'); //reload the latest saved brew when pressing back button, not the cached version before save.
return next();
});
}));
//New Page
app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
//New Page from ID
app.get('/new/:id', asyncHandler(getBrew('share')), asyncHandler(async(req, res, next)=>{
sanitizeBrew(req.brew, 'share');
splitTextStyleAndMetadata(req.brew);
const brew = {
@@ -292,17 +299,31 @@ app.get('/new/:id', asyncHandler(getBrew('share')), (req, res, next)=>{
style : req.brew.style,
renderer : req.brew.renderer,
theme : req.brew.theme,
tags : req.brew.tags
tags : req.brew.tags,
};
req.brew = _.defaults(brew, DEFAULT_BREW);
req.userThemes = await(getUsersBrewThemes(req.account?.username));
req.ogMeta = { ...defaultMetaTags,
title : 'New',
description : 'Start crafting your homebrew on the Homebrewery!'
};
return next();
});
}));
//New Page
app.get('/new', asyncHandler(async(req, res, next)=>{
req.userThemes = await(getUsersBrewThemes(req.account?.username));
req.ogMeta = { ...defaultMetaTags,
title : 'New',
description : 'Start crafting your homebrew on the Homebrewery!'
};
return next();
}));
//Share Page
app.get('/share/:id', asyncHandler(getBrew('share')), asyncHandler(async (req, res, next)=>{
@@ -418,7 +439,8 @@ const renderPage = async (req, res)=>{
enable_v3 : config.get('enable_v3'),
enable_themes : config.get('enable_themes'),
config : configuration,
ogMeta : req.ogMeta
ogMeta : req.ogMeta,
userThemes : req.userThemes
};
const title = req.brew ? req.brew.title : '';
const page = await templateFn('homebrew', title, props)

View File

@@ -8,9 +8,16 @@ const Markdown = require('../shared/naturalcrit/markdown.js');
const yaml = require('js-yaml');
const asyncHandler = require('express-async-handler');
const { nanoid } = require('nanoid');
const { splitTextStyleAndMetadata } = require('../shared/helpers.js');
const { DEFAULT_BREW, DEFAULT_BREW_LOAD } = require('./brewDefaults.js');
const Themes = require('../themes/themes.json');
const isStaticTheme = (renderer, themeName)=>{
return Themes[renderer]?.[themeName] !== undefined;
};
// const getTopBrews = (cb) => {
// HomebrewModel.find().sort({ views: -1 }).limit(5).exec(function(err, brews) {
// cb(brews);
@@ -37,6 +44,43 @@ const api = {
}
return { id, googleId };
},
//Get array of any of this user's brews tagged with `meta:theme`
getUsersBrewThemes : async (username)=>{
if(!username)
return {};
const fields = [
'title',
'tags',
'shareId',
'thumbnail',
'textBin',
'text',
'authors',
'renderer'
];
const userThemes = {};
const brews = await HomebrewModel.getByUser(username, true, fields, { tags: { $in: ['meta:theme', 'meta:Theme'] } });
if(brews) {
for (const brew of brews) {
userThemes[brew.renderer] ??= {};
userThemes[brew.renderer][brew.shareId] = {
name : brew.title,
renderer : brew.renderer,
baseTheme : brew.theme,
baseSnippets : false,
author : brew.authors[0],
path : brew.shareId,
thumbnail : brew.thumbnail || '/assets/naturalCritLogoWhite.svg'
};
}
}
return userThemes;
},
getBrew : (accessType, stubOnly = false)=>{
// Create middleware with the accessType passed in as part of the scope
return async (req, res, next)=>{
@@ -142,7 +186,7 @@ const api = {
return modified;
},
excludeStubProps : (brew)=>{
const propsToExclude = ['text', 'textBin', 'renderer', 'pageCount'];
const propsToExclude = ['text', 'textBin'];
for (const prop of propsToExclude) {
brew[prop] = undefined;
}
@@ -209,6 +253,58 @@ const api = {
res.status(200).send(saved);
},
getThemeBundle : async(req, res)=>{
/* getThemeBundle: Collects the theme and all parent themes
returns an object containing an array of css, and an array of snippets, in render order
req.params.id : The shareId ( User theme ) or name ( static theme )
req.params.renderer : The Markdown renderer used for this theme */
req.params.renderer = _.upperFirst(req.params.renderer);
let currentTheme;
const completeStyles = [];
const completeSnippets = [];
while (req.params.id) {
//=== User Themes ===//
if(!isStaticTheme(req.params.renderer, req.params.id)) {
await api.getBrew('share')(req, res, ()=>{})
.catch((err)=>{
if(err.HBErrorCode == '05')
err = { ...err, name: 'ThemeLoad Error', message: 'Theme Not Found', HBErrorCode: '09' };
throw err;
});
currentTheme = req.brew;
splitTextStyleAndMetadata(currentTheme);
// If there is anything in the snippets or style members, append them to the appropriate array
if(currentTheme?.snippets) completeSnippets.push(JSON.parse(currentTheme.snippets));
if(currentTheme?.style) completeStyles.push(`/* From Brew: ${req.protocol}://${req.get('host')}/share/${req.params.id} */\n\n${currentTheme.style}`);
req.params.id = currentTheme.theme;
req.params.renderer = currentTheme.renderer;
}
//=== Static Themes ===//
else {
const localSnippets = `${req.params.renderer}_${req.params.id}`; // Just log the name for loading on client
const localStyle = `@import url(\"/themes/${req.params.renderer}/${req.params.id}/style.css\");`;
completeSnippets.push(localSnippets);
completeStyles.push(`/* From Theme ${req.params.id} */\n\n${localStyle}`);
req.params.id = Themes[req.params.renderer][req.params.id].baseTheme;
}
}
const returnObj = {
// Reverse the order of the arrays so they are listed oldest parent to youngest child.
styles : completeStyles.reverse(),
snippets : completeSnippets.reverse()
};
res.setHeader('Content-Type', 'application/json');
return res.status(200).send(returnObj);
},
updateBrew : async (req, res)=>{
// Initialize brew from request and body, destructure query params, and set the initial value for the after-save method
const brewFromClient = api.excludePropsFromUpdate(req.body);
@@ -369,5 +465,6 @@ router.put('/api/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api
router.put('/api/update/:id', asyncHandler(api.getBrew('edit', true)), asyncHandler(api.updateBrew));
router.delete('/api/:id', asyncHandler(api.deleteBrew));
router.get('/api/remove/:id', asyncHandler(api.deleteBrew));
router.get('/api/theme/:renderer/:id', asyncHandler(api.getThemeBundle));
module.exports = api;

View File

@@ -14,6 +14,9 @@ describe('Tests for api', ()=>{
let saved;
beforeEach(()=>{
jest.resetModules();
jest.restoreAllMocks();
saved = undefined;
saveFunc = jest.fn(async function() {
saved = { ...this, _id: '1' };
@@ -45,8 +48,9 @@ describe('Tests for api', ()=>{
model.mockImplementation((brew)=>modelBrew(brew));
res = {
status : jest.fn(()=>res),
send : jest.fn(()=>{})
status : jest.fn(()=>res),
send : jest.fn(()=>{}),
setHeader : jest.fn(()=>{})
};
api = require('./homebrew.api');
@@ -81,10 +85,6 @@ describe('Tests for api', ()=>{
};
});
afterEach(()=>{
jest.restoreAllMocks();
});
describe('getId', ()=>{
it('should return only id if google id is not present', ()=>{
const { id, googleId } = api.getId({
@@ -408,8 +408,8 @@ brew`);
expect(sent).not.toEqual(googleBrew);
expect(result.text).toBeUndefined();
expect(result.textBin).toBeUndefined();
expect(result.renderer).toBeUndefined();
expect(result.pageCount).toBeUndefined();
expect(result.renderer).toBe('v3');
expect(result.pageCount).toBe(1);
});
});
@@ -540,9 +540,9 @@ brew`);
description : '',
editId : expect.any(String),
gDrive : false,
pageCount : undefined,
pageCount : 1,
published : false,
renderer : undefined,
renderer : 'V3',
lang : 'en',
shareId : expect.any(String),
googleId : expect.any(String),
@@ -581,6 +581,121 @@ brew`);
});
});
describe('Theme bundle', ()=>{
it('should return Theme Bundle for a User Theme', async ()=>{
const brews = {
userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: null, shareId: 'userThemeAID', style: 'User Theme A Style' }
};
const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId]));
const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' };
await api.getThemeBundle(req, res);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.send).toHaveBeenCalledWith({
styles : ['/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style'],
snippets : []
});
});
it('should return Theme Bundle for nested User Themes', async ()=>{
const brews = {
userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: 'userThemeBID', shareId: 'userThemeAID', style: 'User Theme A Style' },
userThemeBID : { title: 'User Theme B', renderer: 'V3', theme: 'userThemeCID', shareId: 'userThemeBID', style: 'User Theme B Style' },
userThemeCID : { title: 'User Theme C', renderer: 'V3', theme: null, shareId: 'userThemeCID', style: 'User Theme C Style' }
};
const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId]));
const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' };
await api.getThemeBundle(req, res);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.send).toHaveBeenCalledWith({
styles : [
'/* From Brew: https://localhost/share/userThemeCID */\n\nUser Theme C Style',
'/* From Brew: https://localhost/share/userThemeBID */\n\nUser Theme B Style',
'/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style'
],
snippets : []
});
});
it('should return Theme Bundle for a Static Theme', async ()=>{
const req = { params: { renderer: 'V3', id: '5ePHB' }, get: ()=>{ return 'localhost'; }, protocol: 'https' };
await api.getThemeBundle(req, res);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.send).toHaveBeenCalledWith({
styles : [
`/* From Theme Blank */\n\n@import url("/themes/V3/Blank/style.css");`,
`/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");`
],
snippets : [
'V3_Blank',
'V3_5ePHB'
]
});
});
it('should return Theme Bundle for nested User and Static Themes together', async ()=>{
const brews = {
userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: 'userThemeBID', shareId: 'userThemeAID', style: 'User Theme A Style' },
userThemeBID : { title: 'User Theme B', renderer: 'V3', theme: 'userThemeCID', shareId: 'userThemeBID', style: 'User Theme B Style' },
userThemeCID : { title: 'User Theme C', renderer: 'V3', theme: '5eDMG', shareId: 'userThemeCID', style: 'User Theme C Style' }
};
const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId]));
const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' };
await api.getThemeBundle(req, res);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.send).toHaveBeenCalledWith({
styles : [
`/* From Theme Blank */\n\n@import url("/themes/V3/Blank/style.css");`,
`/* From Theme 5ePHB */\n\n@import url("/themes/V3/5ePHB/style.css");`,
`/* From Theme 5eDMG */\n\n@import url("/themes/V3/5eDMG/style.css");`,
'/* From Brew: https://localhost/share/userThemeCID */\n\nUser Theme C Style',
'/* From Brew: https://localhost/share/userThemeBID */\n\nUser Theme B Style',
'/* From Brew: https://localhost/share/userThemeAID */\n\nUser Theme A Style'
],
snippets : [
'V3_Blank',
'V3_5ePHB',
'V3_5eDMG'
]
});
});
it('should fail for an invalid Theme in the chain', async()=>{
const brews = {
userThemeAID : { title: 'User Theme A', renderer: 'V3', theme: 'missingTheme', shareId: 'userThemeAID', style: 'User Theme A Style' },
};
const toBrewPromise = (brew)=>new Promise((res)=>res({ toObject: ()=>brew }));
model.get = jest.fn((getParams)=>toBrewPromise(brews[getParams.shareId]));
const req = { params: { renderer: 'V3', id: 'userThemeAID' }, get: ()=>{ return 'localhost'; }, protocol: 'https' };
let err;
await api.getThemeBundle(req, res)
.catch((e)=>err = e);
expect(err).toEqual({
HBErrorCode : '09',
accessType : 'share',
brewId : 'missingTheme',
message : 'Theme Not Found',
name : 'ThemeLoad Error',
status : 404 });
});
});
describe('deleteBrew', ()=>{
it('should handle case where fetching the brew returns an error', async ()=>{
api.getBrew = jest.fn(()=>async ()=>{ throw { message: 'err', HBErrorCode: '02' }; });

View File

@@ -50,8 +50,8 @@ HomebrewSchema.statics.get = async function(query, fields=null){
return brew;
};
HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, fields=null){
const query = { authors: username, published: true };
HomebrewSchema.statics.getByUser = async function(username, allowAccess=false, fields=null, filter=null){
const query = { authors: username, published: true, ...filter };
if(allowAccess){
delete query.published;
}

View File

@@ -1,5 +1,6 @@
const _ = require('lodash');
const yaml = require('js-yaml');
const request = require('../client/homebrew/utils/request-middleware.js');
const splitTextStyleAndMetadata = (brew)=>{
brew.text = brew.text.replaceAll('\r\n', '\n');
@@ -15,6 +16,11 @@ const splitTextStyleAndMetadata = (brew)=>{
brew.style = brew.text.slice(7, index - 1);
brew.text = brew.text.slice(index + 5);
}
if(brew.text.startsWith('```snippets')) {
const index = brew.text.indexOf('```\n\n');
brew.snippets = brew.text.slice(11, index - 1);
brew.text = brew.text.slice(index + 5);
}
};
const printCurrentBrew = ()=>{
@@ -28,7 +34,24 @@ const printCurrentBrew = ()=>{
}
};
const fetchThemeBundle = async (obj, renderer, theme)=>{
const res = await request
.get(`/api/theme/${renderer}/${theme}`)
.catch((err)=>{
obj.setState({ error: err });
});
if(!res) return;
const themeBundle = res.body;
themeBundle.joinedStyles = themeBundle.styles.map((style)=>`<style>${style}</style>`).join('\n\n');
obj.setState((prevState)=>({
...prevState,
themeBundle : themeBundle
}));
};
module.exports = {
splitTextStyleAndMetadata,
printCurrentBrew
printCurrentBrew,
fetchThemeBundle,
};

View File

@@ -39,8 +39,10 @@ if(typeof window !== 'undefined'){
//Autocompletion
require('codemirror/addon/hint/show-hint.js');
const foldCode = require('./fold-code');
foldCode.registerHomebreweryHelper(CodeMirror);
const foldPagesCode = require('./fold-pages');
foldPagesCode.registerHomebreweryHelper(CodeMirror);
const foldCSSCode = require('./fold-css');
foldCSSCode.registerHomebreweryHelper(CodeMirror);
}
const CodeEditor = createClass({
@@ -411,11 +413,11 @@ const CodeEditor = createClass({
foldOptions : function(cm){
return {
scanUp : true,
rangeFinder : CodeMirror.fold.homebrewery,
rangeFinder : this.props.language === 'css' ? CodeMirror.fold.homebrewerycss : CodeMirror.fold.homebrewery,
widget : (from, to)=>{
let text = '';
let currentLine = from.line;
const maxLength = 50;
let maxLength = 50;
let foldPreviewText = '';
while (currentLine <= to.line && text.length <= maxLength) {
@@ -430,10 +432,15 @@ const CodeEditor = createClass({
}
}
text = foldPreviewText || `Lines ${from.line+1}-${to.line+1}`;
text = text.replace('{', '').trim();
// Truncate data URLs at `data:`
const startOfData = text.indexOf('data:');
if(startOfData > 0)
maxLength = Math.min(startOfData + 5, maxLength);
text = text.trim();
if(text.length > maxLength)
text = `${text.substr(0, maxLength)}...`;
text = `${text.slice(0, maxLength)}...`;
return `\u21A4 ${text} \u21A6`;
}
@@ -450,3 +457,4 @@ const CodeEditor = createClass({
});
module.exports = CodeEditor;

View File

@@ -0,0 +1,44 @@
module.exports = {
registerHomebreweryHelper : function(CodeMirror) {
CodeMirror.registerHelper('fold', 'homebrewerycss', function(cm, start) {
// BRACE FOLDING
const startMatcher = /\{[ \t]*$/;
const endMatcher = /\}[ \t]*$/;
const activeLine = cm.getLine(start.line);
if(activeLine.match(startMatcher)) {
const lastLineNo = cm.lastLine();
let end = start.line + 1;
let braceCount = 1;
while (end < lastLineNo) {
const curLine = cm.getLine(end);
if(curLine.match(startMatcher)) braceCount++;
if(curLine.match(endMatcher)) braceCount--;
if(braceCount == 0) break;
++end;
}
return {
from : CodeMirror.Pos(start.line, 0),
to : CodeMirror.Pos(end, cm.getLine(end).length)
};
}
// @import and data-url folding
const importMatcher = /^@import.*?;/;
const dataURLMatcher = /url\(.*?data\:.*\)/;
if(activeLine.match(importMatcher) || activeLine.match(dataURLMatcher)) {
return {
from : CodeMirror.Pos(start.line, 0),
to : CodeMirror.Pos(start.line, activeLine.length)
};
}
return null;
});
}
};

View File

@@ -28,17 +28,18 @@ const mathParser = new MathParser({
round : true,
floor : true,
ceil : true,
abs : true,
sin : false, cos : false, tan : false, asin : false, acos : false,
atan : false, sinh : false, cosh : false, tanh : false, asinh : false,
acosh : false, atanh : false, sqrt : false, cbrt : false, log : false,
log2 : false, ln : false, lg : false, log10 : false, expm1 : false,
log1p : false, abs : false, trunc : false, join : false, sum : false,
log1p : false, trunc : false, join : false, sum : false, indexOf : false,
'-' : false, '+' : false, exp : false, not : false, length : false,
'!' : false, sign : false, random : false, fac : false, min : false,
max : false, hypot : false, pyt : false, pow : false, atan2 : false,
'if' : false, gamma : false, roundTo : false, map : false, fold : false,
filter : false, indexOf : false,
filter : false,
remainder : false, factorial : false,
comparison : false, concatenate : false,
@@ -46,6 +47,16 @@ const mathParser = new MathParser({
array : false, fndef : false
}
});
// Add sign function
mathParser.functions.sign = function (a) {
if(a >= 0) return '+';
return '-';
};
// Add signed function
mathParser.functions.signed = function (a) {
if(a >= 0) return `+${a}`;
return `${a}`;
};
//Processes the markdown within an HTML block if it's just a class-wrapper
renderer.html = function (html) {
@@ -441,7 +452,7 @@ const replaceVar = function(input, hoist=false, allowUnresolved=false) {
const label = match[2];
//v=====--------------------< HANDLE MATH >-------------------=====v//
const mathRegex = /[a-z]+\(|[+\-*/^()]/g;
const mathRegex = /[a-z]+\(|[+\-*/^(),]/g;
const matches = label.split(mathRegex);
const mathVars = matches.filter((match)=>isNaN(match))?.map((s)=>s.trim()); // Capture any variable names
@@ -451,7 +462,7 @@ const replaceVar = function(input, hoist=false, allowUnresolved=false) {
mathVars?.forEach((variable)=>{
const foundVar = lookupVar(variable, globalPageNumber, hoist);
if(foundVar && foundVar.resolved && foundVar.content && !isNaN(foundVar.content)) // Only subsitute math values if fully resolved, not empty strings, and numbers
replacedLabel = replacedLabel.replaceAll(variable, foundVar.content);
replacedLabel = replacedLabel.replaceAll(new RegExp(`(?<!\\w)(${variable})(?!\\w)`, 'g'), foundVar.content);
});
try {

View File

@@ -1,5 +1,5 @@
const stylelint = require('stylelint');
const { isNumber } = require('stylelint/lib/utils/validateTypes');
const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs');
const { report, ruleMessages, validateOptions } = stylelint.utils;
const ruleName = 'naturalcrit/declaration-block-multi-line-min-declarations';

View File

@@ -1,5 +1,5 @@
const stylelint = require('stylelint');
const { isNumber } = require('stylelint/lib/utils/validateTypes');
const { isNumber } = require('stylelint/lib/utils/validateTypes.cjs');
const { report, ruleMessages, validateOptions } = stylelint.utils;
const ruleName = 'naturalcrit/declaration-colon-min-space-before';

View File

@@ -370,4 +370,36 @@ describe('Cross-page variables', ()=>{
const rendered = renderAllPages([source0, source1]).join('\n\\page\n').trimReturns();
expect(rendered, `Input:\n${[source0, source1].join('\n\\page\n')}`, { showPrefix: false }).toBe('<p>two</p><p>one</p>\\page<p>two</p>');
});
});
describe('Math function parameter handling', ()=>{
it('allows variables in single-parameter functions', function() {
const source = '[var]:4.1\n\n$[floor(var)]';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>4</p>`);
});
it('allows one variable and a number in two-parameter functions', function() {
const source = '[var]:4\n\n$[min(1,var)]';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>1</p>`);
});
it('allows two variables in two-parameter functions', function() {
const source = '[var1]:4\n\n[var2]:8\n\n$[min(var1,var2)]';
const rendered = Markdown.render(source).trimReturns();
expect(rendered, `Input:\n${source}`, { showPrefix: false }).toBe(`<p>4</p>`);
});
});
describe('Variable names that are subsets of other names', ()=>{
it('do not conflict with function names', function() {
const source = `[a]: -1\n\n$[abs(a)]`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered).toBe('<p>1</p>');
});
it('do not conflict with other variable names', function() {
const source = `[ab]: 2\n\n[aba]: 8\n\n[ba]: 4\n\n$[ab + aba + ba]`;
const rendered = Markdown.render(source).trimReturns();
expect(rendered).toBe('<p>14</p>');
});
});

View File

@@ -1,6 +1,6 @@
{
"name" : "5e PHB",
"renderer" : "V3",
"baseTheme" : false,
"baseTheme" : "Blank",
"baseSnippets" : false
}

View File

@@ -349,7 +349,7 @@ module.exports = [
/* Ink Friendly */
*:is(.page,.monster,.note,.descriptive) {
background : white !important;
filter : drop-shadow(0px 0px 3px #888) !important;
box-shadow : 1px 4px 14px #888 !important;
}
.page img {

View File

@@ -35,7 +35,7 @@ const getTOC = (pages)=>{
const ToCExclude = getComputedStyle(heading).getPropertyValue('--TOC');
if(ToCExclude != 'exclude') {
recursiveAdd(heading.innerText.trim(), onPage, headerDepth.indexOf(heading.tagName), res);
recursiveAdd(heading.textContent.trim(), onPage, headerDepth.indexOf(heading.tagName), res);
}
});
return res;

View File

@@ -382,6 +382,14 @@
.useColumns(0.96, @fillMode: balance);
}
//only for IOS devices
@supports (-webkit-touch-callout: none) {
.page .monster.frame {
background-repeat : no-repeat;
background-size : cover;
}
}
// *****************************
// * FOOTER
// *****************************/
@@ -459,6 +467,7 @@
margin-left : 1.5em;
}
}
// *****************************
// * SPELL LIST
// *****************************/
@@ -883,6 +892,9 @@ h6,
.useColumns(0.96, @fillMode: balance);
}
}
.toc.wide li {
break-inside: auto;
}
}
// *****************************
@@ -907,6 +919,10 @@ h6,
.page h1 + * { margin-top : 0; }
.page .descriptive.wide + * {
margin-top: 0;
}
//*****************************
// * RUNE TABLE
// *****************************/

View File

@@ -1,6 +1,6 @@
{
"name" : "Journal",
"renderer" : "V3",
"baseTheme" : false,
"baseTheme" : "Blank",
"baseSnippets" : "5ePHB"
}

View File

@@ -7,6 +7,7 @@
@noteBorderImage : url('/assets/noteBorder.png');
@descriptiveBoxImage : url('/assets/descriptiveBorder.png');
@monsterBlockBackground : url('/assets/parchmentBackgroundGrayscale.jpg');
@monsterBlockOverlay : url('/assets/parchmentBackgroundOverlayed.jpg');
@monsterBorderImage : url('/assets/monsterBorderFancy.png');
@codeBorderImage : url('/assets/codeBorder.png');
@classTableDecoration : url('/assets/classTableDecoration.png');

View File

@@ -18,7 +18,7 @@
"5ePHB": {
"name": "5e PHB",
"renderer": "V3",
"baseTheme": false,
"baseTheme": "Blank",
"baseSnippets": false,
"path": "5ePHB"
},
@@ -32,7 +32,7 @@
"Journal": {
"name": "Journal",
"renderer": "V3",
"baseTheme": false,
"baseTheme": "Blank",
"baseSnippets": "5ePHB",
"path": "Journal"
}