Compare commits

...

174 Commits

Author SHA1 Message Date
Dan Brown
c4d2f741ab
Merge fd71c57aab into e65655594f 2024-09-30 19:20:12 +00:00
Dan Brown
fd71c57aab New translations common.php (Spanish) 2024-09-30 20:20:09 +01:00
Dan Brown
0be82b4f96 New translations common.php (German Informal) 2024-09-30 18:12:19 +01:00
Dan Brown
f83ec3a8b4 New translations common.php (Norwegian Bokmal) 2024-09-30 18:12:18 +01:00
Dan Brown
d00e2ed37c New translations common.php (Uzbek) 2024-09-30 18:12:17 +01:00
Dan Brown
1a963974d8 New translations common.php (Bosnian) 2024-09-30 18:12:16 +01:00
Dan Brown
7b9c67edc4 New translations common.php (Latvian) 2024-09-30 18:12:15 +01:00
Dan Brown
5655c93ea1 New translations common.php (Estonian) 2024-09-30 18:12:14 +01:00
Dan Brown
1db399d12d New translations common.php (Norwegian Nynorsk) 2024-09-30 18:12:13 +01:00
Dan Brown
29862b5323 New translations common.php (Croatian) 2024-09-30 18:12:12 +01:00
Dan Brown
409aa2541d New translations common.php (Spanish, Argentina) 2024-09-30 18:12:11 +01:00
Dan Brown
1a94e6033f New translations common.php (Persian) 2024-09-30 18:12:10 +01:00
Dan Brown
9162b1af83 New translations common.php (Indonesian) 2024-09-30 18:12:08 +01:00
Dan Brown
8ec45611e7 New translations common.php (Portuguese, Brazilian) 2024-09-30 18:12:07 +01:00
Dan Brown
ce03fbcb62 New translations common.php (Vietnamese) 2024-09-30 18:12:06 +01:00
Dan Brown
4a22febc70 New translations common.php (Chinese Traditional) 2024-09-30 18:12:05 +01:00
Dan Brown
b720b424da New translations common.php (Chinese Simplified) 2024-09-30 18:12:04 +01:00
Dan Brown
2a2e472dc4 New translations common.php (Ukrainian) 2024-09-30 18:12:04 +01:00
Dan Brown
c576aa3369 New translations common.php (Turkish) 2024-09-30 18:12:02 +01:00
Dan Brown
7affeea418 New translations common.php (Swedish) 2024-09-30 18:12:01 +01:00
Dan Brown
53f24492c8 New translations common.php (Serbian (Cyrillic)) 2024-09-30 18:12:00 +01:00
Dan Brown
4fade907f3 New translations common.php (Albanian) 2024-09-30 18:11:59 +01:00
Dan Brown
3af17dff7b New translations common.php (Slovenian) 2024-09-30 18:11:59 +01:00
Dan Brown
5aeee6f802 New translations common.php (Slovak) 2024-09-30 18:11:58 +01:00
Dan Brown
2632023981 New translations common.php (Russian) 2024-09-30 18:11:56 +01:00
Dan Brown
42e1f1a308 New translations common.php (Portuguese) 2024-09-30 18:11:56 +01:00
Dan Brown
6d875b7bb2 New translations common.php (Polish) 2024-09-30 18:11:54 +01:00
Dan Brown
1ef8faecac New translations common.php (Dutch) 2024-09-30 18:11:53 +01:00
Dan Brown
0ec731507e New translations common.php (Lithuanian) 2024-09-30 18:11:52 +01:00
Dan Brown
1e74f6f57d New translations common.php (Korean) 2024-09-30 18:11:51 +01:00
Dan Brown
68738e69a6 New translations common.php (Georgian) 2024-09-30 18:11:50 +01:00
Dan Brown
de53bbc13e New translations common.php (Japanese) 2024-09-30 18:11:49 +01:00
Dan Brown
b9352ef921 New translations common.php (Italian) 2024-09-30 18:11:48 +01:00
Dan Brown
9c10fdd431 New translations common.php (Hungarian) 2024-09-30 18:11:47 +01:00
Dan Brown
12bee9ca43 New translations common.php (Hebrew) 2024-09-30 18:11:46 +01:00
Dan Brown
f3e998389a New translations common.php (Finnish) 2024-09-30 18:11:45 +01:00
Dan Brown
972c44894a New translations common.php (Basque) 2024-09-30 18:11:44 +01:00
Dan Brown
8007008a78 New translations common.php (Greek) 2024-09-30 18:11:43 +01:00
Dan Brown
6e20d52c09 New translations common.php (German) 2024-09-30 18:11:42 +01:00
Dan Brown
ab84536c76 New translations common.php (Danish) 2024-09-30 18:11:41 +01:00
Dan Brown
5764425a89 New translations common.php (Czech) 2024-09-30 18:11:40 +01:00
Dan Brown
8d86682616 New translations common.php (Catalan) 2024-09-30 18:11:39 +01:00
Dan Brown
bb913d5809 New translations common.php (Bulgarian) 2024-09-30 18:11:38 +01:00
Dan Brown
55b963735a New translations common.php (Arabic) 2024-09-30 18:11:37 +01:00
Dan Brown
8c41fc086b New translations common.php (Spanish) 2024-09-30 18:11:36 +01:00
Dan Brown
df03d96800 New translations common.php (French) 2024-09-30 18:11:35 +01:00
Dan Brown
9ffd4fdac8 New translations common.php (Romanian) 2024-09-30 18:11:34 +01:00
Dan Brown
0b4488b62f New translations common.php (Welsh) 2024-09-30 18:11:33 +01:00
Dan Brown
e65655594f
Merge branch 'feature/opensearch' into development 2024-09-30 17:21:51 +01:00
Dan Brown
514db60617
Tests: Categorised up meta tests
Extracted robots.txt tests into its own file to fit into new folder.
Also tweaked open search tests a tad to specifically check long app
names.
2024-09-30 17:07:53 +01:00
Dan Brown
8bc6e75319
Code Blocks: Added SAS and R language options
For #5206
2024-09-30 16:47:55 +01:00
Maximilian Walter
2f74cfb42c
Add test for OpenSearch endpoint 2024-09-30 17:45:20 +02:00
Maximilian Walter
1302e3c959
Add missing XML declaration to OpenSearch endpoint 2024-09-30 17:45:20 +02:00
Maximilian Walter
a5b031f906
Translatable description for OpenSearch XML 2024-09-30 17:45:20 +02:00
Dan Brown
71e46c8c8e New translations activities.php (Danish) 2024-09-30 16:17:10 +01:00
Dan Brown
f583354748
Maintenance: Removed stray dd from last commit 2024-09-29 16:50:48 +01:00
Dan Brown
d12e8ec923
Users: Improved user response for failed invite sending
Added specific handling to show relevant error message when user
creation fails due to invite sending errors, while also returning user
to the form with previous input.
Includes test to cover.

For #5195
2024-09-29 16:41:18 +01:00
Dan Brown
89f84c9a95
Pages: Updated editor field to always be set
- Migration for setting on existing pages
- Added test to cover simple new page scenario

For #5117
2024-09-29 14:36:41 +01:00
Dan Brown
6103a22feb
Exports: Made pdf command timeout configurable
Added test to cover.
For #5119
2024-09-27 16:33:58 +01:00
Dan Brown
42264f402d
CSS: Fixed floating search icon on mobile
Also updated styles to use logical elements instead of conditional rules
for altered search boxes.
Related to #2504
2024-09-27 16:02:13 +01:00
Dan Brown
abda9bc00a
PHP Dependancies: Updated packages pending major version changes
Closes #5222
2024-09-27 14:21:12 +01:00
Dan Brown
eec639d84e
Maintenance: Fixed js lint and SCSS build warnings 2024-09-27 13:57:39 +01:00
Dan Brown
56b9107c6b
Dependancies: Updated php & JS deps, updated license lists
Fixed issue now picked up by newer TS version
2024-09-27 12:29:19 +01:00
Dan Brown
b35b62d59f
Merge branch 'lexical' into development 2024-09-27 12:04:01 +01:00
Dan Brown
1b9310e766
Meta: Added lexical licensing info and added TS/JS CI testing 2024-09-27 10:45:48 +01:00
Dan Brown
a62d8381be
Lexical: Updated toolbar & text node exporting
- Updated toolbar to match existing editor, including dynamic RTL/LTR
  controls.
- Updated text node handling to not include spans and extra classes when
  not needed. Added & update tests to cover.
2024-09-23 17:36:16 +01:00
Dan Brown
8b32e6c15a
Page Editors: Added switching/options for new lexical editor 2024-09-22 20:06:55 +01:00
Dan Brown
c8ccb2bac7
Lexical: Range of fixes
- Prevented ui shortcuts running in editor
- Added form modal closing on submit
- Fixed ability to escape lists via enter on empty last item
2024-09-22 16:15:02 +01:00
Dan Brown
ef3de1050f
Lexical: Added UI translation support 2024-09-22 12:29:06 +01:00
Dan Brown
2add15bd72
Lexical: Added direction support to extra blocks
Also removed duplicated dir functionality that remained in core.
2024-09-22 12:07:24 +01:00
Dan Brown
e6edd9340e
Lexical: Added alignment detoggle, fixed inital focus area 2024-09-21 17:02:54 +01:00
Dan Brown
654a7a5d03
Lexical: Removed reconciler level direction handling
- Updated tests to consider changes
2024-09-21 13:00:16 +01:00
Dan Brown
dba8ab947f
Lexical: Finished conversion/update of test files 2024-09-20 15:31:19 +01:00
Dan Brown
787e06e3d8
Lexical: Adapted a range of further existing tests 2024-09-20 13:05:29 +01:00
Dan Brown
ccd486f2a9
Lexical: Got a range of Editor tests working 2024-09-18 17:31:51 +01:00
Dan Brown
22d078b47f
Lexical: Imported core lexical libs
Imported at 0.17.1, Modified to work in-app.
Added & configured test dependancies.
Tests need to be altered to avoid using non-included deps including
react dependancies.
2024-09-18 13:43:39 +01:00
Dan Brown
03490d6597
Lexical: Added RTL/LTR actions
Kinda useless though due to Lexical reconciler :(
2024-09-16 12:29:46 +01:00
Dan Brown
5f46d71af0
Lexical: Fixed a range of issues in RTL mode 2024-09-15 16:10:46 +01:00
Maximilian Walter
4f890c431c
Limit short-name for OpenSearch XML to 16 characters
The specification does not allow more than 16 characters.
2024-09-14 15:31:56 +02:00
Maximilian Walter
c110a97d8a
Remove unofficial method-attribute from OpenSearch-XML 2024-09-14 15:24:42 +02:00
Dan Brown
6872eb802c
Lexical: Altered keyboard handling to indicant handled state 2024-09-13 16:05:55 +01:00
Dan Brown
662110c269
Lexical: Custom list nesting support
Added list nesting support to allow li > ul style nesting which lexical
didn't do by default.
Adds tab handling for inset/outset controls.
Will be a range of edge-case bugs to squash during testing.
2024-09-13 15:50:42 +01:00
Dan Brown
5083188ed8
Lexical: Added block indenting capability
Needed a custom implementation due to hardcoded defaults for Lexical
default indenting.
2024-09-10 15:55:46 +01:00
Dan Brown
2036438203
Lexical: Added single node enter handling
Also updated media to be an inline element to align with old editor
behaviour.
2024-09-10 12:14:26 +01:00
Maximilian Walter
476c2be5a6
Add XML for OpenSearch 2024-09-09 22:54:33 +02:00
Dan Brown
ced66f1671
Lexical: Added single node backspace/delete support 2024-09-09 18:33:54 +01:00
Dan Brown
fb49371c6b
Lexical: Refined editor UI
- Cleaned up dropdown lists to look integrated
- Added icons for color picker clear and menu list items
2024-09-09 14:06:41 +01:00
Dan Brown
fd07aa0f05
Lexical: Further fixes
- Improved node resizer positioning to be more accurate
- Fixed drop handling not running within editor margin space
- Made media dom update smarter to reduce reloads
- Fixed media alignment, broken due to added wrapper
2024-09-09 12:28:01 +01:00
Dan Brown
16518a4f89
Lexical: Range of bug fixes, Updated lexical version
- Updated selection change detection to be more accurate
- Added UI refresh for extra actions
- Fixed remove link deleting contents
2024-09-08 15:54:59 +01:00
Dan Brown
bed2c29a33
Lexical: Added media resize support via drag handles 2024-09-08 13:37:13 +01:00
Dan Brown
e5b6d28bca
Lexical: Revamped image node resize method
Changed from using a decorator to using a helper that watches for image
selections to then display a resize helper.
Also changes resizer to use a ghost and apply changes on end instead of
continuosly during resize.
2024-09-07 18:39:58 +01:00
Dan Brown
1c9afcb84e
Lexical: Added some level of img/media alignment 2024-09-06 14:07:10 +01:00
Dan Brown
1ebb0f8c93
Lexical: Added table column cut/copy/paste support 2024-08-22 13:28:30 +01:00
Dan Brown
8a13a9df80
Lexical: Improved table row copy/paste
Added safeguarding/matching of source/target sizes to prevent broken
tables.
2024-08-22 10:08:08 +01:00
Dan Brown
ddf5f2543c
Lexical: Added drop/paste image handling 2024-08-21 12:59:45 +01:00
Dan Brown
dbb2fe3e59
Lexical: Finished off baseline shortcut implementation 2024-08-20 14:54:53 +01:00
Dan Brown
aa1fac62d5
Lexical: Started adding editor shortcuts 2024-08-20 13:07:33 +01:00
Dan Brown
111a313d51
Lexical: Added custom alignment handling for blocks
To align with pre-existing use of alignment classes.
2024-08-18 16:51:08 +01:00
Dan Brown
0039f893cc
Lexical: Integrated diagram manager, added menu split button 2024-08-17 10:48:34 +01:00
Dan Brown
ad6b26ba97
Lexical: Added basic URL field header option list
May show bad option label names on chrome/safari.
This was an easy first pass without loads of extra custom UI since we're
using native datalists.
2024-08-16 12:29:40 +01:00
Dan Brown
1ef4044419
Lexical: Connected link selector to link form 2024-08-16 11:22:12 +01:00
Dan Brown
accf2565a0
Lexical: Integrated image manager to image button/form 2024-08-13 19:36:18 +01:00
Dan Brown
ec965f28c0
Lexical: Added id support for all main block types 2024-08-11 16:08:51 +01:00
Dan Brown
ebf95f637a
Lexical: Wired table properties, and other buttons 2024-08-10 13:14:55 +01:00
Dan Brown
abbfd42a6c
Lexical: Kinda made row copy/paste work 2024-08-09 21:58:45 +01:00
Dan Brown
db4208a7eb
Lexical: Linked row properties form up 2024-08-09 12:42:04 +01:00
Dan Brown
da54e1d87c
Lexical: Added cell width fetching, Created custom row node 2024-08-09 11:24:25 +01:00
Dan Brown
e8532ef4de
Lexical: Added merge cell logic 2024-08-07 20:32:54 +01:00
Dan Brown
fcc1c2968d
Lexical: Added table cell node import logic 2024-08-06 09:36:37 +01:00
Dan Brown
b3d3b14f79
Lexical: Finished off core cell properties functionality 2024-08-05 18:49:17 +01:00
Dan Brown
8939f310db
Lexical: Started linking up cell properties form 2024-08-05 15:08:52 +01:00
Dan Brown
efec752985
Lexical: Split helpers to utils, refactored files 2024-08-03 18:14:01 +01:00
Dan Brown
e94ad78ea7
Lexical: Completed out table menu elements, logic pending 2024-08-03 18:01:54 +01:00
Dan Brown
a27a325af7
Lexical: Started on table actions
Started building table cell form/actions
2024-08-02 15:28:54 +01:00
Dan Brown
6b06d490c5
Lexical: Started table menu options
Updated UI elements to handle new scenarios needed in more complex table
menu
2024-08-02 11:16:54 +01:00
Dan Brown
13f8f39dd5
Lexical: Updated task list to use/support old format 2024-07-30 14:42:19 +01:00
Dan Brown
fe05cff64f
Lexical: Linked up change/draft management 2024-07-29 21:43:20 +01:00
Dan Brown
d86837ac07
Lexical: Got working with attachment insert/drop 2024-07-29 21:14:42 +01:00
Dan Brown
9a7edc6e52
Lexical: Started drop handling, handled templates 2024-07-29 15:27:41 +01:00
Dan Brown
ce8c9dd079
Lexical: Added form complex/tab ui support 2024-07-28 12:48:58 +01:00
Dan Brown
c8f6b7e0d6
Lexical: Got media node core work & form done 2024-07-27 17:25:30 +01:00
Dan Brown
f284d31861
Lexical: Started media node support 2024-07-25 16:25:08 +01:00
Dan Brown
76b0d2d5d8
Lexical: Added common events support 2024-07-23 15:35:18 +01:00
Dan Brown
2cab778f19
Lexical: Improved table resize bars
Added scoll & page resize handling.
Added cropping/limiting to edit area.
2024-07-23 12:45:58 +01:00
Dan Brown
b618287585
Lexical: Added table toolbar, organised button code 2024-07-21 15:11:24 +01:00
Dan Brown
63f4b42453
Lexical: Added toolbar scroll/resize handling
Also added smarter above/below positioning to respond if toolbar would
be off the bottom of the editor, and added hide/show when they'd go
outside editor scroll bounds.
2024-07-19 18:12:51 +01:00
Dan Brown
c7c0df0964
Lexical: Finished up core drawing insert/editing
Added new options that sits on the context, for things needed but not
for the core editor, which are defined out of the editor (drawio URL,
error message text, pageId etc...)
2024-07-19 12:09:41 +01:00
Dan Brown
fb87fb5750
JS: Converted http service to ts 2024-07-18 15:13:14 +01:00
Dan Brown
634b0aaa07
Lexical: Started converting drawio to TS
Converted events service to TS as part of this.
2024-07-18 11:19:11 +01:00
Dan Brown
5002a89754
Lexical: Standardised helper function format 2024-07-17 16:45:57 +01:00
Dan Brown
b367490edc
Lexical: Added list support, started todo 2024-07-17 16:38:20 +01:00
Dan Brown
ea4c50c2c2
Lexical: Added code block selection & edit features
Also added extra lifecycle handling for decorators to things can be
properly cleaned up after node destruction.
2024-07-16 16:36:08 +01:00
Dan Brown
51d8044a54
Lexical: Added initial form/modal styles 2024-07-09 20:49:47 +01:00
Dan Brown
2c96af9aea
Lexical: Worked on toolbar styling, got format submenu working 2024-07-04 16:16:16 +01:00
Dan Brown
04c7e680fd
Lexical: Linked up saving logic of editor via interface 2024-07-04 13:09:53 +01:00
Dan Brown
a8f1160743
JS: Converted come common services to typescript 2024-07-03 11:00:57 +01:00
Dan Brown
feca1f0502
Lexical: Started diagram support 2024-07-03 10:28:04 +01:00
Dan Brown
d0a5a5ef37
Lexical: Linked code block to editor, added button 2024-07-02 17:34:03 +01:00
Dan Brown
97f570a4ee
Lexical: Started code block node implementation 2024-07-02 14:46:30 +01:00
Dan Brown
9ebbf7ce94
Lexical: Started loading real content, Improved html loading
Added more styling/layout for buttons and main content area
2024-07-01 15:10:22 +01:00
Dan Brown
c2ecbf071f
Lexical: Added tracked container, added fullscreen action
Changed how the editor is loaded in, so it now creates its own DOM, and
content is passed via creation function, to be better self-contained.
2024-07-01 10:44:23 +01:00
Dan Brown
b1c489090e
Lexical: Added context toolbar placement, added link toolbar
Also added some basic context toolbar styling
2024-06-30 19:52:09 +01:00
Dan Brown
c9a03c5b01
Lexical: Added base context toolbar logic 2024-06-30 12:13:13 +01:00
Dan Brown
517c578a5f
Lexical: Reorganised some logic into manager 2024-06-30 10:31:39 +01:00
Dan Brown
f10ec3271a
Lexical: Added overflow container 2024-06-27 16:28:06 +01:00
Dan Brown
4e2820d6e3
Lexical: Added horizontal rule node 2024-06-27 15:48:06 +01:00
Dan Brown
72a0e081ca
Lexical: Completed initial table cell resize handle logic 2024-06-26 17:22:00 +01:00
Dan Brown
b1130cb1c3
Lexical: Linked up table resize handler (unfinished) 2024-06-26 13:52:00 +01:00
Dan Brown
59936631ec
Lexical: Extracted mouse drag tracking to new helper 2024-06-25 18:33:29 +01:00
Dan Brown
3af22ce754
Lexical: Created custom table node with col width handling 2024-06-24 20:50:17 +01:00
Dan Brown
5546b8ff43
Lexical: Added more icons, made reflective text/bg color buttons 2024-06-23 15:50:41 +01:00
Dan Brown
a07092b7e6
Lexical: Updated lexical, added undo state tracking, format styles 2024-06-23 11:36:48 +01:00
Dan Brown
ac01c62e6e
Lexical: Added table creator UI 2024-06-21 16:18:44 +01:00
Dan Brown
f47f7dd9d2
Lexical: Added base table support and started resize handling 2024-06-21 13:47:47 +01:00
Dan Brown
13d970c7ce
Lexical: Added button icon system
With a bunch of default icons
2024-06-19 20:00:29 +01:00
Dan Brown
e2409a5fab
Lexical: Added basic list button/support 2024-06-19 16:14:20 +01:00
Dan Brown
9e43e03db4
Lexical: Added color picker controls 2024-06-12 19:51:42 +01:00
Dan Brown
a475cf68bf
Lexical: Added clear formatting button 2024-06-12 14:24:50 +01:00
Dan Brown
e889bc680b
Lexical: Added view/edit source code button/form/action 2024-06-12 14:01:36 +01:00
Dan Brown
5c343638b6
Added base node/button for details/summary 2024-06-06 14:43:50 +01:00
Dan Brown
0722960260
Lexical: Added selection to state for aligned reading
Connected up to work with image form
2024-06-05 18:43:42 +01:00
Dan Brown
e959c468f6
Lexical: Made image resize handles functional 2024-06-05 17:18:58 +01:00
Dan Brown
ba871ec46a
Lexical: Started image resize controls, Defined thorough decorator model 2024-06-05 13:04:49 +01:00
Dan Brown
a74e04141c
Lexical: Started build of image node and decoration UI 2024-06-03 16:56:31 +01:00
Dan Brown
7c504a10a8
Lexical: Created core modal functionality 2024-06-01 16:49:47 +01:00
Dan Brown
ae98745439
Lexical: Started on form UI 2024-05-30 16:50:55 +01:00
Dan Brown
57259aee00
Lexical: Added format previews to format buttons 2024-05-30 12:25:25 +01:00
Dan Brown
dc1a40ea74
Lexical: Added ui container type
Structured UI logical to be fairly standard and mostly covered via
a base class that handles context and core dom work.
2024-05-29 20:38:31 +01:00
Dan Brown
483d9bf26c
Lexical: Added a range of format buttons 2024-05-28 22:56:58 +01:00
Dan Brown
b24d60e98d
Lexical: Started UI fundementals with basic button 2024-05-28 18:04:48 +01:00
Dan Brown
0f8bd869d8
Lexical: Added custom id-supporting paragraph blocks 2024-05-28 15:09:50 +01:00
Dan Brown
49546cd627
Lexical: Switched to ts for new editor build 2024-05-27 23:50:28 +01:00
Dan Brown
6e852d2e65
Lexical: Played with commands, extracted & improved callout node 2024-05-27 20:23:45 +01:00
Dan Brown
5a4f595341
Editors: Added lexical editor for testing
Started basic playground for testing lexical as a new WYSIWYG editor.
Moved out tinymce to be under wysiwyg-tinymce instead so lexical is the
default, but TinyMce code remains.
2024-05-27 15:39:41 +01:00
384 changed files with 67009 additions and 1239 deletions

View File

@ -334,6 +334,11 @@ EXPORT_PAGE_SIZE=a4
# Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
EXPORT_PDF_COMMAND=false
# Export PDF Command Timeout
# The number of seconds that the export PDF command will run before a timeout occurs.
# Only applies for the EXPORT_PDF_COMMAND option, not for DomPDF or wkhtmltopdf.
EXPORT_PDF_COMMAND_TIMEOUT=15
# Set path to wkhtmltopdf binary for PDF generation.
# Can be 'false' or a path path like: '/home/bins/wkhtmltopdf'
# When false, BookStack will attempt to find a wkhtmltopdf in the application

View File

@ -13,9 +13,9 @@ on:
jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-22.04
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v1
- uses: actions/checkout@v4
- name: Install NPM deps
run: npm ci

29
.github/workflows/test-js.yml vendored Normal file
View File

@ -0,0 +1,29 @@
name: test-js
on:
push:
paths:
- '**.js'
- '**.ts'
- '**.json'
pull_request:
paths:
- '**.js'
- '**.ts'
- '**.json'
jobs:
build:
if: ${{ github.ref != 'refs/heads/l10n_development' }}
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install NPM deps
run: npm ci
- name: Run TypeScript type checking
run: npm run ts:lint
- name: Run JavaScript tests
run: npm run test

1
.gitignore vendored
View File

@ -2,6 +2,7 @@
/node_modules
/.vscode
/composer
/coverage
Homestead.yaml
.env
.idea

View File

@ -0,0 +1,10 @@
<?php
namespace BookStack\Access;
use Exception;
class UserInviteException extends Exception
{
//
}

View File

@ -13,11 +13,17 @@ class UserInviteService extends UserTokenService
/**
* Send an invitation to a user to sign into BookStack
* Removes existing invitation tokens.
* @throws UserInviteException
*/
public function sendInvitation(User $user)
{
$this->deleteByUser($user);
$token = $this->createTokenForUser($user);
try {
$user->notify(new UserInviteNotification($token));
} catch (\Exception $exception) {
throw new UserInviteException($exception->getMessage(), $exception->getCode(), $exception);
}
}
}

View File

@ -64,4 +64,14 @@ class MetaController extends Controller
'jsLibData' => file_get_contents(base_path('dev/licensing/js-library-licenses.txt')),
]);
}
/**
* Show the view for /opensearch.xml.
*/
public function opensearch()
{
return response()
->view('misc.opensearch')
->header('Content-Type', 'application/opensearchdescription+xml');
}
}

View File

@ -29,6 +29,10 @@ return [
// Example: EXPORT_PDF_COMMAND="/scripts/convert.sh {input_html_path} {output_pdf_path}"
'pdf_command' => env('EXPORT_PDF_COMMAND', false),
// The amount of time allowed for PDF generation command to run
// before the process times out and is stopped.
'pdf_command_timeout' => env('EXPORT_PDF_COMMAND_TIMEOUT', 15),
// 2024-04: Snappy/WKHTMLtoPDF now considered deprecated in regard to BookStack support.
'snappy' => [
'pdf_binary' => env('WKHTMLTOPDF', false),

View File

@ -3,6 +3,7 @@
namespace BookStack\Entities\Models;
use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Permissions\PermissionApplicator;
use BookStack\Uploads\Attachment;
use Illuminate\Database\Eloquent\Builder;

View File

@ -11,7 +11,7 @@ use BookStack\Entities\Models\PageRevision;
use BookStack\Entities\Queries\EntityQueries;
use BookStack\Entities\Tools\BookContents;
use BookStack\Entities\Tools\PageContent;
use BookStack\Entities\Tools\PageEditorData;
use BookStack\Entities\Tools\PageEditorType;
use BookStack\Entities\Tools\TrashCan;
use BookStack\Exceptions\MoveOperationException;
use BookStack\Exceptions\PermissionsException;
@ -43,6 +43,7 @@ class PageRepo
'owned_by' => user()->id,
'updated_by' => user()->id,
'draft' => true,
'editor' => PageEditorType::getSystemDefault()->value,
]);
if ($parent instanceof Chapter) {
@ -127,7 +128,9 @@ class PageRepo
}
$pageContent = new PageContent($page);
$currentEditor = $page->editor ?: PageEditorData::getSystemDefaultEditor();
$defaultEditor = PageEditorType::getSystemDefault();
$currentEditor = PageEditorType::forPage($page) ?: $defaultEditor;
$inputEditor = PageEditorType::fromRequestValue($input['editor'] ?? '') ?? $currentEditor;
$newEditor = $currentEditor;
$haveInput = isset($input['markdown']) || isset($input['html']);
@ -136,15 +139,17 @@ class PageRepo
if ($haveInput && $inputEmpty) {
$pageContent->setNewHTML('', user());
} elseif (!empty($input['markdown']) && is_string($input['markdown'])) {
$newEditor = 'markdown';
$newEditor = PageEditorType::Markdown;
$pageContent->setNewMarkdown($input['markdown'], user());
} elseif (isset($input['html'])) {
$newEditor = 'wysiwyg';
$newEditor = ($inputEditor->isHtmlBased() ? $inputEditor : null) ?? ($defaultEditor->isHtmlBased() ? $defaultEditor : null) ?? PageEditorType::WysiwygTinymce;
$pageContent->setNewHTML($input['html'], user());
}
if ($newEditor !== $currentEditor && userCan('editor-change')) {
$page->editor = $newEditor;
if (($newEditor !== $currentEditor || empty($page->editor)) && userCan('editor-change')) {
$page->editor = $newEditor->value;
} elseif (empty($page->editor)) {
$page->editor = $defaultEditor->value;
}
}

View File

@ -74,17 +74,17 @@ class PageEditorData
];
}
protected function updateContentForEditor(Page $page, string $editorType): void
protected function updateContentForEditor(Page $page, PageEditorType $editorType): void
{
$isHtml = !empty($page->html) && empty($page->markdown);
// HTML to markdown-clean conversion
if ($editorType === 'markdown' && $isHtml && $this->requestedEditor === 'markdown-clean') {
if ($editorType === PageEditorType::Markdown && $isHtml && $this->requestedEditor === 'markdown-clean') {
$page->markdown = (new HtmlToMarkdown($page->html))->convert();
}
// Markdown to HTML conversion if we don't have HTML
if ($editorType === 'wysiwyg' && !$isHtml) {
if ($editorType->isHtmlBased() && !$isHtml) {
$page->html = (new MarkdownToHtml($page->markdown))->convert();
}
}
@ -94,24 +94,16 @@ class PageEditorData
* Defaults based upon the current content of the page otherwise will fall back
* to system default but will take a requested type (if provided) if permissions allow.
*/
protected function getEditorType(Page $page): string
protected function getEditorType(Page $page): PageEditorType
{
$editorType = $page->editor ?: self::getSystemDefaultEditor();
$editorType = PageEditorType::forPage($page) ?: PageEditorType::getSystemDefault();
// Use requested editor if valid and if we have permission
$requestedType = explode('-', $this->requestedEditor)[0];
if (($requestedType === 'markdown' || $requestedType === 'wysiwyg') && userCan('editor-change')) {
$requestedType = PageEditorType::fromRequestValue($this->requestedEditor);
if ($requestedType && userCan('editor-change')) {
$editorType = $requestedType;
}
return $editorType;
}
/**
* Get the configured system default editor.
*/
public static function getSystemDefaultEditor(): string
{
return setting('app-editor') === 'markdown' ? 'markdown' : 'wysiwyg';
}
}

View File

@ -0,0 +1,37 @@
<?php
namespace BookStack\Entities\Tools;
use BookStack\Entities\Models\Page;
enum PageEditorType: string
{
case WysiwygTinymce = 'wysiwyg';
case WysiwygLexical = 'wysiwyg2024';
case Markdown = 'markdown';
public function isHtmlBased(): bool
{
return match ($this) {
self::WysiwygTinymce, self::WysiwygLexical => true,
self::Markdown => false,
};
}
public static function fromRequestValue(string $value): static|null
{
$editor = explode('-', $value)[0];
return static::tryFrom($editor);
}
public static function forPage(Page $page): static|null
{
return static::tryFrom($page->editor);
}
public static function getSystemDefault(): static
{
$setting = setting('app-editor');
return static::tryFrom($setting) ?? static::WysiwygTinymce;
}
}

View File

@ -5,6 +5,7 @@ namespace BookStack\Entities\Tools;
use BookStack\Exceptions\PdfExportException;
use Knp\Snappy\Pdf as SnappyPdf;
use Dompdf\Dompdf;
use Symfony\Component\Process\Exception\ProcessTimedOutException;
use Symfony\Component\Process\Process;
class PdfGenerator
@ -85,9 +86,15 @@ class PdfGenerator
file_put_contents($inputHtml, $html);
$timeout = intval(config('exports.pdf_command_timeout'));
$process = Process::fromShellCommandline($command);
$process->setTimeout(15);
$process->setTimeout($timeout);
try {
$process->run();
} catch (ProcessTimedOutException $e) {
throw new PdfExportException("PDF Export via command failed due to timeout at {$timeout} second(s)");
}
if (!$process->isSuccessful()) {
throw new PdfExportException("PDF Export via command failed with exit code {$process->getExitCode()}, stdout: {$process->getOutput()}, stderr: {$process->getErrorOutput()}");

View File

@ -3,6 +3,7 @@
namespace BookStack\Users\Controllers;
use BookStack\Access\SocialDriverManager;
use BookStack\Access\UserInviteException;
use BookStack\Exceptions\ImageUploadException;
use BookStack\Exceptions\UserUpdateException;
use BookStack\Http\Controller;
@ -14,6 +15,7 @@ use BookStack\Util\SimpleListOptions;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Validation\Rules\Password;
use Illuminate\Validation\ValidationException;
@ -91,9 +93,15 @@ class UserController extends Controller
$validated = $this->validate($request, array_filter($validationRules));
try {
DB::transaction(function () use ($validated, $sendInvite) {
$this->userRepo->create($validated, $sendInvite);
});
} catch (UserInviteException $e) {
Log::error("Failed to send user invite with error: {$e->getMessage()}");
$this->showErrorNotification(trans('errors.users_could_not_send_invite'));
return redirect('/settings/users/create')->withInput();
}
return redirect('/settings/users');
}

View File

@ -2,6 +2,7 @@
namespace BookStack\Users;
use BookStack\Access\UserInviteException;
use BookStack\Access\UserInviteService;
use BookStack\Activity\ActivityType;
use BookStack\Entities\EntityProvider;
@ -83,6 +84,7 @@ class UserRepo
* As per "createWithoutActivity" but records a "create" activity.
*
* @param array{name: string, email: string, password: ?string, external_auth_id: ?string, language: ?string, roles: ?array} $data
* @throws UserInviteException
*/
public function create(array $data, bool $sendInvite = false): User
{

View File

@ -16,9 +16,9 @@
"ext-json": "*",
"ext-mbstring": "*",
"ext-xml": "*",
"bacon/bacon-qr-code": "^2.0",
"bacon/bacon-qr-code": "^3.0",
"doctrine/dbal": "^3.5",
"dompdf/dompdf": "^2.0",
"dompdf/dompdf": "^3.0",
"guzzlehttp/guzzle": "^7.4",
"intervention/image": "^3.5",
"knplabs/knp-snappy": "^1.5",

801
composer.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// Ensure we have an "editor" value set for pages
// Get default
$default = DB::table('settings')
->where('setting_key', '=', 'app-editor')
->first()
->value ?? 'wysiwyg';
$default = ($default === 'markdown') ? 'markdown' : 'wysiwyg';
// We set it to 'markdown' for pages currently with markdown content
DB::table('pages')
->where('editor', '=', '')
->where('markdown', '!=', '')
->update(['editor' => 'markdown']);
// We set it to 'wysiwyg' where we have HTML but no markdown
DB::table('pages')
->where('editor', '=', '')
->where('markdown', '=', '')
->where('html', '!=', '')
->update(['editor' => 'wysiwyg']);
// Otherwise, where still empty, set to the current default
DB::table('pages')
->where('editor', '=', '')
->update(['editor' => $default]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Can't reverse due to not knowing what would have been empty before
}
};

View File

@ -14,6 +14,7 @@ const entryPoints = {
code: path.join(__dirname, '../../resources/js/code/index.mjs'),
'legacy-modes': path.join(__dirname, '../../resources/js/code/legacy-modes.mjs'),
markdown: path.join(__dirname, '../../resources/js/markdown/index.mjs'),
wysiwyg: path.join(__dirname, '../../resources/js/wysiwyg/index.ts'),
};
// Locate our output directory
@ -31,6 +32,15 @@ esbuild.build({
format: 'esm',
minify: isProd,
logLevel: 'info',
loader: {
'.svg': 'text',
},
absWorkingDir: path.join(__dirname, '../..'),
alias: {
'@icons': './resources/icons',
lexical: './resources/js/wysiwyg/lexical/core',
'@lexical': './resources/js/wysiwyg/lexical',
},
banner: {
js: '// See the "/licenses" URI for full package license details',
css: '/* See the "/licenses" URI for full package license details */',

File diff suppressed because it is too large Load Diff

View File

@ -128,7 +128,7 @@ Link: https://github.com/fruitcake/php-cors
graham-campbell/result-type
License: MIT
License File: vendor/graham-campbell/result-type/LICENSE
Copyright: Copyright (c) 2020-2023 Graham Campbell <*****@**********.**.**>
Copyright: Copyright (c) 2020-2024 Graham Campbell <*****@**********.**.**>
Source: https://github.com/GrahamCampbell/Result-Type.git
Link: https://github.com/GrahamCampbell/Result-Type.git
-----------
@ -676,13 +676,6 @@ Copyright: Copyright (c) 2015-present Fabien Potencier
Source: https://github.com/symfony/polyfill-mbstring.git
Link: https://symfony.com
-----------
symfony/polyfill-php72
License: MIT
License File: vendor/symfony/polyfill-php72/LICENSE
Copyright: Copyright (c) 2015-present Fabien Potencier
Source: https://github.com/symfony/polyfill-php72.git
Link: https://symfony.com
-----------
symfony/polyfill-php80
License: MIT
License File: vendor/symfony/polyfill-php80/LICENSE

209
jest.config.ts Normal file
View File

@ -0,0 +1,209 @@
/**
* For a detailed explanation regarding each configuration property, visit:
* https://jestjs.io/docs/configuration
*/
import type {Config} from 'jest';
import {pathsToModuleNameMapper} from "ts-jest";
import { compilerOptions } from './tsconfig.json';
const config: Config = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "/tmp/jest_rs",
// Automatically clear mock calls, instances, contexts and results before every test
clearMocks: true,
// Indicates whether the coverage information should be collected while executing the test
collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
// collectCoverageFrom: undefined,
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "/node_modules/"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// The default configuration for fake timers
// fakeTimers: {
// "enableGlobally": false
// },
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
globals: {
__DEV__: true,
},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "mjs",
// "cjs",
// "jsx",
// "ts",
// "tsx",
// "json",
// "node"
// ],
modulePaths: ['./'],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
'lexical/shared/invariant': 'resources/js/wysiwyg/lexical/core/shared/__mocks__/invariant',
...pathsToModuleNameMapper(compilerOptions.paths),
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
// preset: undefined,
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state before every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
// resolver: undefined,
// Automatically restore mock state and implementation before every test
// restoreMocks: false,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
roots: [
"./resources/js"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "jsdom",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"**/__tests__/**/*.test.[jt]s",
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "/node_modules/"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jest-circus/runner",
// A map from regular expressions to paths to transformers
transform: {
"^.+.tsx?$": ["ts-jest",{}],
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "/node_modules/",
// "\\.pnp\\.[^\\/]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
};
export default config;

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'سياسة الخصوصية',
'terms_of_service' => 'اتفاقية شروط الخدمة',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Политика за поверителност',
'terms_of_service' => 'Условия на услугата',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Pravila o privatnosti',
'terms_of_service' => 'Uslovi korištenja',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Política de privadesa',
'terms_of_service' => 'Condicions del servei',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Zásady ochrany osobních údajů',
'terms_of_service' => 'Podmínky služby',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Polisi Preifatrwydd',
'terms_of_service' => 'Telerau Gwasanaeth',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -65,7 +65,7 @@ return [
'auth_login' => 'loggede ind',
'auth_register' => 'registered as new user',
'auth_password_reset_request' => 'requested user password reset',
'auth_password_reset_update' => 'reset user password',
'auth_password_reset_update' => 'nulstil adgangskode',
'mfa_setup_method' => 'configured MFA method',
'mfa_setup_method_notification' => 'Multi-faktor metode konfigureret',
'mfa_remove_method' => 'removed MFA method',
@ -85,8 +85,8 @@ return [
'webhook_delete_notification' => 'Webhooken blev slettet',
// Users
'user_create' => 'created user',
'user_create_notification' => 'User successfully created',
'user_create' => 'opret bruger',
'user_create_notification' => 'Bruger oprettet korrekt',
'user_update' => 'updated user',
'user_update_notification' => 'Brugeren blev opdateret',
'user_delete' => 'deleted user',

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privatlivspolitik',
'terms_of_service' => 'Tjenestevilkår',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Datenschutzbestimmungen',
'terms_of_service' => 'Allgemeine Geschäftsbedingungen',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Datenschutzerklärung',
'terms_of_service' => 'Allgemeine Geschäftsbedingungen',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Πολιτική Απορρήτου',
'terms_of_service' => 'Όροι χρήσης',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privacy Policy',
'terms_of_service' => 'Terms of Service',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -224,6 +224,8 @@ return [
'pages_edit_switch_to_markdown_clean' => '(Clean Content)',
'pages_edit_switch_to_markdown_stable' => '(Stable Content)',
'pages_edit_switch_to_wysiwyg' => 'Switch to WYSIWYG Editor',
'pages_edit_switch_to_new_wysiwyg' => 'Switch to new WYSIWYG',
'pages_edit_switch_to_new_wysiwyg_desc' => '(In Alpha Testing)',
'pages_edit_set_changelog' => 'Set Changelog',
'pages_edit_enter_changelog_desc' => 'Enter a brief description of the changes you\'ve made',
'pages_edit_enter_changelog' => 'Enter Changelog',

View File

@ -78,6 +78,7 @@ return [
// Users
'users_cannot_delete_only_admin' => 'You cannot delete the only admin',
'users_cannot_delete_guest' => 'You cannot delete the guest user',
'users_could_not_send_invite' => 'Could not create user since invite email failed to send',
// Roles
'role_cannot_be_edited' => 'This role cannot be edited',

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Política de privacidad',
'terms_of_service' => 'Términos de Servicio',
// OpenSearch
'opensearch_description' => 'Buscar :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Política de privacidad',
'terms_of_service' => 'Términos de Servicio',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privaatsus',
'terms_of_service' => 'Kasutustingimused',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Pribatutasun politika',
'terms_of_service' => 'Zerbitzu-baldintzak',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'سیاست حفظ حریم خصوصی',
'terms_of_service' => 'شرایط خدمات',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Tietosuojaseloste',
'terms_of_service' => 'Palvelun käyttöehdot',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Politique de confidentialité',
'terms_of_service' => 'Conditions d\'utilisation',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'מדיניות הפרטיות',
'terms_of_service' => 'תנאי שימוש',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Politika privatnosti',
'terms_of_service' => 'Uvjeti korištenja',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Adatvédelmi irányelvek',
'terms_of_service' => 'Felhasználási feltételek',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Kebijakan Privasi',
'terms_of_service' => 'Ketentuan Layanan',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Norme sulla privacy',
'terms_of_service' => 'Condizioni del Servizio',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'プライバシーポリシー',
'terms_of_service' => '利用規約',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privacy Policy',
'terms_of_service' => 'Terms of Service',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => '개인 정보 처리 방침',
'terms_of_service' => '서비스 이용 약관',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privatumo politika',
'terms_of_service' => 'Paslaugų teikimo paslaugos',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privātuma politika',
'terms_of_service' => 'Pakalpojuma noteikumi',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Personvernregler',
'terms_of_service' => 'Bruksvilkår',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privacybeleid',
'terms_of_service' => 'Algemene voorwaarden',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Personvernreglar',
'terms_of_service' => 'Bruksvilkår',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Polityka prywatności',
'terms_of_service' => 'Warunki usługi',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Política de Privacidade',
'terms_of_service' => 'Termos de Utilização',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Políticas de Privacidade',
'terms_of_service' => 'Termos de Serviço',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Politică de confidențialitate',
'terms_of_service' => 'Termeni și condiții',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Политика конфиденциальности',
'terms_of_service' => 'Условия использования',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Zásady ochrany osobných údajov',
'terms_of_service' => 'Podmienky používania',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Pravilnik o zasebnosti',
'terms_of_service' => 'Pogoji uporabe',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Privacy Policy',
'terms_of_service' => 'Terms of Service',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Правила о приватности',
'terms_of_service' => 'Услови коришћења',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Integritetspolicy',
'terms_of_service' => 'Användarvillkor',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Gizlilik Politikası',
'terms_of_service' => 'Hizmet Şartları',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Політика приватності',
'terms_of_service' => 'Умови використання',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Maxfiylik siyosati',
'terms_of_service' => 'Xizmat korsatish shartlari',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => 'Chính Sách Quyền Riêng Tư',
'terms_of_service' => 'Điều khoản Dịch vụ',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => '隐私政策',
'terms_of_service' => '服务条款',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

View File

@ -107,4 +107,7 @@ return [
// Not directly used but available for convenience to users.
'privacy_policy' => '隱私權政策',
'terms_of_service' => '服務條款',
// OpenSearch
'opensearch_description' => 'Search :appName',
];

4014
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,7 @@
"build:css:watch": "sass ./resources/sass:./public/dist --watch --embed-sources",
"build:css:production": "sass ./resources/sass:./public/dist -s compressed",
"build:js:dev": "node dev/build/esbuild.js",
"build:js:watch": "chokidar --initial \"./resources/**/*.js\" \"./resources/**/*.mjs\" -c \"npm run build:js:dev\"",
"build:js:watch": "chokidar --initial \"./resources/**/*.js\" \"./resources/**/*.mjs\" \"./resources/**/*.ts\" -c \"npm run build:js:dev\"",
"build:js:production": "node dev/build/esbuild.js production",
"build": "npm-run-all --parallel build:*:dev",
"production": "npm-run-all --parallel build:*:production",
@ -14,7 +14,9 @@
"livereload": "livereload ./public/dist/",
"permissions": "chown -R $USER:$USER bootstrap/cache storage public/uploads",
"lint": "eslint \"resources/**/*.js\" \"resources/**/*.mjs\"",
"fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\""
"fix": "eslint --fix \"resources/**/*.js\" \"resources/**/*.mjs\"",
"ts:lint": "tsc --noEmit",
"test": "jest"
},
"devDependencies": {
"@lezer/generator": "^1.5.1",
@ -23,9 +25,14 @@
"eslint": "^8.55.0",
"eslint-config-airbnb-base": "^15.0.0",
"eslint-plugin-import": "^2.29.0",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"livereload": "^0.9.3",
"npm-run-all": "^4.1.5",
"sass": "^1.69.5"
"sass": "^1.69.5",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.2",
"typescript": "5.6.*"
},
"dependencies": {
"@codemirror/commands": "^6.3.2",
@ -44,6 +51,7 @@
"@lezer/highlight": "^1.2.0",
"@ssddanbrown/codemirror-lang-smarty": "^1.0.0",
"@ssddanbrown/codemirror-lang-twig": "^1.0.0",
"@types/jest": "^29.5.13",
"codemirror": "^6.0.1",
"idb-keyval": "^6.2.1",
"markdown-it": "^14.1.0",
@ -59,7 +67,8 @@
},
"extends": "airbnb-base",
"ignorePatterns": [
"resources/**/*-stub.js"
"resources/**/*-stub.js",
"resources/**/*.ts"
],
"overrides": [],
"parserOptions": {

View File

@ -152,6 +152,7 @@ Note: This is not an exhaustive list of all libraries and projects that would be
* [Laravel](http://laravel.com/) - _[MIT](https://github.com/laravel/framework/blob/v8.82.0/LICENSE.md)_
* [TinyMCE](https://www.tinymce.com/) - _[MIT](https://github.com/tinymce/tinymce/blob/develop/LICENSE.TXT)_
* [Lexical](https://lexical.dev/) - _[MIT](https://github.com/facebook/lexical/blob/main/LICENSE)_
* [CodeMirror](https://codemirror.net) - _[MIT](https://github.com/codemirror/CodeMirror/blob/master/LICENSE)_
* [Sortable](https://github.com/SortableJS/Sortable) - _[MIT](https://github.com/SortableJS/Sortable/blob/master/LICENSE)_
* [Google Material Icons](https://github.com/google/material-design-icons) - _[Apache-2.0](https://github.com/google/material-design-icons/blob/master/LICENSE)_

View File

@ -0,0 +1 @@
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="m2 6.9159 10 10.168 10-10.168z" stroke-width="2.0168"/></svg>

After

Width:  |  Height:  |  Size: 131 B

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/><path fill="none" d="M0 0h24v24H0z"/></svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"/></svg>

Before

Width:  |  Height:  |  Size: 216 B

After

Width:  |  Height:  |  Size: 179 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm160-160v-80h400v80H280ZM120-440v-80h720v80H120Zm160-160v-80h400v80H280ZM120-760v-80h720v80H120Z"/></svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm0-160v-80h720v80H120Zm0-160v-80h720v80H120Zm0-160v-80h720v80H120Zm0-160v-80h720v80H120Z"/></svg>

After

Width:  |  Height:  |  Size: 195 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm0-160v-80h480v80H120Zm0-160v-80h720v80H120Zm0-160v-80h480v80H120Zm0-160v-80h720v80H120Z"/></svg>

After

Width:  |  Height:  |  Size: 195 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-760v-80h720v80H120Zm240 160v-80h480v80H360ZM120-440v-80h720v80H120Zm240 160v-80h480v80H360ZM120-120v-80h720v80H120Z"/></svg>

After

Width:  |  Height:  |  Size: 203 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M272-200v-560h221q65 0 120 40t55 111q0 51-23 78.5T602-491q25 11 55.5 41t30.5 90q0 89-65 124.5T501-200H272Zm121-112h104q48 0 58.5-24.5T566-372q0-11-10.5-35.5T494-432H393v120Zm0-228h93q33 0 48-17t15-38q0-24-17-39t-44-15h-95v109Z"/></svg>

After

Width:  |  Height:  |  Size: 309 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m384-336 56-57-87-87 87-87-56-57-144 144 144 144Zm192 0 144-144-144-144-56 57 87 87-87 87 56 57ZM200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-560H200v560Zm0-560v560-560Z"/></svg>

After

Width:  |  Height:  |  Size: 336 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M320-240 80-480l240-240 57 57-184 184 183 183-56 56Zm320 0-57-57 184-184-183-183 56-56 240 240-240 240Z"/></svg>

After

Width:  |  Height:  |  Size: 186 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M800-436q0 36-8 69t-22 63l-62-60q6-17 9-34.5t3-37.5q0-47-17.5-89T650-600L480-768l-88 86-56-56 144-142 226 222q44 42 69 99.5T800-436Zm-8 380L668-180q-41 29-88 44.5T480-120q-133 0-226.5-92.5T160-436q0-51 16-98t48-90L56-792l56-56 736 736-56 56ZM480-200q36 0 68.5-10t61.5-28L280-566q-21 32-30.5 64t-9.5 66q0 98 70 167t170 69Zm-37-204Zm110-116Z"/></svg>

After

Width:  |  Height:  |  Size: 422 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h560q33 0 56.5 23.5T840-760v560q0 33-23.5 56.5T760-120H200Zm0-80h560v-480H200v480Zm80-280v-80h400v80H280Zm0 160v-80h240v80H280Z"/></svg>

After

Width:  |  Height:  |  Size: 270 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M480-60q-63 0-106.5-43.5T330-210q0-52 31-91.5t79-53.5v-85H200v-160H100v-280h280v280H280v80h400v-85q-48-14-79-53.5T570-750q0-63 43.5-106.5T720-900q63 0 106.5 43.5T870-750q0 52-31 91.5T760-605v165H520v85q48 14 79 53.5t31 91.5q0 63-43.5 106.5T480-60Zm240-620q29 0 49.5-20.5T790-750q0-29-20.5-49.5T720-820q-29 0-49.5 20.5T650-750q0 29 20.5 49.5T720-680Zm-540 0h120v-120H180v120Zm300 540q29 0 49.5-20.5T550-210q0-29-20.5-49.5T480-280q-29 0-49.5 20.5T410-210q0 29 20.5 49.5T480-140ZM240-740Zm480-10ZM480-210Z"/></svg>

After

Width:  |  Height:  |  Size: 585 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M440-800v400q0 17-11.5 28.5T400-360q-17 0-28.5-11.5T360-400v-160q-66 0-113-47t-47-113q0-66 47-113t113-47h280q17 0 28.5 11.5T680-840q0 17-11.5 28.5T640-800h-40v400q0 17-11.5 28.5T560-360q-17 0-28.5-11.5T520-400v-400h-80Zm-80 160v-160q-33 0-56.5 23.5T280-720q0 33 23.5 56.5T360-640Zm0-80Zm328 520H160q-17 0-28.5-11.5T120-240q0-17 11.5-28.5T160-280h528l-36-36q-11-11-11-28t11-28q11-11 28-11t28 11l104 104q12 12 12 28t-12 28L708-108q-11 11-28 11t-28-11q-11-11-11-28t11-28l36-36Z"/></svg>

After

Width:  |  Height:  |  Size: 557 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M440-800v400q0 17-11.5 28.5T400-360q-17 0-28.5-11.5T360-400v-160q-66 0-113-47t-47-113q0-66 47-113t113-47h280q17 0 28.5 11.5T680-840q0 17-11.5 28.5T640-800h-40v400q0 17-11.5 28.5T560-360q-17 0-28.5-11.5T520-400v-400h-80ZM272-200l36 36q11 11 11 28t-11 28q-11 11-28 11t-28-11L148-212q-12-12-12-28t12-28l104-104q11-11 28-11t28 11q11 11 11 28t-11 28l-36 36h528q17 0 28.5 11.5T840-240q0 17-11.5 28.5T800-200H272Zm88-440v-160q-33 0-56.5 23.5T280-720q0 33 23.5 56.5T360-640Zm0-80Z"/></svg>

After

Width:  |  Height:  |  Size: 555 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="m528-546-93-93-121-121h486v120H568l-40 94ZM792-56 460-388l-80 188H249l119-280L56-792l56-56 736 736-56 56Z"/></svg>

After

Width:  |  Height:  |  Size: 188 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-200h80v120h120v80H120Zm520 0v-80h120v-120h80v200H640ZM120-640v-200h200v80H200v120h-80Zm640 0v-120H640v-80h200v200h-80Z"/></svg>

After

Width:  |  Height:  |  Size: 211 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M478-240q21 0 35.5-14.5T528-290q0-21-14.5-35.5T478-340q-21 0-35.5 14.5T428-290q0 21 14.5 35.5T478-240Zm-36-154h74q0-33 7.5-52t42.5-52q26-26 41-49.5t15-56.5q0-56-41-86t-97-30q-57 0-92.5 30T342-618l66 26q5-18 22.5-39t53.5-21q32 0 48 17.5t16 38.5q0 20-12 37.5T506-526q-44 39-54 59t-10 73Zm38 314q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"/></svg>

After

Width:  |  Height:  |  Size: 653 B

View File

@ -0,0 +1 @@
<svg version="1.1" viewBox="0 -960 960 960" xmlns="http://www.w3.org/2000/svg"><path class="editor-icon-color-bar" d="m80-2e-6v-160h800v160z"/><path d="m584-480-104-104-160 160 103 104zm-47-160 103 103 160-159-104-104zm-84-29 216 216-189 190c-16 16-34.833 24-56.5 24s-40.5-8-56.5-24l-27 23h-200l126-125c-16-16-24.333-35.167-25-57.5s7-41.5 23-57.5zm0 0 187-187c16-16 34.833-24 56.5-24s40.5 8 56.5 24l104 103c16 16 24 34.833 24 56.5s-8 40.5-24 56.5l-188 187z"/></svg>

After

Width:  |  Height:  |  Size: 465 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M160-440v-80h640v80H160Z"/></svg>

After

Width:  |  Height:  |  Size: 107 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h200v80H200v560h560v-214l80 80v134q0 33-23.5 56.5T760-120H200Zm40-160 120-160 90 120 120-160 150 200H240Zm622-144L738-548q-21 14-45 21t-51 7q-74 0-126-52.5T464-700q0-75 52.5-127.5T644-880q75 0 127.5 52.5T824-700q0 27-8 52t-20 46l122 122-56 56ZM644-600q42 0 71-29t29-71q0-42-29-71t-71-29q-42 0-71 29t-29 71q0 42 29 71t71 29Z"/></svg>

After

Width:  |  Height:  |  Size: 466 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h360v80H200v560h560v-360h80v360q0 33-23.5 56.5T760-120H200Zm480-480v-80h-80v-80h80v-80h80v80h80v80h-80v80h-80ZM240-280h480L570-480 450-320l-90-120-120 160Zm-40-480v560-560Z"/></svg>

After

Width:  |  Height:  |  Size: 315 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm320-160v-80h400v80H440Zm0-160v-80h400v80H440Zm0-160v-80h400v80H440ZM120-760v-80h720v80H120Zm160 440L120-480l160-160v320Z"/></svg>

After

Width:  |  Height:  |  Size: 228 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M120-120v-80h720v80H120Zm320-160v-80h400v80H440Zm0-160v-80h400v80H440Zm0-160v-80h400v80H440ZM120-760v-80h720v80H120Zm0 440v-320l160 160-160 160Z"/></svg>

After

Width:  |  Height:  |  Size: 227 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M200-200v-100h160l120-360H320v-100h400v100H580L460-300h140v100H200Z"/></svg>

After

Width:  |  Height:  |  Size: 150 B

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M680-160v-120H560v-80h120v-120h80v120h120v80H760v120h-80ZM440-280H280q-83 0-141.5-58.5T80-480q0-83 58.5-141.5T280-680h160v80H280q-50 0-85 35t-35 85q0 50 35 85t85 35h160v80ZM320-440v-80h320v80H320Zm560-40h-80q0-50-35-85t-85-35H520v-80h160q83 0 141.5 58.5T880-480Z"/></svg>

After

Width:  |  Height:  |  Size: 345 B

Some files were not shown because too many files have changed in this diff Show More