From 14434bc2acdb7f7890bed2ef14647a7d71e84f81 Mon Sep 17 00:00:00 2001 From: mirivlad Date: Wed, 26 Nov 2025 17:25:27 +0800 Subject: [PATCH] finish line! --- 333.txt | 982 +++++------ assets/css/quill.snow.css | 10 + assets/css/quill_reset.css | 2 +- assets/css/style.css | 14 + assets/editor/README.md | 23 + assets/editor/ckeditor5/LICENSE-ckeditor5.md | 55 + assets/editor/ckeditor5/ckeditor5-content.css | 5 + assets/editor/ckeditor5/ckeditor5-editor.css | 5 + assets/editor/ckeditor5/ckeditor5.css | 6 + assets/editor/ckeditor5/ckeditor5.css.map | 1 + assets/editor/ckeditor5/ckeditor5.js | 6 + assets/editor/ckeditor5/ckeditor5.js.map | 1 + assets/editor/ckeditor5/ckeditor5.umd.js | 1450 +++++++++++++++++ assets/editor/ckeditor5/ckeditor5.umd.js.map | 1 + assets/editor/ckeditor5/translations/af.d.ts | 8 + assets/editor/ckeditor5/translations/af.js | 5 + .../editor/ckeditor5/translations/af.umd.js | 11 + assets/editor/ckeditor5/translations/ar.d.ts | 8 + assets/editor/ckeditor5/translations/ar.js | 5 + .../editor/ckeditor5/translations/ar.umd.js | 11 + assets/editor/ckeditor5/translations/ast.d.ts | 8 + assets/editor/ckeditor5/translations/ast.js | 5 + .../editor/ckeditor5/translations/ast.umd.js | 11 + assets/editor/ckeditor5/translations/az.d.ts | 8 + assets/editor/ckeditor5/translations/az.js | 5 + .../editor/ckeditor5/translations/az.umd.js | 11 + assets/editor/ckeditor5/translations/be.d.ts | 8 + assets/editor/ckeditor5/translations/be.js | 5 + .../editor/ckeditor5/translations/be.umd.js | 11 + assets/editor/ckeditor5/translations/bg.d.ts | 8 + assets/editor/ckeditor5/translations/bg.js | 5 + .../editor/ckeditor5/translations/bg.umd.js | 11 + assets/editor/ckeditor5/translations/bn.d.ts | 8 + assets/editor/ckeditor5/translations/bn.js | 5 + .../editor/ckeditor5/translations/bn.umd.js | 11 + assets/editor/ckeditor5/translations/bs.d.ts | 8 + assets/editor/ckeditor5/translations/bs.js | 5 + .../editor/ckeditor5/translations/bs.umd.js | 11 + assets/editor/ckeditor5/translations/ca.d.ts | 8 + assets/editor/ckeditor5/translations/ca.js | 5 + .../editor/ckeditor5/translations/ca.umd.js | 11 + assets/editor/ckeditor5/translations/cs.d.ts | 8 + assets/editor/ckeditor5/translations/cs.js | 5 + .../editor/ckeditor5/translations/cs.umd.js | 11 + assets/editor/ckeditor5/translations/da.d.ts | 8 + assets/editor/ckeditor5/translations/da.js | 5 + .../editor/ckeditor5/translations/da.umd.js | 11 + .../editor/ckeditor5/translations/de-ch.d.ts | 8 + assets/editor/ckeditor5/translations/de-ch.js | 5 + .../ckeditor5/translations/de-ch.umd.js | 11 + assets/editor/ckeditor5/translations/de.d.ts | 8 + assets/editor/ckeditor5/translations/de.js | 5 + .../editor/ckeditor5/translations/de.umd.js | 11 + assets/editor/ckeditor5/translations/el.d.ts | 8 + assets/editor/ckeditor5/translations/el.js | 5 + .../editor/ckeditor5/translations/el.umd.js | 11 + .../editor/ckeditor5/translations/en-au.d.ts | 8 + assets/editor/ckeditor5/translations/en-au.js | 5 + .../ckeditor5/translations/en-au.umd.js | 11 + .../editor/ckeditor5/translations/en-gb.d.ts | 8 + assets/editor/ckeditor5/translations/en-gb.js | 5 + .../ckeditor5/translations/en-gb.umd.js | 11 + assets/editor/ckeditor5/translations/en.d.ts | 8 + assets/editor/ckeditor5/translations/en.js | 5 + .../editor/ckeditor5/translations/en.umd.js | 11 + assets/editor/ckeditor5/translations/eo.d.ts | 8 + assets/editor/ckeditor5/translations/eo.js | 5 + .../editor/ckeditor5/translations/eo.umd.js | 11 + .../editor/ckeditor5/translations/es-co.d.ts | 8 + assets/editor/ckeditor5/translations/es-co.js | 5 + .../ckeditor5/translations/es-co.umd.js | 11 + assets/editor/ckeditor5/translations/es.d.ts | 8 + assets/editor/ckeditor5/translations/es.js | 5 + .../editor/ckeditor5/translations/es.umd.js | 11 + assets/editor/ckeditor5/translations/et.d.ts | 8 + assets/editor/ckeditor5/translations/et.js | 5 + .../editor/ckeditor5/translations/et.umd.js | 11 + assets/editor/ckeditor5/translations/eu.d.ts | 8 + assets/editor/ckeditor5/translations/eu.js | 5 + .../editor/ckeditor5/translations/eu.umd.js | 11 + assets/editor/ckeditor5/translations/fa.d.ts | 8 + assets/editor/ckeditor5/translations/fa.js | 5 + .../editor/ckeditor5/translations/fa.umd.js | 11 + assets/editor/ckeditor5/translations/fi.d.ts | 8 + assets/editor/ckeditor5/translations/fi.js | 5 + .../editor/ckeditor5/translations/fi.umd.js | 11 + assets/editor/ckeditor5/translations/fr.d.ts | 8 + assets/editor/ckeditor5/translations/fr.js | 5 + .../editor/ckeditor5/translations/fr.umd.js | 11 + assets/editor/ckeditor5/translations/gl.d.ts | 8 + assets/editor/ckeditor5/translations/gl.js | 5 + .../editor/ckeditor5/translations/gl.umd.js | 11 + assets/editor/ckeditor5/translations/gu.d.ts | 8 + assets/editor/ckeditor5/translations/gu.js | 5 + .../editor/ckeditor5/translations/gu.umd.js | 11 + assets/editor/ckeditor5/translations/he.d.ts | 8 + assets/editor/ckeditor5/translations/he.js | 5 + .../editor/ckeditor5/translations/he.umd.js | 11 + assets/editor/ckeditor5/translations/hi.d.ts | 8 + assets/editor/ckeditor5/translations/hi.js | 5 + .../editor/ckeditor5/translations/hi.umd.js | 11 + assets/editor/ckeditor5/translations/hr.d.ts | 8 + assets/editor/ckeditor5/translations/hr.js | 5 + .../editor/ckeditor5/translations/hr.umd.js | 11 + assets/editor/ckeditor5/translations/hu.d.ts | 8 + assets/editor/ckeditor5/translations/hu.js | 5 + .../editor/ckeditor5/translations/hu.umd.js | 11 + assets/editor/ckeditor5/translations/hy.d.ts | 8 + assets/editor/ckeditor5/translations/hy.js | 5 + .../editor/ckeditor5/translations/hy.umd.js | 11 + assets/editor/ckeditor5/translations/id.d.ts | 8 + assets/editor/ckeditor5/translations/id.js | 5 + .../editor/ckeditor5/translations/id.umd.js | 11 + assets/editor/ckeditor5/translations/it.d.ts | 8 + assets/editor/ckeditor5/translations/it.js | 5 + .../editor/ckeditor5/translations/it.umd.js | 11 + assets/editor/ckeditor5/translations/ja.d.ts | 8 + assets/editor/ckeditor5/translations/ja.js | 5 + .../editor/ckeditor5/translations/ja.umd.js | 11 + assets/editor/ckeditor5/translations/jv.d.ts | 8 + assets/editor/ckeditor5/translations/jv.js | 5 + .../editor/ckeditor5/translations/jv.umd.js | 11 + assets/editor/ckeditor5/translations/kk.d.ts | 8 + assets/editor/ckeditor5/translations/kk.js | 5 + .../editor/ckeditor5/translations/kk.umd.js | 11 + assets/editor/ckeditor5/translations/km.d.ts | 8 + assets/editor/ckeditor5/translations/km.js | 5 + .../editor/ckeditor5/translations/km.umd.js | 11 + assets/editor/ckeditor5/translations/kn.d.ts | 8 + assets/editor/ckeditor5/translations/kn.js | 5 + .../editor/ckeditor5/translations/kn.umd.js | 11 + assets/editor/ckeditor5/translations/ko.d.ts | 8 + assets/editor/ckeditor5/translations/ko.js | 5 + .../editor/ckeditor5/translations/ko.umd.js | 11 + assets/editor/ckeditor5/translations/ku.d.ts | 8 + assets/editor/ckeditor5/translations/ku.js | 5 + .../editor/ckeditor5/translations/ku.umd.js | 11 + assets/editor/ckeditor5/translations/lt.d.ts | 8 + assets/editor/ckeditor5/translations/lt.js | 5 + .../editor/ckeditor5/translations/lt.umd.js | 11 + assets/editor/ckeditor5/translations/lv.d.ts | 8 + assets/editor/ckeditor5/translations/lv.js | 5 + .../editor/ckeditor5/translations/lv.umd.js | 11 + assets/editor/ckeditor5/translations/ms.d.ts | 8 + assets/editor/ckeditor5/translations/ms.js | 5 + .../editor/ckeditor5/translations/ms.umd.js | 11 + assets/editor/ckeditor5/translations/nb.d.ts | 8 + assets/editor/ckeditor5/translations/nb.js | 5 + .../editor/ckeditor5/translations/nb.umd.js | 11 + assets/editor/ckeditor5/translations/ne.d.ts | 8 + assets/editor/ckeditor5/translations/ne.js | 5 + .../editor/ckeditor5/translations/ne.umd.js | 11 + assets/editor/ckeditor5/translations/nl.d.ts | 8 + assets/editor/ckeditor5/translations/nl.js | 5 + .../editor/ckeditor5/translations/nl.umd.js | 11 + assets/editor/ckeditor5/translations/no.d.ts | 8 + assets/editor/ckeditor5/translations/no.js | 5 + .../editor/ckeditor5/translations/no.umd.js | 11 + assets/editor/ckeditor5/translations/oc.d.ts | 8 + assets/editor/ckeditor5/translations/oc.js | 5 + .../editor/ckeditor5/translations/oc.umd.js | 11 + assets/editor/ckeditor5/translations/pl.d.ts | 8 + assets/editor/ckeditor5/translations/pl.js | 5 + .../editor/ckeditor5/translations/pl.umd.js | 11 + .../editor/ckeditor5/translations/pt-br.d.ts | 8 + assets/editor/ckeditor5/translations/pt-br.js | 5 + .../ckeditor5/translations/pt-br.umd.js | 11 + assets/editor/ckeditor5/translations/pt.d.ts | 8 + assets/editor/ckeditor5/translations/pt.js | 5 + .../editor/ckeditor5/translations/pt.umd.js | 11 + assets/editor/ckeditor5/translations/ro.d.ts | 8 + assets/editor/ckeditor5/translations/ro.js | 5 + .../editor/ckeditor5/translations/ro.umd.js | 11 + assets/editor/ckeditor5/translations/ru.d.ts | 8 + assets/editor/ckeditor5/translations/ru.js | 5 + .../editor/ckeditor5/translations/ru.umd.js | 11 + assets/editor/ckeditor5/translations/si.d.ts | 8 + assets/editor/ckeditor5/translations/si.js | 5 + .../editor/ckeditor5/translations/si.umd.js | 11 + assets/editor/ckeditor5/translations/sk.d.ts | 8 + assets/editor/ckeditor5/translations/sk.js | 5 + .../editor/ckeditor5/translations/sk.umd.js | 11 + assets/editor/ckeditor5/translations/sl.d.ts | 8 + assets/editor/ckeditor5/translations/sl.js | 5 + .../editor/ckeditor5/translations/sl.umd.js | 11 + assets/editor/ckeditor5/translations/sq.d.ts | 8 + assets/editor/ckeditor5/translations/sq.js | 5 + .../editor/ckeditor5/translations/sq.umd.js | 11 + .../ckeditor5/translations/sr-latn.d.ts | 8 + .../editor/ckeditor5/translations/sr-latn.js | 5 + .../ckeditor5/translations/sr-latn.umd.js | 11 + assets/editor/ckeditor5/translations/sr.d.ts | 8 + assets/editor/ckeditor5/translations/sr.js | 5 + .../editor/ckeditor5/translations/sr.umd.js | 11 + assets/editor/ckeditor5/translations/sv.d.ts | 8 + assets/editor/ckeditor5/translations/sv.js | 5 + .../editor/ckeditor5/translations/sv.umd.js | 11 + assets/editor/ckeditor5/translations/th.d.ts | 8 + assets/editor/ckeditor5/translations/th.js | 5 + .../editor/ckeditor5/translations/th.umd.js | 11 + assets/editor/ckeditor5/translations/ti.d.ts | 8 + assets/editor/ckeditor5/translations/ti.js | 5 + .../editor/ckeditor5/translations/ti.umd.js | 11 + assets/editor/ckeditor5/translations/tk.d.ts | 8 + assets/editor/ckeditor5/translations/tk.js | 5 + .../editor/ckeditor5/translations/tk.umd.js | 11 + assets/editor/ckeditor5/translations/tr.d.ts | 8 + assets/editor/ckeditor5/translations/tr.js | 5 + .../editor/ckeditor5/translations/tr.umd.js | 11 + assets/editor/ckeditor5/translations/tt.d.ts | 8 + assets/editor/ckeditor5/translations/tt.js | 5 + .../editor/ckeditor5/translations/tt.umd.js | 11 + assets/editor/ckeditor5/translations/ug.d.ts | 8 + assets/editor/ckeditor5/translations/ug.js | 5 + .../editor/ckeditor5/translations/ug.umd.js | 11 + assets/editor/ckeditor5/translations/uk.d.ts | 8 + assets/editor/ckeditor5/translations/uk.js | 5 + .../editor/ckeditor5/translations/uk.umd.js | 11 + assets/editor/ckeditor5/translations/ur.d.ts | 8 + assets/editor/ckeditor5/translations/ur.js | 5 + .../editor/ckeditor5/translations/ur.umd.js | 11 + assets/editor/ckeditor5/translations/uz.d.ts | 8 + assets/editor/ckeditor5/translations/uz.js | 5 + .../editor/ckeditor5/translations/uz.umd.js | 11 + assets/editor/ckeditor5/translations/vi.d.ts | 8 + assets/editor/ckeditor5/translations/vi.js | 5 + .../editor/ckeditor5/translations/vi.umd.js | 11 + .../editor/ckeditor5/translations/zh-cn.d.ts | 8 + assets/editor/ckeditor5/translations/zh-cn.js | 5 + .../ckeditor5/translations/zh-cn.umd.js | 11 + assets/editor/ckeditor5/translations/zh.d.ts | 8 + assets/editor/ckeditor5/translations/zh.js | 5 + .../editor/ckeditor5/translations/zh.umd.js | 11 + assets/editor/index.html | 39 + assets/editor/main.js | 241 +++ assets/editor/style.css | 23 + assets/js/autosave.js | 241 +-- assets/js/editor.js | 106 +- assets/js/quill.js | 3 + controllers/BookController.php | 26 +- controllers/ChapterController.php | 135 +- controllers/ExportController.php | 3 +- controllers/SeriesController.php | 2 + index.php | 77 +- models/Book.php | 31 +- models/Chapter.php | 27 + uploads/avatars/avatar_1_1764119487.jpg | Bin 0 -> 6516 bytes uploads/covers/cover_2_1764062021.jpg | Bin uploads/covers/cover_3_1764051570.jpg | Bin views/admin/users.php | 4 +- views/books/edit.php | 34 +- views/books/index.php | 6 +- views/books/view_public.php | 75 +- views/chapters/create.php | 55 +- views/chapters/edit.php | 12 +- views/chapters/index.php | 16 +- views/dashboard/index.php | 9 +- views/layouts/header.php | 4 +- views/series/edit.php | 43 +- views/series/index.php | 15 +- views/user/view_public.php | 4 +- 261 files changed, 4530 insertions(+), 1014 deletions(-) mode change 100644 => 100755 333.txt create mode 100755 assets/css/quill.snow.css mode change 100644 => 100755 assets/css/quill_reset.css create mode 100644 assets/editor/README.md create mode 100644 assets/editor/ckeditor5/LICENSE-ckeditor5.md create mode 100644 assets/editor/ckeditor5/ckeditor5-content.css create mode 100644 assets/editor/ckeditor5/ckeditor5-editor.css create mode 100644 assets/editor/ckeditor5/ckeditor5.css create mode 100644 assets/editor/ckeditor5/ckeditor5.css.map create mode 100644 assets/editor/ckeditor5/ckeditor5.js create mode 100644 assets/editor/ckeditor5/ckeditor5.js.map create mode 100644 assets/editor/ckeditor5/ckeditor5.umd.js create mode 100644 assets/editor/ckeditor5/ckeditor5.umd.js.map create mode 100644 assets/editor/ckeditor5/translations/af.d.ts create mode 100644 assets/editor/ckeditor5/translations/af.js create mode 100644 assets/editor/ckeditor5/translations/af.umd.js create mode 100644 assets/editor/ckeditor5/translations/ar.d.ts create mode 100644 assets/editor/ckeditor5/translations/ar.js create mode 100644 assets/editor/ckeditor5/translations/ar.umd.js create mode 100644 assets/editor/ckeditor5/translations/ast.d.ts create mode 100644 assets/editor/ckeditor5/translations/ast.js create mode 100644 assets/editor/ckeditor5/translations/ast.umd.js create mode 100644 assets/editor/ckeditor5/translations/az.d.ts create mode 100644 assets/editor/ckeditor5/translations/az.js create mode 100644 assets/editor/ckeditor5/translations/az.umd.js create mode 100644 assets/editor/ckeditor5/translations/be.d.ts create mode 100644 assets/editor/ckeditor5/translations/be.js create mode 100644 assets/editor/ckeditor5/translations/be.umd.js create mode 100644 assets/editor/ckeditor5/translations/bg.d.ts create mode 100644 assets/editor/ckeditor5/translations/bg.js create mode 100644 assets/editor/ckeditor5/translations/bg.umd.js create mode 100644 assets/editor/ckeditor5/translations/bn.d.ts create mode 100644 assets/editor/ckeditor5/translations/bn.js create mode 100644 assets/editor/ckeditor5/translations/bn.umd.js create mode 100644 assets/editor/ckeditor5/translations/bs.d.ts create mode 100644 assets/editor/ckeditor5/translations/bs.js create mode 100644 assets/editor/ckeditor5/translations/bs.umd.js create mode 100644 assets/editor/ckeditor5/translations/ca.d.ts create mode 100644 assets/editor/ckeditor5/translations/ca.js create mode 100644 assets/editor/ckeditor5/translations/ca.umd.js create mode 100644 assets/editor/ckeditor5/translations/cs.d.ts create mode 100644 assets/editor/ckeditor5/translations/cs.js create mode 100644 assets/editor/ckeditor5/translations/cs.umd.js create mode 100644 assets/editor/ckeditor5/translations/da.d.ts create mode 100644 assets/editor/ckeditor5/translations/da.js create mode 100644 assets/editor/ckeditor5/translations/da.umd.js create mode 100644 assets/editor/ckeditor5/translations/de-ch.d.ts create mode 100644 assets/editor/ckeditor5/translations/de-ch.js create mode 100644 assets/editor/ckeditor5/translations/de-ch.umd.js create mode 100644 assets/editor/ckeditor5/translations/de.d.ts create mode 100644 assets/editor/ckeditor5/translations/de.js create mode 100644 assets/editor/ckeditor5/translations/de.umd.js create mode 100644 assets/editor/ckeditor5/translations/el.d.ts create mode 100644 assets/editor/ckeditor5/translations/el.js create mode 100644 assets/editor/ckeditor5/translations/el.umd.js create mode 100644 assets/editor/ckeditor5/translations/en-au.d.ts create mode 100644 assets/editor/ckeditor5/translations/en-au.js create mode 100644 assets/editor/ckeditor5/translations/en-au.umd.js create mode 100644 assets/editor/ckeditor5/translations/en-gb.d.ts create mode 100644 assets/editor/ckeditor5/translations/en-gb.js create mode 100644 assets/editor/ckeditor5/translations/en-gb.umd.js create mode 100644 assets/editor/ckeditor5/translations/en.d.ts create mode 100644 assets/editor/ckeditor5/translations/en.js create mode 100644 assets/editor/ckeditor5/translations/en.umd.js create mode 100644 assets/editor/ckeditor5/translations/eo.d.ts create mode 100644 assets/editor/ckeditor5/translations/eo.js create mode 100644 assets/editor/ckeditor5/translations/eo.umd.js create mode 100644 assets/editor/ckeditor5/translations/es-co.d.ts create mode 100644 assets/editor/ckeditor5/translations/es-co.js create mode 100644 assets/editor/ckeditor5/translations/es-co.umd.js create mode 100644 assets/editor/ckeditor5/translations/es.d.ts create mode 100644 assets/editor/ckeditor5/translations/es.js create mode 100644 assets/editor/ckeditor5/translations/es.umd.js create mode 100644 assets/editor/ckeditor5/translations/et.d.ts create mode 100644 assets/editor/ckeditor5/translations/et.js create mode 100644 assets/editor/ckeditor5/translations/et.umd.js create mode 100644 assets/editor/ckeditor5/translations/eu.d.ts create mode 100644 assets/editor/ckeditor5/translations/eu.js create mode 100644 assets/editor/ckeditor5/translations/eu.umd.js create mode 100644 assets/editor/ckeditor5/translations/fa.d.ts create mode 100644 assets/editor/ckeditor5/translations/fa.js create mode 100644 assets/editor/ckeditor5/translations/fa.umd.js create mode 100644 assets/editor/ckeditor5/translations/fi.d.ts create mode 100644 assets/editor/ckeditor5/translations/fi.js create mode 100644 assets/editor/ckeditor5/translations/fi.umd.js create mode 100644 assets/editor/ckeditor5/translations/fr.d.ts create mode 100644 assets/editor/ckeditor5/translations/fr.js create mode 100644 assets/editor/ckeditor5/translations/fr.umd.js create mode 100644 assets/editor/ckeditor5/translations/gl.d.ts create mode 100644 assets/editor/ckeditor5/translations/gl.js create mode 100644 assets/editor/ckeditor5/translations/gl.umd.js create mode 100644 assets/editor/ckeditor5/translations/gu.d.ts create mode 100644 assets/editor/ckeditor5/translations/gu.js create mode 100644 assets/editor/ckeditor5/translations/gu.umd.js create mode 100644 assets/editor/ckeditor5/translations/he.d.ts create mode 100644 assets/editor/ckeditor5/translations/he.js create mode 100644 assets/editor/ckeditor5/translations/he.umd.js create mode 100644 assets/editor/ckeditor5/translations/hi.d.ts create mode 100644 assets/editor/ckeditor5/translations/hi.js create mode 100644 assets/editor/ckeditor5/translations/hi.umd.js create mode 100644 assets/editor/ckeditor5/translations/hr.d.ts create mode 100644 assets/editor/ckeditor5/translations/hr.js create mode 100644 assets/editor/ckeditor5/translations/hr.umd.js create mode 100644 assets/editor/ckeditor5/translations/hu.d.ts create mode 100644 assets/editor/ckeditor5/translations/hu.js create mode 100644 assets/editor/ckeditor5/translations/hu.umd.js create mode 100644 assets/editor/ckeditor5/translations/hy.d.ts create mode 100644 assets/editor/ckeditor5/translations/hy.js create mode 100644 assets/editor/ckeditor5/translations/hy.umd.js create mode 100644 assets/editor/ckeditor5/translations/id.d.ts create mode 100644 assets/editor/ckeditor5/translations/id.js create mode 100644 assets/editor/ckeditor5/translations/id.umd.js create mode 100644 assets/editor/ckeditor5/translations/it.d.ts create mode 100644 assets/editor/ckeditor5/translations/it.js create mode 100644 assets/editor/ckeditor5/translations/it.umd.js create mode 100644 assets/editor/ckeditor5/translations/ja.d.ts create mode 100644 assets/editor/ckeditor5/translations/ja.js create mode 100644 assets/editor/ckeditor5/translations/ja.umd.js create mode 100644 assets/editor/ckeditor5/translations/jv.d.ts create mode 100644 assets/editor/ckeditor5/translations/jv.js create mode 100644 assets/editor/ckeditor5/translations/jv.umd.js create mode 100644 assets/editor/ckeditor5/translations/kk.d.ts create mode 100644 assets/editor/ckeditor5/translations/kk.js create mode 100644 assets/editor/ckeditor5/translations/kk.umd.js create mode 100644 assets/editor/ckeditor5/translations/km.d.ts create mode 100644 assets/editor/ckeditor5/translations/km.js create mode 100644 assets/editor/ckeditor5/translations/km.umd.js create mode 100644 assets/editor/ckeditor5/translations/kn.d.ts create mode 100644 assets/editor/ckeditor5/translations/kn.js create mode 100644 assets/editor/ckeditor5/translations/kn.umd.js create mode 100644 assets/editor/ckeditor5/translations/ko.d.ts create mode 100644 assets/editor/ckeditor5/translations/ko.js create mode 100644 assets/editor/ckeditor5/translations/ko.umd.js create mode 100644 assets/editor/ckeditor5/translations/ku.d.ts create mode 100644 assets/editor/ckeditor5/translations/ku.js create mode 100644 assets/editor/ckeditor5/translations/ku.umd.js create mode 100644 assets/editor/ckeditor5/translations/lt.d.ts create mode 100644 assets/editor/ckeditor5/translations/lt.js create mode 100644 assets/editor/ckeditor5/translations/lt.umd.js create mode 100644 assets/editor/ckeditor5/translations/lv.d.ts create mode 100644 assets/editor/ckeditor5/translations/lv.js create mode 100644 assets/editor/ckeditor5/translations/lv.umd.js create mode 100644 assets/editor/ckeditor5/translations/ms.d.ts create mode 100644 assets/editor/ckeditor5/translations/ms.js create mode 100644 assets/editor/ckeditor5/translations/ms.umd.js create mode 100644 assets/editor/ckeditor5/translations/nb.d.ts create mode 100644 assets/editor/ckeditor5/translations/nb.js create mode 100644 assets/editor/ckeditor5/translations/nb.umd.js create mode 100644 assets/editor/ckeditor5/translations/ne.d.ts create mode 100644 assets/editor/ckeditor5/translations/ne.js create mode 100644 assets/editor/ckeditor5/translations/ne.umd.js create mode 100644 assets/editor/ckeditor5/translations/nl.d.ts create mode 100644 assets/editor/ckeditor5/translations/nl.js create mode 100644 assets/editor/ckeditor5/translations/nl.umd.js create mode 100644 assets/editor/ckeditor5/translations/no.d.ts create mode 100644 assets/editor/ckeditor5/translations/no.js create mode 100644 assets/editor/ckeditor5/translations/no.umd.js create mode 100644 assets/editor/ckeditor5/translations/oc.d.ts create mode 100644 assets/editor/ckeditor5/translations/oc.js create mode 100644 assets/editor/ckeditor5/translations/oc.umd.js create mode 100644 assets/editor/ckeditor5/translations/pl.d.ts create mode 100644 assets/editor/ckeditor5/translations/pl.js create mode 100644 assets/editor/ckeditor5/translations/pl.umd.js create mode 100644 assets/editor/ckeditor5/translations/pt-br.d.ts create mode 100644 assets/editor/ckeditor5/translations/pt-br.js create mode 100644 assets/editor/ckeditor5/translations/pt-br.umd.js create mode 100644 assets/editor/ckeditor5/translations/pt.d.ts create mode 100644 assets/editor/ckeditor5/translations/pt.js create mode 100644 assets/editor/ckeditor5/translations/pt.umd.js create mode 100644 assets/editor/ckeditor5/translations/ro.d.ts create mode 100644 assets/editor/ckeditor5/translations/ro.js create mode 100644 assets/editor/ckeditor5/translations/ro.umd.js create mode 100644 assets/editor/ckeditor5/translations/ru.d.ts create mode 100644 assets/editor/ckeditor5/translations/ru.js create mode 100644 assets/editor/ckeditor5/translations/ru.umd.js create mode 100644 assets/editor/ckeditor5/translations/si.d.ts create mode 100644 assets/editor/ckeditor5/translations/si.js create mode 100644 assets/editor/ckeditor5/translations/si.umd.js create mode 100644 assets/editor/ckeditor5/translations/sk.d.ts create mode 100644 assets/editor/ckeditor5/translations/sk.js create mode 100644 assets/editor/ckeditor5/translations/sk.umd.js create mode 100644 assets/editor/ckeditor5/translations/sl.d.ts create mode 100644 assets/editor/ckeditor5/translations/sl.js create mode 100644 assets/editor/ckeditor5/translations/sl.umd.js create mode 100644 assets/editor/ckeditor5/translations/sq.d.ts create mode 100644 assets/editor/ckeditor5/translations/sq.js create mode 100644 assets/editor/ckeditor5/translations/sq.umd.js create mode 100644 assets/editor/ckeditor5/translations/sr-latn.d.ts create mode 100644 assets/editor/ckeditor5/translations/sr-latn.js create mode 100644 assets/editor/ckeditor5/translations/sr-latn.umd.js create mode 100644 assets/editor/ckeditor5/translations/sr.d.ts create mode 100644 assets/editor/ckeditor5/translations/sr.js create mode 100644 assets/editor/ckeditor5/translations/sr.umd.js create mode 100644 assets/editor/ckeditor5/translations/sv.d.ts create mode 100644 assets/editor/ckeditor5/translations/sv.js create mode 100644 assets/editor/ckeditor5/translations/sv.umd.js create mode 100644 assets/editor/ckeditor5/translations/th.d.ts create mode 100644 assets/editor/ckeditor5/translations/th.js create mode 100644 assets/editor/ckeditor5/translations/th.umd.js create mode 100644 assets/editor/ckeditor5/translations/ti.d.ts create mode 100644 assets/editor/ckeditor5/translations/ti.js create mode 100644 assets/editor/ckeditor5/translations/ti.umd.js create mode 100644 assets/editor/ckeditor5/translations/tk.d.ts create mode 100644 assets/editor/ckeditor5/translations/tk.js create mode 100644 assets/editor/ckeditor5/translations/tk.umd.js create mode 100644 assets/editor/ckeditor5/translations/tr.d.ts create mode 100644 assets/editor/ckeditor5/translations/tr.js create mode 100644 assets/editor/ckeditor5/translations/tr.umd.js create mode 100644 assets/editor/ckeditor5/translations/tt.d.ts create mode 100644 assets/editor/ckeditor5/translations/tt.js create mode 100644 assets/editor/ckeditor5/translations/tt.umd.js create mode 100644 assets/editor/ckeditor5/translations/ug.d.ts create mode 100644 assets/editor/ckeditor5/translations/ug.js create mode 100644 assets/editor/ckeditor5/translations/ug.umd.js create mode 100644 assets/editor/ckeditor5/translations/uk.d.ts create mode 100644 assets/editor/ckeditor5/translations/uk.js create mode 100644 assets/editor/ckeditor5/translations/uk.umd.js create mode 100644 assets/editor/ckeditor5/translations/ur.d.ts create mode 100644 assets/editor/ckeditor5/translations/ur.js create mode 100644 assets/editor/ckeditor5/translations/ur.umd.js create mode 100644 assets/editor/ckeditor5/translations/uz.d.ts create mode 100644 assets/editor/ckeditor5/translations/uz.js create mode 100644 assets/editor/ckeditor5/translations/uz.umd.js create mode 100644 assets/editor/ckeditor5/translations/vi.d.ts create mode 100644 assets/editor/ckeditor5/translations/vi.js create mode 100644 assets/editor/ckeditor5/translations/vi.umd.js create mode 100644 assets/editor/ckeditor5/translations/zh-cn.d.ts create mode 100644 assets/editor/ckeditor5/translations/zh-cn.js create mode 100644 assets/editor/ckeditor5/translations/zh-cn.umd.js create mode 100644 assets/editor/ckeditor5/translations/zh.d.ts create mode 100644 assets/editor/ckeditor5/translations/zh.js create mode 100644 assets/editor/ckeditor5/translations/zh.umd.js create mode 100644 assets/editor/index.html create mode 100644 assets/editor/main.js create mode 100644 assets/editor/style.css mode change 100644 => 100755 assets/js/editor.js create mode 100755 assets/js/quill.js create mode 100755 uploads/avatars/avatar_1_1764119487.jpg mode change 100644 => 100755 uploads/covers/cover_2_1764062021.jpg mode change 100644 => 100755 uploads/covers/cover_3_1764051570.jpg mode change 100644 => 100755 views/series/index.php diff --git a/333.txt b/333.txt old mode 100644 new mode 100755 index 5df903d..d6bc3f5 --- a/333.txt +++ b/333.txt @@ -428,6 +428,7 @@ use TCPDF; class ExportController extends BaseController { public function export($book_id, $format = 'pdf') { + $this->requireLogin(); $user_id = $_SESSION['user_id']; @@ -461,7 +462,7 @@ class ExportController extends BaseController { } // Для публичного доступа - только опубликованные главы - $chapters = $bookModel->getPublishedChapters($book['id']); + $chapters = $chapterModel->getPublishedChapters($book['id']); // Получаем информацию об авторе $author_name = $this->getAuthorName($book['user_id']); @@ -1594,6 +1595,8 @@ class BookController extends BaseController { $this->requireLogin(); $seriesModel = new Series($this->pdo); $series = $seriesModel->findByUser($_SESSION['user_id']); + $error = ''; + $cover_error = ''; if ($_SERVER['REQUEST_METHOD'] === 'POST') { if (!verify_csrf_token($_POST['csrf_token'] ?? '')) { @@ -1619,16 +1622,32 @@ class BookController extends BaseController { ]; if ($bookModel->create($data)) { - $_SESSION['success'] = "Книга успешно создана"; $new_book_id = $this->pdo->lastInsertId(); - $this->redirect("/books/{$new_book_id}/edit"); + + // Обработка загрузки обложки + if (isset($_FILES['cover_image']) && $_FILES['cover_image']['error'] === UPLOAD_ERR_OK) { + $cover_result = handleCoverUpload($_FILES['cover_image'], $new_book_id); + if ($cover_result['success']) { + $bookModel->updateCover($new_book_id, $cover_result['filename']); + } else { + $cover_error = $cover_result['error']; + // Сохраняем ошибку в сессии, чтобы показать после редиректа + $_SESSION['cover_error'] = $cover_error; + } + } + + $_SESSION['success'] = "Книга успешно создана" . ($cover_error ? ", но возникла ошибка с обложкой: " . $cover_error : ""); + $this->redirect("/books/{$new_book_id}/edit"); } else { $_SESSION['error'] = "Ошибка при создании книги"; } + } $this->render('books/create', [ 'series' => $series, + 'error' => $error, + 'cover_error' => $cover_error, 'page_title' => 'Создание новой книги' ]); } @@ -1800,13 +1819,14 @@ class BookController extends BaseController { public function viewPublic($share_token) { $bookModel = new Book($this->pdo); + $chapterModel = new Chapter($this->pdo); $book = $bookModel->findByShareToken($share_token); if (!$book) { http_response_code(404); $this->render('errors/404'); return; } - $chapters = $bookModel->getPublishedChapters($book['id']); + $chapters = $chapterModel->getPublishedChapters($book['id']); // Получаем информацию об авторе $stmt = $this->pdo->prepare("SELECT id, username, display_name FROM users WHERE id = ?"); @@ -1821,6 +1841,29 @@ class BookController extends BaseController { ]); } + public function viewAll($id) { + $bookModel = new Book($this->pdo); + $chapterModel = new Chapter($this->pdo); + $book = $bookModel->findById($id); + if (!$book) { + http_response_code(404); + $this->render('errors/404'); + return; + } + $chapters = $chapterModel->findByBook($book['id']); + + // Получаем информацию об авторе + $stmt = $this->pdo->prepare("SELECT id, username, display_name FROM users WHERE id = ?"); + $stmt->execute([$book['user_id']]); + $author = $stmt->fetch(PDO::FETCH_ASSOC); + + $this->render('books/view_public', [ + 'book' => $book, + 'chapters' => $chapters, + 'author' => $author, + 'page_title' => $book['title'] + ]); + } public function regenerateToken($id) { $this->requireLogin(); @@ -2097,6 +2140,33 @@ class Chapter { return $stmt->fetch() !== false; } + + public function getPublishedChapters($book_id) { + $stmt = $this->pdo->prepare(" + SELECT * FROM chapters + WHERE book_id = ? AND status = 'published' + ORDER BY sort_order, created_at + "); + $stmt->execute([$book_id]); + return $stmt->fetchAll(PDO::FETCH_ASSOC); + } + + // private function getAllChapters($book_id) { + // $stmt = $this->pdo->prepare("SELECT id, content FROM chapters WHERE book_id = ?"); + // $stmt->execute([$book_id]); + // return $stmt->fetchAll(PDO::FETCH_ASSOC); + // } + + // private function updateChapterContent($chapter_id, $content) { + // $word_count = $this->countWords($content); + // $stmt = $this->pdo->prepare(" + // UPDATE chapters + // SET content = ?, word_count = ?, updated_at = CURRENT_TIMESTAMP + // WHERE id = ? + // "); + // return $stmt->execute([$content, $word_count, $chapter_id]); + // } + } ?> // ./models/Series.php @@ -2531,15 +2601,7 @@ class Book { return $success ? $new_token : false; } - public function getPublishedChapters($book_id) { - $stmt = $this->pdo->prepare(" - SELECT * FROM chapters - WHERE book_id = ? AND status = 'published' - ORDER BY sort_order, created_at - "); - $stmt->execute([$book_id]); - return $stmt->fetchAll(PDO::FETCH_ASSOC); - } + public function updateCover($book_id, $filename) { $stmt = $this->pdo->prepare("UPDATE books SET cover_image = ? WHERE id = ?"); @@ -2601,24 +2663,7 @@ class Book { $stmt->execute([$book_id]); return $stmt->fetch(PDO::FETCH_ASSOC); } - - - private function getAllChapters($book_id) { - $stmt = $this->pdo->prepare("SELECT id, content FROM chapters WHERE book_id = ?"); - $stmt->execute([$book_id]); - return $stmt->fetchAll(PDO::FETCH_ASSOC); - } - - private function updateChapterContent($chapter_id, $content) { - $word_count = $this->countWords($content); - $stmt = $this->pdo->prepare(" - UPDATE chapters - SET content = ?, word_count = ?, updated_at = CURRENT_TIMESTAMP - WHERE id = ? - "); - return $stmt->execute([$content, $word_count, $chapter_id]); - } public function getBooksNotInSeries($user_id, $series_id = null) { $sql = "SELECT * FROM books WHERE user_id = ? AND (series_id IS NULL OR series_id = ?)"; @@ -2777,6 +2822,7 @@ $router->add('/register', 'AuthController@register'); // Книги $router->add('/books', 'BookController@index'); +$router->add('/book/all/{id}', 'BookController@viewAll'); $router->add('/books/create', 'BookController@create'); $router->add('/books/{id}/edit', 'BookController@edit'); $router->add('/books/{id}/delete', 'BookController@delete'); @@ -3579,11 +3625,91 @@ function deleteUserAvatar($user_id) { Приложение распространяется под лицензией [AGPLv3](https://www.gnu.org/licenses/agpl-3.0.html). // ./assets/index.php +// ./assets/css/quill_reset.css +/* Увеличиваем специфичность для кнопок Quill */ +.ql-toolbar .ql-picker-label, +.ql-toolbar button, +.ql-toolbar [role="button"] { + all: unset; /* Сбрасываем все стили Pico (background, border, padding и т.д.) */ + display: inline-block; /* Восстанавливаем базовые стили Quill */ + cursor: pointer; + padding: 0; /* Quill кнопки не имеют padding */ + margin: 0; + border: none; + background: none; + color: inherit; /* Наследуем цвет от Quill */ + font-size: inherit; + line-height: inherit; + text-decoration: none; /* Убираем подчёркивание, если это */ +} + +/* Восстанавливаем hover/active стили Quill (если они сломались) */ +.ql-toolbar button:hover, +.ql-toolbar [role="button"]:hover { + color: #06c; /* Пример из Quill snow theme; адаптируйте */ + background: none; /* Без фона */ +} + +.ql-toolbar button.ql-active, +.ql-toolbar [role="button"].ql-active { + color: #06c; + background: none; +} + +/* Для иконок (SVG в Quill) */ +.ql-toolbar .ql-icon { + fill: currentColor; /* Убедимся, что иконки наследуют цвет */ +} + +/* Если Quill использует для кнопок */ +.ql-toolbar .ql-picker-item, +.ql-toolbar .ql-picker-options [role="button"] { + all: unset; + cursor: pointer; +} +// ./assets/css/quill.snow.css +/*! + * Quill Editor v2.0.3 + * https://quilljs.com + * Copyright (c) 2017-2024, Slab + * Copyright (c) 2014, Jason Chen + * Copyright (c) 2013, salesforce.com + */ +.ql-container{box-sizing:border-box;font-family:Helvetica,Arial,sans-serif;font-size:13px;height:100%;margin:0;position:relative}.ql-container.ql-disabled .ql-tooltip{visibility:hidden}.ql-container:not(.ql-disabled) li[data-list=checked] > .ql-ui,.ql-container:not(.ql-disabled) li[data-list=unchecked] > .ql-ui{cursor:pointer}.ql-clipboard{left:-100000px;height:1px;overflow-y:hidden;position:absolute;top:50%}.ql-clipboard p{margin:0;padding:0}.ql-editor{box-sizing:border-box;counter-reset:list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9;line-height:1.42;height:100%;outline:none;overflow-y:auto;padding:12px 15px;tab-size:4;-moz-tab-size:4;text-align:left;white-space:pre-wrap;word-wrap:break-word}.ql-editor > *{cursor:text}.ql-editor p,.ql-editor ol,.ql-editor pre,.ql-editor blockquote,.ql-editor h1,.ql-editor h2,.ql-editor h3,.ql-editor h4,.ql-editor h5,.ql-editor h6{margin:0;padding:0}@supports (counter-set:none){.ql-editor p,.ql-editor h1,.ql-editor h2,.ql-editor h3,.ql-editor h4,.ql-editor h5,.ql-editor h6{counter-set:list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor p,.ql-editor h1,.ql-editor h2,.ql-editor h3,.ql-editor h4,.ql-editor h5,.ql-editor h6{counter-reset:list-0 list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}}.ql-editor table{border-collapse:collapse}.ql-editor td{border:1px solid #000;padding:2px 5px}.ql-editor ol{padding-left:1.5em}.ql-editor li{list-style-type:none;padding-left:1.5em;position:relative}.ql-editor li > .ql-ui:before{display:inline-block;margin-left:-1.5em;margin-right:.3em;text-align:right;white-space:nowrap;width:1.2em}.ql-editor li[data-list=checked] > .ql-ui,.ql-editor li[data-list=unchecked] > .ql-ui{color:#777}.ql-editor li[data-list=bullet] > .ql-ui:before{content:'\2022'}.ql-editor li[data-list=checked] > .ql-ui:before{content:'\2611'}.ql-editor li[data-list=unchecked] > .ql-ui:before{content:'\2610'}@supports (counter-set:none){.ql-editor li[data-list]{counter-set:list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list]{counter-reset:list-1 list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}}.ql-editor li[data-list=ordered]{counter-increment:list-0}.ql-editor li[data-list=ordered] > .ql-ui:before{content:counter(list-0, decimal) '. '}.ql-editor li[data-list=ordered].ql-indent-1{counter-increment:list-1}.ql-editor li[data-list=ordered].ql-indent-1 > .ql-ui:before{content:counter(list-1, lower-alpha) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-1{counter-set:list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-1{counter-reset:list-2 list-3 list-4 list-5 list-6 list-7 list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-2{counter-increment:list-2}.ql-editor li[data-list=ordered].ql-indent-2 > .ql-ui:before{content:counter(list-2, lower-roman) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-2{counter-set:list-3 list-4 list-5 list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-2{counter-reset:list-3 list-4 list-5 list-6 list-7 list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-3{counter-increment:list-3}.ql-editor li[data-list=ordered].ql-indent-3 > .ql-ui:before{content:counter(list-3, decimal) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-3{counter-set:list-4 list-5 list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-3{counter-reset:list-4 list-5 list-6 list-7 list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-4{counter-increment:list-4}.ql-editor li[data-list=ordered].ql-indent-4 > .ql-ui:before{content:counter(list-4, lower-alpha) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-4{counter-set:list-5 list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-4{counter-reset:list-5 list-6 list-7 list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-5{counter-increment:list-5}.ql-editor li[data-list=ordered].ql-indent-5 > .ql-ui:before{content:counter(list-5, lower-roman) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-5{counter-set:list-6 list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-5{counter-reset:list-6 list-7 list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-6{counter-increment:list-6}.ql-editor li[data-list=ordered].ql-indent-6 > .ql-ui:before{content:counter(list-6, decimal) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-6{counter-set:list-7 list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-6{counter-reset:list-7 list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-7{counter-increment:list-7}.ql-editor li[data-list=ordered].ql-indent-7 > .ql-ui:before{content:counter(list-7, lower-alpha) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-7{counter-set:list-8 list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-7{counter-reset:list-8 list-9}}.ql-editor li[data-list=ordered].ql-indent-8{counter-increment:list-8}.ql-editor li[data-list=ordered].ql-indent-8 > .ql-ui:before{content:counter(list-8, lower-roman) '. '}@supports (counter-set:none){.ql-editor li[data-list].ql-indent-8{counter-set:list-9}}@supports not (counter-set:none){.ql-editor li[data-list].ql-indent-8{counter-reset:list-9}}.ql-editor li[data-list=ordered].ql-indent-9{counter-increment:list-9}.ql-editor li[data-list=ordered].ql-indent-9 > .ql-ui:before{content:counter(list-9, decimal) '. '}.ql-editor .ql-indent-1:not(.ql-direction-rtl){padding-left:3em}.ql-editor li.ql-indent-1:not(.ql-direction-rtl){padding-left:4.5em}.ql-editor .ql-indent-1.ql-direction-rtl.ql-align-right{padding-right:3em}.ql-editor li.ql-indent-1.ql-direction-rtl.ql-align-right{padding-right:4.5em}.ql-editor .ql-indent-2:not(.ql-direction-rtl){padding-left:6em}.ql-editor li.ql-indent-2:not(.ql-direction-rtl){padding-left:7.5em}.ql-editor .ql-indent-2.ql-direction-rtl.ql-align-right{padding-right:6em}.ql-editor li.ql-indent-2.ql-direction-rtl.ql-align-right{padding-right:7.5em}.ql-editor .ql-indent-3:not(.ql-direction-rtl){padding-left:9em}.ql-editor li.ql-indent-3:not(.ql-direction-rtl){padding-left:10.5em}.ql-editor .ql-indent-3.ql-direction-rtl.ql-align-right{padding-right:9em}.ql-editor li.ql-indent-3.ql-direction-rtl.ql-align-right{padding-right:10.5em}.ql-editor .ql-indent-4:not(.ql-direction-rtl){padding-left:12em}.ql-editor li.ql-indent-4:not(.ql-direction-rtl){padding-left:13.5em}.ql-editor .ql-indent-4.ql-direction-rtl.ql-align-right{padding-right:12em}.ql-editor li.ql-indent-4.ql-direction-rtl.ql-align-right{padding-right:13.5em}.ql-editor .ql-indent-5:not(.ql-direction-rtl){padding-left:15em}.ql-editor li.ql-indent-5:not(.ql-direction-rtl){padding-left:16.5em}.ql-editor .ql-indent-5.ql-direction-rtl.ql-align-right{padding-right:15em}.ql-editor li.ql-indent-5.ql-direction-rtl.ql-align-right{padding-right:16.5em}.ql-editor .ql-indent-6:not(.ql-direction-rtl){padding-left:18em}.ql-editor li.ql-indent-6:not(.ql-direction-rtl){padding-left:19.5em}.ql-editor .ql-indent-6.ql-direction-rtl.ql-align-right{padding-right:18em}.ql-editor li.ql-indent-6.ql-direction-rtl.ql-align-right{padding-right:19.5em}.ql-editor .ql-indent-7:not(.ql-direction-rtl){padding-left:21em}.ql-editor li.ql-indent-7:not(.ql-direction-rtl){padding-left:22.5em}.ql-editor .ql-indent-7.ql-direction-rtl.ql-align-right{padding-right:21em}.ql-editor li.ql-indent-7.ql-direction-rtl.ql-align-right{padding-right:22.5em}.ql-editor .ql-indent-8:not(.ql-direction-rtl){padding-left:24em}.ql-editor li.ql-indent-8:not(.ql-direction-rtl){padding-left:25.5em}.ql-editor .ql-indent-8.ql-direction-rtl.ql-align-right{padding-right:24em}.ql-editor li.ql-indent-8.ql-direction-rtl.ql-align-right{padding-right:25.5em}.ql-editor .ql-indent-9:not(.ql-direction-rtl){padding-left:27em}.ql-editor li.ql-indent-9:not(.ql-direction-rtl){padding-left:28.5em}.ql-editor .ql-indent-9.ql-direction-rtl.ql-align-right{padding-right:27em}.ql-editor li.ql-indent-9.ql-direction-rtl.ql-align-right{padding-right:28.5em}.ql-editor li.ql-direction-rtl{padding-right:1.5em}.ql-editor li.ql-direction-rtl > .ql-ui:before{margin-left:.3em;margin-right:-1.5em;text-align:left}.ql-editor table{table-layout:fixed;width:100%}.ql-editor table td{outline:none}.ql-editor .ql-code-block-container{font-family:monospace}.ql-editor .ql-video{display:block;max-width:100%}.ql-editor .ql-video.ql-align-center{margin:0 auto}.ql-editor .ql-video.ql-align-right{margin:0 0 0 auto}.ql-editor .ql-bg-black{background-color:#000}.ql-editor .ql-bg-red{background-color:#e60000}.ql-editor .ql-bg-orange{background-color:#f90}.ql-editor .ql-bg-yellow{background-color:#ff0}.ql-editor .ql-bg-green{background-color:#008a00}.ql-editor .ql-bg-blue{background-color:#06c}.ql-editor .ql-bg-purple{background-color:#93f}.ql-editor .ql-color-white{color:#fff}.ql-editor .ql-color-red{color:#e60000}.ql-editor .ql-color-orange{color:#f90}.ql-editor .ql-color-yellow{color:#ff0}.ql-editor .ql-color-green{color:#008a00}.ql-editor .ql-color-blue{color:#06c}.ql-editor .ql-color-purple{color:#93f}.ql-editor .ql-font-serif{font-family:Georgia,Times New Roman,serif}.ql-editor .ql-font-monospace{font-family:Monaco,Courier New,monospace}.ql-editor .ql-size-small{font-size:.75em}.ql-editor .ql-size-large{font-size:1.5em}.ql-editor .ql-size-huge{font-size:2.5em}.ql-editor .ql-direction-rtl{direction:rtl;text-align:inherit}.ql-editor .ql-align-center{text-align:center}.ql-editor .ql-align-justify{text-align:justify}.ql-editor .ql-align-right{text-align:right}.ql-editor .ql-ui{position:absolute}.ql-editor.ql-blank::before{color:rgba(0,0,0,0.6);content:attr(data-placeholder);font-style:italic;left:15px;pointer-events:none;position:absolute;right:15px}.ql-snow.ql-toolbar:after,.ql-snow .ql-toolbar:after{clear:both;content:'';display:table}.ql-snow.ql-toolbar button,.ql-snow .ql-toolbar button{background:none;border:none;cursor:pointer;display:inline-block;float:left;height:24px;padding:3px 5px;width:28px}.ql-snow.ql-toolbar button svg,.ql-snow .ql-toolbar button svg{float:left;height:100%}.ql-snow.ql-toolbar button:active:hover,.ql-snow .ql-toolbar button:active:hover{outline:none}.ql-snow.ql-toolbar input.ql-image[type=file],.ql-snow .ql-toolbar input.ql-image[type=file]{display:none}.ql-snow.ql-toolbar button:hover,.ql-snow .ql-toolbar button:hover,.ql-snow.ql-toolbar button:focus,.ql-snow .ql-toolbar button:focus,.ql-snow.ql-toolbar button.ql-active,.ql-snow .ql-toolbar button.ql-active,.ql-snow.ql-toolbar .ql-picker-label:hover,.ql-snow .ql-toolbar .ql-picker-label:hover,.ql-snow.ql-toolbar .ql-picker-label.ql-active,.ql-snow .ql-toolbar .ql-picker-label.ql-active,.ql-snow.ql-toolbar .ql-picker-item:hover,.ql-snow .ql-toolbar .ql-picker-item:hover,.ql-snow.ql-toolbar .ql-picker-item.ql-selected,.ql-snow .ql-toolbar .ql-picker-item.ql-selected{color:#06c}.ql-snow.ql-toolbar button:hover .ql-fill,.ql-snow .ql-toolbar button:hover .ql-fill,.ql-snow.ql-toolbar button:focus .ql-fill,.ql-snow .ql-toolbar button:focus .ql-fill,.ql-snow.ql-toolbar button.ql-active .ql-fill,.ql-snow .ql-toolbar button.ql-active .ql-fill,.ql-snow.ql-toolbar .ql-picker-label:hover .ql-fill,.ql-snow .ql-toolbar .ql-picker-label:hover .ql-fill,.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-fill,.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-fill,.ql-snow.ql-toolbar .ql-picker-item:hover .ql-fill,.ql-snow .ql-toolbar .ql-picker-item:hover .ql-fill,.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-fill,.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-fill,.ql-snow.ql-toolbar button:hover .ql-stroke.ql-fill,.ql-snow .ql-toolbar button:hover .ql-stroke.ql-fill,.ql-snow.ql-toolbar button:focus .ql-stroke.ql-fill,.ql-snow .ql-toolbar button:focus .ql-stroke.ql-fill,.ql-snow.ql-toolbar button.ql-active .ql-stroke.ql-fill,.ql-snow .ql-toolbar button.ql-active .ql-stroke.ql-fill,.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke.ql-fill,.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke.ql-fill,.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke.ql-fill,.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill,.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke.ql-fill{fill:#06c}.ql-snow.ql-toolbar button:hover .ql-stroke,.ql-snow .ql-toolbar button:hover .ql-stroke,.ql-snow.ql-toolbar button:focus .ql-stroke,.ql-snow .ql-toolbar button:focus .ql-stroke,.ql-snow.ql-toolbar button.ql-active .ql-stroke,.ql-snow .ql-toolbar button.ql-active .ql-stroke,.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke,.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke,.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke,.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke,.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke,.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke,.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke,.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke,.ql-snow.ql-toolbar button:hover .ql-stroke-miter,.ql-snow .ql-toolbar button:hover .ql-stroke-miter,.ql-snow.ql-toolbar button:focus .ql-stroke-miter,.ql-snow .ql-toolbar button:focus .ql-stroke-miter,.ql-snow.ql-toolbar button.ql-active .ql-stroke-miter,.ql-snow .ql-toolbar button.ql-active .ql-stroke-miter,.ql-snow.ql-toolbar .ql-picker-label:hover .ql-stroke-miter,.ql-snow .ql-toolbar .ql-picker-label:hover .ql-stroke-miter,.ql-snow.ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,.ql-snow .ql-toolbar .ql-picker-label.ql-active .ql-stroke-miter,.ql-snow.ql-toolbar .ql-picker-item:hover .ql-stroke-miter,.ql-snow .ql-toolbar .ql-picker-item:hover .ql-stroke-miter,.ql-snow.ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter,.ql-snow .ql-toolbar .ql-picker-item.ql-selected .ql-stroke-miter{stroke:#06c}@media (pointer:coarse){.ql-snow.ql-toolbar button:hover:not(.ql-active),.ql-snow .ql-toolbar button:hover:not(.ql-active){color:#444}.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-fill,.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-fill,.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill,.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke.ql-fill{fill:#444}.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke,.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke,.ql-snow.ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter,.ql-snow .ql-toolbar button:hover:not(.ql-active) .ql-stroke-miter{stroke:#444}}.ql-snow{box-sizing:border-box}.ql-snow *{box-sizing:border-box}.ql-snow .ql-hidden{display:none}.ql-snow .ql-out-bottom,.ql-snow .ql-out-top{visibility:hidden}.ql-snow .ql-tooltip{position:absolute;transform:translateY(10px)}.ql-snow .ql-tooltip a{cursor:pointer;text-decoration:none}.ql-snow .ql-tooltip.ql-flip{transform:translateY(-10px)}.ql-snow .ql-formats{display:inline-block;vertical-align:middle}.ql-snow .ql-formats:after{clear:both;content:'';display:table}.ql-snow .ql-stroke{fill:none;stroke:#444;stroke-linecap:round;stroke-linejoin:round;stroke-width:2}.ql-snow .ql-stroke-miter{fill:none;stroke:#444;stroke-miterlimit:10;stroke-width:2}.ql-snow .ql-fill,.ql-snow .ql-stroke.ql-fill{fill:#444}.ql-snow .ql-empty{fill:none}.ql-snow .ql-even{fill-rule:evenodd}.ql-snow .ql-thin,.ql-snow .ql-stroke.ql-thin{stroke-width:1}.ql-snow .ql-transparent{opacity:.4}.ql-snow .ql-direction svg:last-child{display:none}.ql-snow .ql-direction.ql-active svg:last-child{display:inline}.ql-snow .ql-direction.ql-active svg:first-child{display:none}.ql-snow .ql-editor h1{font-size:2em}.ql-snow .ql-editor h2{font-size:1.5em}.ql-snow .ql-editor h3{font-size:1.17em}.ql-snow .ql-editor h4{font-size:1em}.ql-snow .ql-editor h5{font-size:.83em}.ql-snow .ql-editor h6{font-size:.67em}.ql-snow .ql-editor a{text-decoration:underline}.ql-snow .ql-editor blockquote{border-left:4px solid #ccc;margin-bottom:5px;margin-top:5px;padding-left:16px}.ql-snow .ql-editor code,.ql-snow .ql-editor .ql-code-block-container{background-color:#f0f0f0;border-radius:3px}.ql-snow .ql-editor .ql-code-block-container{margin-bottom:5px;margin-top:5px;padding:5px 10px}.ql-snow .ql-editor code{font-size:85%;padding:2px 4px}.ql-snow .ql-editor .ql-code-block-container{background-color:#23241f;color:#f8f8f2;overflow:visible}.ql-snow .ql-editor img{max-width:100%}.ql-snow .ql-picker{color:#444;display:inline-block;float:left;font-size:14px;font-weight:500;height:24px;position:relative;vertical-align:middle}.ql-snow .ql-picker-label{cursor:pointer;display:inline-block;height:100%;padding-left:8px;padding-right:2px;position:relative;width:100%}.ql-snow .ql-picker-label::before{display:inline-block;line-height:22px}.ql-snow .ql-picker-options{background-color:#fff;display:none;min-width:100%;padding:4px 8px;position:absolute;white-space:nowrap}.ql-snow .ql-picker-options .ql-picker-item{cursor:pointer;display:block;padding-bottom:5px;padding-top:5px}.ql-snow .ql-picker.ql-expanded .ql-picker-label{color:#ccc;z-index:2}.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-fill{fill:#ccc}.ql-snow .ql-picker.ql-expanded .ql-picker-label .ql-stroke{stroke:#ccc}.ql-snow .ql-picker.ql-expanded .ql-picker-options{display:block;margin-top:-1px;top:100%;z-index:1}.ql-snow .ql-color-picker,.ql-snow .ql-icon-picker{width:28px}.ql-snow .ql-color-picker .ql-picker-label,.ql-snow .ql-icon-picker .ql-picker-label{padding:2px 4px}.ql-snow .ql-color-picker .ql-picker-label svg,.ql-snow .ql-icon-picker .ql-picker-label svg{right:4px}.ql-snow .ql-icon-picker .ql-picker-options{padding:4px 0}.ql-snow .ql-icon-picker .ql-picker-item{height:24px;width:24px;padding:2px 4px}.ql-snow .ql-color-picker .ql-picker-options{padding:3px 5px;width:152px}.ql-snow .ql-color-picker .ql-picker-item{border:1px solid transparent;float:left;height:16px;margin:2px;padding:0;width:16px}.ql-snow .ql-picker:not(.ql-color-picker):not(.ql-icon-picker) svg{position:absolute;margin-top:-9px;right:0;top:50%;width:18px}.ql-snow .ql-picker.ql-header .ql-picker-label[data-label]:not([data-label=''])::before,.ql-snow .ql-picker.ql-font .ql-picker-label[data-label]:not([data-label=''])::before,.ql-snow .ql-picker.ql-size .ql-picker-label[data-label]:not([data-label=''])::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-label]:not([data-label=''])::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-label]:not([data-label=''])::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-label]:not([data-label=''])::before{content:attr(data-label)}.ql-snow .ql-picker.ql-header{width:98px}.ql-snow .ql-picker.ql-header .ql-picker-label::before,.ql-snow .ql-picker.ql-header .ql-picker-item::before{content:'Normal'}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before{content:'Heading 1'}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before{content:'Heading 2'}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before{content:'Heading 3'}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before{content:'Heading 4'}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before{content:'Heading 5'}.ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before{content:'Heading 6'}.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before{font-size:2em}.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before{font-size:1.5em}.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before{font-size:1.17em}.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before{font-size:1em}.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before{font-size:.83em}.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before{font-size:.67em}.ql-snow .ql-picker.ql-font{width:108px}.ql-snow .ql-picker.ql-font .ql-picker-label::before,.ql-snow .ql-picker.ql-font .ql-picker-item::before{content:'Sans Serif'}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=serif]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before{content:'Serif'}.ql-snow .ql-picker.ql-font .ql-picker-label[data-value=monospace]::before,.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before{content:'Monospace'}.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=serif]::before{font-family:Georgia,Times New Roman,serif}.ql-snow .ql-picker.ql-font .ql-picker-item[data-value=monospace]::before{font-family:Monaco,Courier New,monospace}.ql-snow .ql-picker.ql-size{width:98px}.ql-snow .ql-picker.ql-size .ql-picker-label::before,.ql-snow .ql-picker.ql-size .ql-picker-item::before{content:'Normal'}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=small]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before{content:'Small'}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=large]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before{content:'Large'}.ql-snow .ql-picker.ql-size .ql-picker-label[data-value=huge]::before,.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before{content:'Huge'}.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=small]::before{font-size:10px}.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=large]::before{font-size:18px}.ql-snow .ql-picker.ql-size .ql-picker-item[data-value=huge]::before{font-size:32px}.ql-snow .ql-color-picker.ql-background .ql-picker-item{background-color:#fff}.ql-snow .ql-color-picker.ql-color .ql-picker-item{background-color:#000}.ql-code-block-container{position:relative}.ql-code-block-container .ql-ui{right:5px;top:5px}.ql-toolbar.ql-snow{border:1px solid #ccc;box-sizing:border-box;font-family:'Helvetica Neue','Helvetica','Arial',sans-serif;padding:8px}.ql-toolbar.ql-snow .ql-formats{margin-right:15px}.ql-toolbar.ql-snow .ql-picker-label{border:1px solid transparent}.ql-toolbar.ql-snow .ql-picker-options{border:1px solid transparent;box-shadow:rgba(0,0,0,0.2) 0 2px 8px}.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-label{border-color:#ccc}.ql-toolbar.ql-snow .ql-picker.ql-expanded .ql-picker-options{border-color:#ccc}.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item.ql-selected,.ql-toolbar.ql-snow .ql-color-picker .ql-picker-item:hover{border-color:#000}.ql-toolbar.ql-snow + .ql-container.ql-snow{border-top:0}.ql-snow .ql-tooltip{background-color:#fff;border:1px solid #ccc;box-shadow:0 0 5px #ddd;color:#444;padding:5px 12px;white-space:nowrap}.ql-snow .ql-tooltip::before{content:"Visit URL:";line-height:26px;margin-right:8px}.ql-snow .ql-tooltip input[type=text]{display:none;border:1px solid #ccc;font-size:13px;height:26px;margin:0;padding:3px 5px;width:170px}.ql-snow .ql-tooltip a.ql-preview{display:inline-block;max-width:200px;overflow-x:hidden;text-overflow:ellipsis;vertical-align:top}.ql-snow .ql-tooltip a.ql-action::after{border-right:1px solid #ccc;content:'Edit';margin-left:16px;padding-right:8px}.ql-snow .ql-tooltip a.ql-remove::before{content:'Remove';margin-left:8px}.ql-snow .ql-tooltip a{line-height:26px}.ql-snow .ql-tooltip.ql-editing a.ql-preview,.ql-snow .ql-tooltip.ql-editing a.ql-remove{display:none}.ql-snow .ql-tooltip.ql-editing input[type=text]{display:inline-block}.ql-snow .ql-tooltip.ql-editing a.ql-action::after{border-right:0;content:'Save';padding-right:0}.ql-snow .ql-tooltip[data-mode=link]::before{content:"Enter link:"}.ql-snow .ql-tooltip[data-mode=formula]::before{content:"Enter formula:"}.ql-snow .ql-tooltip[data-mode=video]::before{content:"Enter video:"}.ql-snow a{color:#06c}.ql-container.ql-snow{border:1px solid #ccc} + +/*# sourceMappingURL=quill.snow.css.map*/ // ./assets/css/index.php // ./assets/css/style.css /* ===== БАЗОВЫЕ СТИЛИ ===== */ -/* Восстанавливаем центрирование контейнера */ +h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 1rem; + color: var(--color); + font-weight: var(--font-weight); + font-size: var(--font-size); + font-family: var(--font-family); +} +article{ + margin-top: 0em; +} +article > header { + margin-top: calc(var(--block-spacing-vertical) * -1); + margin-bottom: calc(var(--block-spacing-vertical)-1); + border-bottom: var(--border-width) solid var(--card-border-color); + border-top-right-radius: var(--border-radius); + border-top-left-radius: var(--border-radius); + padding-bottom: 0.5em; +} +article > footer { + margin-top: calc(var(--block-spacing-vertical)-1); + margin-bottom: calc(var(--block-spacing-vertical) * -1); + border-top: var(--border-width) solid var(--card-border-color); + border-bottom-right-radius: var(--border-radius); + border-bottom-left-radius: var(--border-radius); + padding-top: 0.5em; +} +/* Центрирование контейнера */ main.container { margin: 1rem auto; padding: 1rem 0; @@ -3722,6 +3848,17 @@ main.container { border-color: var(--secondary); color: var(--secondary-inverse); } +.red-btn { + background: #ff4444; + border-color: #ff4444; + color: white; +} + +.red-btn:hover { + background: #dd3333; + border-color: #dd3333; + color: white; +} /* ===== КНИГИ И КОНТЕНТ ===== */ .book-content { @@ -3885,35 +4022,7 @@ main.container { line-height: 1.6; } -/* Переопределение Pico CSS для Quill */ -.writer-editor-container [role="button"] { - background: none !important; - background-color: transparent !important; - border: 1px solid transparent !important; - border-radius: 3px !important; - color: #444 !important; - font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif !important; - font-size: 14px !important; - font-weight: normal !important; - text-align: center !important; - text-decoration: none !important; - text-transform: none !important; - box-shadow: none !important; - text-shadow: none !important; - transition: none !important; - padding: 3px 5px !important; - margin: 2px !important; - width: 28px !important; - height: 24px !important; - display: inline-block !important; - cursor: pointer !important; -} -.writer-editor-container [role="button"]:hover { - background-color: #f3f3f3 !important; - border-color: #ccc !important; - color: #444 !important; -} /* ===== DASHBOARD ===== */ .dashboard-buttons { display: flex; @@ -4275,335 +4384,207 @@ main.container { .series-stat-number { font-size: 1.1rem; } +} +/* ===== СТИЛИ ДЛЯ СПИСКА КНИГ ===== */ +.books-grid { + display: grid; + gap: 1.5rem; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); +} + +.book-card { + background: white; + border: 1px solid #e0e0e0; + border-radius: 8px; + overflow: hidden; + transition: all 0.3s ease; + position: relative; +} + +.book-card:hover { + transform: translateY(-2px); + box-shadow: 0 8px 25px rgba(0,0,0,0.1); + border-color: #007bff; +} + +.book-cover-container { + position: relative; + height: 200px; + overflow: hidden; + background: #f8f9fa; +} + +.book-cover { + width: 100%; + height: 100%; + object-fit: cover; + transition: transform 0.3s ease; +} + +.book-card:hover .book-cover { + transform: scale(1.05); +} + +.cover-placeholder { + width: 100%; + height: 100%; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + display: flex; + align-items: center; + justify-content: center; + color: white; + font-size: 3rem; +} + +.book-status { + position: absolute; + top: 10px; + right: 10px; + background: rgba(255, 255, 255, 0.9); + border-radius: 50%; + width: 30px; + height: 30px; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.8rem; + box-shadow: 0 2px 5px rgba(0,0,0,0.2); +} + +.book-status.published { + background: rgba(40, 167, 69, 0.9); + color: white; +} + +.book-info { + padding: 1.5rem; +} + +.book-title { + margin: 0 0 0.5rem 0; + font-size: 1.2rem; + line-height: 1.3; +} + +.book-title a { + text-decoration: none; + color: inherit; +} + +.book-title a:hover { + color: #007bff; +} + +.book-genre { + margin: 0 0 0.5rem 0; + color: #666; + font-style: italic; + font-size: 0.9rem; +} + +.book-description { + margin: 0 0 1rem 0; + color: #555; + line-height: 1.4; + font-size: 0.9rem; + display: -webkit-box; + -webkit-line-clamp: 3; + -webkit-box-orient: vertical; + overflow: hidden; +} + +.book-stats { + display: flex; + gap: 1rem; + margin-bottom: 1rem; + padding: 0.75rem; + background: #f8f9fa; + border-radius: 4px; + font-size: 0.85rem; +} + +.stat-item { + text-align: center; + flex: 1; +} + +.stat-item strong { + display: block; + font-size: 1.1rem; + color: #6f42c1; +} + +.book-actions { + display:grid; + gap: 0.5rem; + flex-wrap: nowrap; +} + +.book-actions .compact-button { + flex: 1; + min-width: 0; + text-align: center; + white-space: nowrap; +} + +/* Пустое состояние */ +.books-empty-state { + text-align: center; + padding: 3rem 2rem; + background: #f8f9fa; + border-radius: 8px; + border: 2px dashed #dee2e6; +} + +.books-empty-icon { + font-size: 4rem; + margin-bottom: 1rem; + opacity: 0.5; +} + +/* Футер со статистикой */ +.books-stats-footer { + margin-top: 2rem; + padding: 1rem; + background: #f8f9fa; + border-radius: 5px; + text-align: center; + color: #666; +} + +/* Адаптивность */ +@media (max-width: 768px) { + .books-grid { + grid-template-columns: 1fr; + } + + .book-stats { + flex-direction: column; + gap: 0.5rem; + } + + .book-actions { + flex-direction: column; + } + + .book-actions .compact-button { + width: 100%; + } +} + +@media (max-width: 480px) { + .book-info { + padding: 1rem; + } + + .book-cover-container { + height: 160px; + } + + .cover-placeholder { + font-size: 2rem; + } } -// ./assets/js/autosave.js -// assets/js/autosave.js -document.addEventListener('DOMContentLoaded', function() { - // Ждем инициализации редактора - setTimeout(() => { - initializeAutoSave(); - }, 1000); -}); - -function initializeAutoSave() { - console.log('AutoSave: Initializing...'); - - // Ищем активные редакторы Quill - const quillEditors = document.querySelectorAll('.ql-editor'); - const textareas = document.querySelectorAll('textarea.writer-editor'); - - if (quillEditors.length === 0 || textareas.length === 0) { - console.log('AutoSave: No Quill editors found, retrying in 1s...'); - setTimeout(initializeAutoSave, 1000); - return; - } - - console.log(`AutoSave: Found ${quillEditors.length} Quill editor(s)`); - - // Для каждого редактора настраиваем автосейв - quillEditors.forEach((quillEditor, index) => { - const textarea = textareas[index]; - if (!textarea) return; - - setupAutoSaveForEditor(quillEditor, textarea, index); - }); -} - -function setupAutoSaveForEditor(quillEditor, textarea, editorIndex) { - let saveTimeout; - let isSaving = false; - let lastSavedContent = textarea.value; - let changeCount = 0; - - // Получаем экземпляр Quill из контейнера - const quillContainer = quillEditor.closest('.ql-container'); - const quillInstance = quillContainer ? Quill.find(quillContainer) : null; - - if (!quillInstance) { - console.error(`AutoSave: Could not find Quill instance for editor ${editorIndex}`); - return; - } - - console.log(`AutoSave: Setting up for editor ${editorIndex}`); - - function showSaveMessage(message) { - let messageEl = document.getElementById('autosave-message'); - if (!messageEl) { - messageEl = document.createElement('div'); - messageEl.id = 'autosave-message'; - messageEl.style.cssText = ` - position: fixed; - top: 70px; - right: 10px; - padding: 8px 12px; - background: #28a745; - color: white; - border-radius: 3px; - z-index: 10000; - font-size: 0.8rem; - box-shadow: 0 2px 5px rgba(0,0,0,0.2); - `; - document.body.appendChild(messageEl); - } - - messageEl.textContent = message; - messageEl.style.display = 'block'; - - setTimeout(() => { - messageEl.style.display = 'none'; - }, 2000); - } - - function showError(message) { - let messageEl = document.getElementById('autosave-message'); - if (!messageEl) { - messageEl = document.createElement('div'); - messageEl.id = 'autosave-message'; - messageEl.style.cssText = ` - position: fixed; - top: 70px; - right: 10px; - padding: 8px 12px; - background: #dc3545; - color: white; - border-radius: 3px; - z-index: 10000; - font-size: 0.8rem; - box-shadow: 0 2px 5px rgba(0,0,0,0.2); - `; - document.body.appendChild(messageEl); - } - - messageEl.textContent = message; - messageEl.style.background = '#dc3545'; - messageEl.style.display = 'block'; - - setTimeout(() => { - messageEl.style.display = 'none'; - messageEl.style.background = '#28a745'; - }, 3000); - } - - function autoSave() { - if (isSaving) { - console.log('AutoSave: Already saving, skipping...'); - return; - } - - const currentContent = textarea.value; - - // Проверяем, изменилось ли содержимое - if (currentContent === lastSavedContent) { - console.log('AutoSave: No changes detected'); - return; - } - - changeCount++; - console.log(`AutoSave: Changes detected (${changeCount}), saving...`); - - isSaving = true; - - // Показываем индикатор сохранения - showSaveMessage('Сохранение...'); - - const formData = new FormData(); - formData.append('content', currentContent); - - // Добавляем title если есть - const titleInput = document.querySelector('input[name="title"]'); - if (titleInput) { - formData.append('title', titleInput.value); - } - - // Добавляем status если есть - const statusSelect = document.querySelector('select[name="status"]'); - if (statusSelect) { - formData.append('status', statusSelect.value); - } - - formData.append('autosave', 'true'); - formData.append('csrf_token', document.querySelector('input[name="csrf_token"]')?.value || ''); - - const currentUrl = window.location.href; - - fetch(currentUrl, { - method: 'POST', - body: formData - }) - .then(response => { - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - return response.json(); - }) - .then(data => { - if (data.success) { - lastSavedContent = currentContent; - showSaveMessage('Автосохранено: ' + new Date().toLocaleTimeString()); - console.log('AutoSave: Successfully saved'); - } else { - throw new Error(data.error || 'Unknown error'); - } - }) - .catch(error => { - console.error('AutoSave Error:', error); - showError('Ошибка автосохранения: ' + error.message); - }) - .finally(() => { - isSaving = false; - }); - } - - // Слушаем изменения в Quill редакторе - quillInstance.on('text-change', function(delta, oldDelta, source) { - if (source === 'user') { - console.log('AutoSave: Text changed by user'); - clearTimeout(saveTimeout); - saveTimeout = setTimeout(autoSave, 2000); // Сохраняем через 2 секунды после изменения - } - }); - - // Также слушаем изменения в title и status - const titleInput = document.querySelector('input[name="title"]'); - if (titleInput) { - titleInput.addEventListener('input', function() { - clearTimeout(saveTimeout); - saveTimeout = setTimeout(autoSave, 2000); - }); - } - - const statusSelect = document.querySelector('select[name="status"]'); - if (statusSelect) { - statusSelect.addEventListener('change', function() { - clearTimeout(saveTimeout); - saveTimeout = setTimeout(autoSave, 1000); - }); - } - - // Предупреждение при закрытии страницы с несохраненными изменениями - window.addEventListener('beforeunload', function(e) { - if (textarea.value !== lastSavedContent && !isSaving) { - e.preventDefault(); - e.returnValue = 'У вас есть несохраненные изменения. Вы уверены, что хотите уйти?'; - return e.returnValue; - } - }); - - // Периодическое сохранение каждые 30 секунд (на всякий случай) - setInterval(() => { - if (textarea.value !== lastSavedContent && !isSaving) { - console.log('AutoSave: Periodic save triggered'); - autoSave(); - } - }, 30000); - - console.log(`AutoSave: Successfully set up for editor ${editorIndex}`); -} -// ./assets/js/index.php - -// ./assets/js/editor.js -// assets/js/editor.js -class WriterEditor { - constructor() { - this.editors = []; - this.init(); - } - - init() { - // Инициализируем редакторы для текстовых областей с классом .writer-editor - document.querySelectorAll('textarea.writer-editor').forEach(textarea => { - this.initEditor(textarea); - }); - } - - initEditor(textarea) { - // Создаем контейнер для Quill - const editorContainer = document.createElement('div'); - editorContainer.className = 'writer-editor-container'; - editorContainer.style.height = '500px'; - editorContainer.style.marginBottom = '20px'; - - // Вставляем контейнер перед textarea - textarea.parentNode.insertBefore(editorContainer, textarea); - - // Скрываем оригинальный textarea - textarea.style.display = 'none'; - - // Настройки Quill - const quill = new Quill(editorContainer, { - theme: 'snow', - modules: { - toolbar: [ - [{ 'header': [1, 2, 3, false] }], - ['bold', 'italic', 'underline', 'strike'], - ['blockquote', 'code-block'], - [{ 'list': 'ordered'}, { 'list': 'bullet' }], - [{ 'script': 'sub'}, { 'script': 'super' }], - [{ 'indent': '-1'}, { 'indent': '+1' }], - [{ 'direction': 'rtl' }], - [{ 'size': ['small', false, 'large', 'huge'] }], - [{ 'color': [] }, { 'background': [] }], - [{ 'font': [] }], - [{ 'align': [] }], - ['link', 'image', 'video'], - ['clean'] - ], - history: { - delay: 1000, - maxStack: 100, - userOnly: true - } - }, - placeholder: 'Начните писать вашу главу...', - formats: [ - 'header', 'bold', 'italic', 'underline', 'strike', - 'blockquote', 'code-block', 'list', 'bullet', - 'script', 'indent', 'direction', 'size', - 'color', 'background', 'font', 'align', - 'link', 'image', 'video' - ] - }); - - // Устанавливаем начальное содержимое - if (textarea.value) { - quill.root.innerHTML = textarea.value; - } - - // Обновляем textarea при изменении содержимого - quill.on('text-change', () => { - textarea.value = quill.root.innerHTML; - }); - - // Сохраняем ссылку на редактор - this.editors.push({ - quill: quill, - textarea: textarea - }); - - return quill; - } - - // Метод для получения HTML содержимого - getContent(editorIndex = 0) { - if (this.editors[editorIndex]) { - return this.editors[editorIndex].quill.root.innerHTML; - } - return ''; - } - - // Метод для установки содержимого - setContent(content, editorIndex = 0) { - if (this.editors[editorIndex]) { - this.editors[editorIndex].quill.root.innerHTML = content; - } - } -} - - -// Инициализация редактора при загрузке страницы -document.addEventListener('DOMContentLoaded', function() { - window.writerEditor = new WriterEditor(); -}); // ./config/index.php // ./config/config.php @@ -5118,8 +5099,8 @@ EOT; <?= e($page_title ?? 'Web Writer') ?> - - + +