├── .all-contributorsrc ├── .eslintignore ├── .gitattributes ├── .github ├── FUNDING.yml └── workflows │ ├── build.yml │ ├── codeql.yml │ ├── cypress.yml │ └── pr-title.yml ├── .gitignore ├── .husky └── pre-commit ├── .nvmrc ├── .prettierignore ├── .prettierrc ├── .vscode └── tasks.json ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── cypress.config.ts ├── cypress ├── README.md ├── cypress.d.ts ├── e2e │ ├── _old │ │ ├── editorial_workflow_spec_bitbucket_backend.js │ │ ├── editorial_workflow_spec_git-gateway_github_backend.js │ │ ├── editorial_workflow_spec_git-gateway_gitlab_backend.js │ │ ├── editorial_workflow_spec_github_backend_graphql.js │ │ ├── editorial_workflow_spec_github_backend_graphql_open_authoring.js │ │ ├── editorial_workflow_spec_github_backend_rest.js │ │ ├── editorial_workflow_spec_github_backend_rest_open_authoring.js │ │ ├── editorial_workflow_spec_gitlab_backend.js │ │ ├── editorial_workflow_spec_proxy_git_backend.js │ │ ├── field_validations_spec.js │ │ ├── i18n_editorial_workflow_spec_test_backend.js │ │ ├── i18n_simple_workflow_spec_proxy_fs_backend.js │ │ ├── markdown_widget_backspace_spec.js │ │ ├── markdown_widget_code_block_spec.js │ │ ├── markdown_widget_enter_spec.js │ │ ├── markdown_widget_hotkeys_spec.js │ │ ├── markdown_widget_link_spec.js │ │ ├── markdown_widget_list_spec.js │ │ ├── markdown_widget_marks_spec.js │ │ ├── markdown_widget_quote_spec.js │ │ ├── media_library_spec_bitbucket_backend.js │ │ ├── media_library_spec_bitbucket_backend_large_media.js │ │ ├── media_library_spec_git-gateway_github_backend_large_media.js │ │ ├── media_library_spec_git-gateway_gitlab_backend_large_media.js │ │ ├── media_library_spec_github_backend_graphql.js │ │ ├── media_library_spec_github_backend_rest.js │ │ ├── media_library_spec_gitlab_backend.js │ │ ├── media_library_spec_proxy_git_backend.js │ │ ├── media_library_spec_test_backend.js │ │ ├── search_suggestion_spec.js │ │ ├── simple_workflow_spec_bitbucket_backend.ts │ │ ├── simple_workflow_spec_git-gateway_github_backend.js │ │ ├── simple_workflow_spec_git-gateway_gitlab_backend.js │ │ ├── simple_workflow_spec_github_backend_graphql.js │ │ ├── simple_workflow_spec_github_backend_rest.js │ │ ├── simple_workflow_spec_gitlab_backend.js │ │ ├── simple_workflow_spec_proxy_fs_backend.js │ │ └── simple_workflow_spec_proxy_git_backend.js │ ├── common │ │ ├── editorial_workflow.js │ │ ├── editorial_workflow_migrations.js │ │ ├── entries.ts │ │ ├── i18n.js │ │ ├── i18n_editorial_workflow_spec.js │ │ ├── media_library.js │ │ ├── open_authoring.js │ │ ├── simple_workflow.ts │ │ └── spec_utils.ts │ ├── editorial_workflow_test_backend.spec.ts │ ├── simple_workflow_test_backend.spec.ts │ ├── view_filters.spec.ts │ └── view_groups.spec.ts ├── fixtures │ └── media │ │ ├── decap.png │ │ └── netlify.png ├── interface.ts ├── plugins │ ├── bitbucket.js │ ├── common.js │ ├── gitGateway.js │ ├── github.js │ ├── gitlab.js │ ├── index.ts │ ├── proxy.js │ └── testBackend.ts ├── run.mjs ├── snapshots │ ├── media_library_spec_git-gateway_backend_large_media.js │ │ ├── Git Gateway Backend Media Library - Large Media -- can delete image from global media library.snap.png │ │ ├── Git Gateway Backend Media Library - Large Media -- can publish entry with image.snap.png │ │ ├── Git Gateway Backend Media Library - Large Media -- can save entry with image.snap.png │ │ ├── Git Gateway Backend Media Library - Large Media -- can upload image from entry media library.snap.png │ │ ├── Git Gateway Backend Media Library - Large Media -- can upload image from global media library.snap.png │ │ ├── Git Gateway Backend Media Library - Large Media -- should not show draft entry image in global media library.snap.png │ │ ├── Git Gateway Backend Media Library - Large Media -- should show published entry image in global media library.snap.png │ │ └── Git Gateway Backend Media Library - Large Media -- should show published entry image in grid view.snap.png │ ├── media_library_spec_github_backend_graphql.js │ │ ├── GitHub Backend Media Library - GraphQL API -- can delete image from global media library.snap.png │ │ ├── GitHub Backend Media Library - GraphQL API -- can publish entry with image.snap.png │ │ ├── GitHub Backend Media Library - GraphQL API -- can save entry with image.snap.png │ │ ├── GitHub Backend Media Library - GraphQL API -- can upload image from entry media library.snap.png │ │ ├── GitHub Backend Media Library - GraphQL API -- can upload image from global media library.snap.png │ │ ├── GitHub Backend Media Library - GraphQL API -- should not show draft entry image in global media library.snap.png │ │ ├── GitHub Backend Media Library - GraphQL API -- should show published entry image in global media library.snap.png │ │ └── GitHub Backend Media Library - GraphQL API -- should show published entry image in grid view.snap.png │ ├── media_library_spec_github_backend_rest.js │ │ ├── GitHub Backend Media Library - REST API -- can delete image from global media library.snap.png │ │ ├── GitHub Backend Media Library - REST API -- can publish entry with image.snap.png │ │ ├── GitHub Backend Media Library - REST API -- can save entry with image.snap.png │ │ ├── GitHub Backend Media Library - REST API -- can upload image from entry media library.snap.png │ │ ├── GitHub Backend Media Library - REST API -- can upload image from global media library.snap.png │ │ ├── GitHub Backend Media Library - REST API -- should not show draft entry image in global media library.snap.png │ │ ├── GitHub Backend Media Library - REST API -- should show published entry image in global media library.snap.png │ │ └── GitHub Backend Media Library - REST API -- should show published entry image in grid view.snap.png │ └── media_library_spec_test_backend.js │ │ ├── Test Backend Media Library -- can delete image from global media library.snap.png │ │ ├── Test Backend Media Library -- can publish entry with image.snap.png │ │ ├── Test Backend Media Library -- can save entry with image.snap.png │ │ ├── Test Backend Media Library -- can upload image from entry media library.snap.png │ │ ├── Test Backend Media Library -- can upload image from global media library.snap.png │ │ ├── Test Backend Media Library -- should not show draft entry image in global media library.snap.png │ │ ├── Test Backend Media Library -- should show published entry image in global media library.snap.png │ │ └── Test Backend Media Library -- should show published entry image in grid view.snap.png ├── support │ ├── commands.ts │ └── e2e.ts └── utils │ ├── README.md │ ├── config.ts │ ├── constants.ts │ ├── regexp.ts │ └── steps.ts ├── lerna.json ├── netlify.toml ├── nx.json ├── package.json ├── packages ├── app │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── babel.config.js │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ └── index.ts │ ├── tailwind.config.js │ ├── tsconfig.json │ └── webpack.config.js ├── core │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── babel.config.js │ ├── dev-test │ │ ├── _posts │ │ │ └── assets │ │ │ │ └── uploads │ │ │ │ ├── lobby.jpg │ │ │ │ └── moby-dick.jpg │ │ ├── assets │ │ │ └── uploads │ │ │ │ ├── Other Pics │ │ │ │ ├── lobby.jpg │ │ │ │ └── moby-dick.jpg │ │ │ │ ├── lobby.jpg │ │ │ │ └── moby-dick.jpg │ │ ├── backends │ │ │ ├── bitbucket │ │ │ │ ├── config.yml │ │ │ │ └── index.html │ │ │ ├── git-gateway │ │ │ │ ├── config.yml │ │ │ │ └── index.html │ │ │ ├── github │ │ │ │ ├── config.yml │ │ │ │ └── index.html │ │ │ ├── gitlab │ │ │ │ ├── config.yml │ │ │ │ ├── index.html │ │ │ │ └── index.js │ │ │ ├── proxy │ │ │ │ ├── _data │ │ │ │ │ ├── authors.yml │ │ │ │ │ ├── navigation.mdx │ │ │ │ │ └── settings.json │ │ │ │ ├── _faq │ │ │ │ │ └── a-question.md │ │ │ │ ├── _i18n_playground │ │ │ │ │ ├── file1.de.md │ │ │ │ │ ├── file1.en.md │ │ │ │ │ └── file1.fr.md │ │ │ │ ├── _nested_pages │ │ │ │ │ ├── authors │ │ │ │ │ │ ├── author-1 │ │ │ │ │ │ │ └── index.md │ │ │ │ │ │ └── index.md │ │ │ │ │ ├── index.md │ │ │ │ │ └── posts │ │ │ │ │ │ ├── hello-world │ │ │ │ │ │ └── index.md │ │ │ │ │ │ └── index.md │ │ │ │ ├── _posts │ │ │ │ │ ├── 2022-11-01-something │ │ │ │ │ │ ├── index.md │ │ │ │ │ │ ├── ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg │ │ │ │ │ │ └── static-cms-icon.svg │ │ │ │ │ ├── 2022-11-01-test │ │ │ │ │ │ ├── index.md │ │ │ │ │ │ ├── kittens.jpg │ │ │ │ │ │ └── moby-dick.jpg │ │ │ │ │ └── 2022-11-02-test │ │ │ │ │ │ ├── index.md │ │ │ │ │ │ ├── kanefreeman_2.jpg │ │ │ │ │ │ └── ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg │ │ │ │ ├── _sink │ │ │ │ │ └── kitchen_sink1.md │ │ │ │ ├── assets │ │ │ │ │ ├── posts │ │ │ │ │ │ ├── kanefreeman_2.jpg │ │ │ │ │ │ ├── kittens.jpg │ │ │ │ │ │ ├── moby-dick.jpg │ │ │ │ │ │ └── ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg │ │ │ │ │ └── upload │ │ │ │ │ │ ├── document.txt │ │ │ │ │ │ ├── kanefreeman-pre.png │ │ │ │ │ │ ├── kanefreeman_2.jpg │ │ │ │ │ │ ├── kanefreeman_widescreen.png │ │ │ │ │ │ ├── kittens.jpg │ │ │ │ │ │ ├── moby-dick.jpg │ │ │ │ │ │ ├── nf-logo.png │ │ │ │ │ │ ├── shortfin-mako-shark-seas.webp │ │ │ │ │ │ └── static-cms-logo.png │ │ │ │ ├── config.yml │ │ │ │ └── index.html │ │ │ └── test │ │ │ │ ├── config.yml │ │ │ │ └── index.html │ │ ├── config.yml │ │ ├── data.js │ │ ├── example.css │ │ ├── index.html │ │ └── index.js │ ├── jest.config.integration.js │ ├── jest.config.js │ ├── package.json │ ├── postcss.config.js │ ├── src │ │ ├── __mocks__ │ │ │ ├── @udecode │ │ │ │ ├── plate-core.ts │ │ │ │ ├── plate-list.ts │ │ │ │ └── plate.ts │ │ │ ├── array-move.ts │ │ │ ├── backend.ts │ │ │ ├── clean-stack.ts │ │ │ ├── copy-text-to-clipboard.ts │ │ │ ├── localforage.ts │ │ │ ├── react-markdown.ts │ │ │ ├── react-polyglot.ts │ │ │ ├── react-virtualized-auto-sizer.ts │ │ │ ├── react-waypoint.ts │ │ │ ├── remark-gfm.ts │ │ │ ├── remark-mdx.ts │ │ │ ├── remark-parse.ts │ │ │ ├── slate-react.ts │ │ │ ├── styleMock.ts │ │ │ ├── unified.ts │ │ │ ├── url-join.ts │ │ │ ├── uuid.ts │ │ │ └── yaml.ts │ │ ├── __tests__ │ │ │ ├── backend.spec.ts │ │ │ └── testConfig.ts │ │ ├── actions │ │ │ ├── __tests__ │ │ │ │ └── config.spec.ts │ │ │ ├── auth.ts │ │ │ ├── config.ts │ │ │ ├── editorialWorkflow.ts │ │ │ ├── entries.ts │ │ │ ├── globalUI.ts │ │ │ ├── media.ts │ │ │ ├── mediaLibrary.ts │ │ │ ├── scroll.ts │ │ │ ├── search.ts │ │ │ ├── status.ts │ │ │ └── waitUntil.ts │ │ ├── backend.ts │ │ ├── backends │ │ │ ├── bitbucket │ │ │ │ ├── API.ts │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── __tests__ │ │ │ │ │ └── api.spec.ts │ │ │ │ ├── git-lfs-client.ts │ │ │ │ ├── implementation.ts │ │ │ │ └── index.ts │ │ │ ├── git-gateway │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── GitHubAPI.ts │ │ │ │ ├── GitLabAPI.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── AuthenticationPage.spec.tsx │ │ │ │ │ └── GitHubAPI.spec.ts │ │ │ │ ├── implementation.tsx │ │ │ │ ├── index.ts │ │ │ │ └── netlify-lfs-client.ts │ │ │ ├── gitea │ │ │ │ ├── API.ts │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── __tests__ │ │ │ │ │ ├── API.spec.ts │ │ │ │ │ └── implementation.spec.ts │ │ │ │ ├── implementation.tsx │ │ │ │ ├── index.ts │ │ │ │ └── types.ts │ │ │ ├── github │ │ │ │ ├── API.ts │ │ │ │ ├── AuthenticationPage.css │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── __tests__ │ │ │ │ │ ├── API.spec.ts │ │ │ │ │ └── implementation.spec.ts │ │ │ │ ├── fragmentTypes.ts │ │ │ │ ├── fragments.ts │ │ │ │ ├── implementation.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── mutations.ts │ │ │ │ ├── queries.ts │ │ │ │ └── types.ts │ │ │ ├── gitlab │ │ │ │ ├── API.ts │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── __tests__ │ │ │ │ │ ├── API.spec.ts │ │ │ │ │ └── gitlab.spec.ts │ │ │ │ ├── implementation.ts │ │ │ │ ├── index.ts │ │ │ │ └── queries.ts │ │ │ ├── index.ts │ │ │ ├── proxy │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── implementation.ts │ │ │ │ └── index.ts │ │ │ └── test │ │ │ │ ├── AuthenticationPage.tsx │ │ │ │ ├── __tests__ │ │ │ │ └── implementation.spec.ts │ │ │ │ ├── implementation.ts │ │ │ │ └── index.ts │ │ ├── bootstrap.tsx │ │ ├── components │ │ │ ├── App.css │ │ │ ├── App.tsx │ │ │ ├── ErrorBoundary.css │ │ │ ├── ErrorBoundary.tsx │ │ │ ├── MainView.css │ │ │ ├── MainView.tsx │ │ │ ├── NotFoundPage.tsx │ │ │ ├── collections │ │ │ │ ├── Collection.classes.ts │ │ │ │ ├── Collection.css │ │ │ │ ├── CollectionControls.tsx │ │ │ │ ├── CollectionHeader.tsx │ │ │ │ ├── CollectionPage.tsx │ │ │ │ ├── CollectionRoute.tsx │ │ │ │ ├── CollectionSearch.css │ │ │ │ ├── CollectionSearch.tsx │ │ │ │ ├── CollectionView.tsx │ │ │ │ ├── FilterControl.css │ │ │ │ ├── FilterControl.tsx │ │ │ │ ├── GroupControl.css │ │ │ │ ├── GroupControl.tsx │ │ │ │ ├── NestedCollection.css │ │ │ │ ├── NestedCollection.tsx │ │ │ │ ├── SortControl.css │ │ │ │ ├── SortControl.tsx │ │ │ │ ├── entries │ │ │ │ │ ├── Entries.classes.ts │ │ │ │ │ ├── Entries.css │ │ │ │ │ ├── Entries.tsx │ │ │ │ │ ├── EntriesCollection.tsx │ │ │ │ │ ├── EntriesSearch.tsx │ │ │ │ │ ├── EntryCard.css │ │ │ │ │ ├── EntryCard.tsx │ │ │ │ │ ├── EntryListing.tsx │ │ │ │ │ ├── EntryListingCardGrid.tsx │ │ │ │ │ ├── EntryListingGrid.tsx │ │ │ │ │ ├── EntryListingTable.tsx │ │ │ │ │ └── EntryRow.tsx │ │ │ │ └── mobile │ │ │ │ │ ├── MobileCollectionControls.classes.ts │ │ │ │ │ ├── MobileCollectionControls.css │ │ │ │ │ ├── MobileCollectionControls.tsx │ │ │ │ │ └── MobileCollectionControlsDrawer.tsx │ │ │ ├── common │ │ │ │ ├── alert │ │ │ │ │ ├── Alert.css │ │ │ │ │ └── Alert.tsx │ │ │ │ ├── autocomplete │ │ │ │ │ ├── Autocomplete.css │ │ │ │ │ └── Autocomplete.tsx │ │ │ │ ├── button │ │ │ │ │ ├── Button.css │ │ │ │ │ ├── Button.tsx │ │ │ │ │ ├── IconButton.css │ │ │ │ │ ├── IconButton.tsx │ │ │ │ │ └── useButtonClassNames.tsx │ │ │ │ ├── card │ │ │ │ │ ├── Card.classes.ts │ │ │ │ │ ├── Card.css │ │ │ │ │ ├── Card.tsx │ │ │ │ │ ├── CardActionArea.tsx │ │ │ │ │ ├── CardContent.tsx │ │ │ │ │ ├── CardHeader.tsx │ │ │ │ │ └── CardMedia.tsx │ │ │ │ ├── checkbox │ │ │ │ │ ├── Checkbox.css │ │ │ │ │ └── Checkbox.tsx │ │ │ │ ├── confirm │ │ │ │ │ ├── Confirm.css │ │ │ │ │ └── Confirm.tsx │ │ │ │ ├── field │ │ │ │ │ ├── ErrorMessage.css │ │ │ │ │ ├── ErrorMessage.tsx │ │ │ │ │ ├── Field.css │ │ │ │ │ ├── Field.tsx │ │ │ │ │ ├── Hint.css │ │ │ │ │ ├── Hint.tsx │ │ │ │ │ ├── Label.css │ │ │ │ │ └── Label.tsx │ │ │ │ ├── image │ │ │ │ │ ├── Image.css │ │ │ │ │ └── Image.tsx │ │ │ │ ├── link │ │ │ │ │ └── Link.tsx │ │ │ │ ├── menu │ │ │ │ │ ├── Menu.css │ │ │ │ │ ├── Menu.tsx │ │ │ │ │ ├── MenuGroup.css │ │ │ │ │ ├── MenuGroup.tsx │ │ │ │ │ ├── MenuItemButton.css │ │ │ │ │ ├── MenuItemButton.tsx │ │ │ │ │ ├── MenuItemLink.css │ │ │ │ │ └── MenuItemLink.tsx │ │ │ │ ├── modal │ │ │ │ │ ├── Backdrop.tsx │ │ │ │ │ ├── Modal.classes.ts │ │ │ │ │ ├── Modal.css │ │ │ │ │ └── Modal.tsx │ │ │ │ ├── pill │ │ │ │ │ ├── Pill.css │ │ │ │ │ └── Pill.tsx │ │ │ │ ├── progress │ │ │ │ │ ├── CircularProgress.css │ │ │ │ │ ├── CircularProgress.tsx │ │ │ │ │ ├── Loader.css │ │ │ │ │ └── Loader.tsx │ │ │ │ ├── select │ │ │ │ │ ├── Option.css │ │ │ │ │ ├── Option.tsx │ │ │ │ │ ├── Select.css │ │ │ │ │ └── Select.tsx │ │ │ │ ├── switch │ │ │ │ │ ├── Switch.css │ │ │ │ │ └── Switch.tsx │ │ │ │ ├── table │ │ │ │ │ ├── Table.classes.ts │ │ │ │ │ ├── Table.css │ │ │ │ │ ├── Table.tsx │ │ │ │ │ ├── TableCell.tsx │ │ │ │ │ ├── TableHeaderCell.tsx │ │ │ │ │ └── TableRow.tsx │ │ │ │ ├── text-field │ │ │ │ │ ├── TextArea.css │ │ │ │ │ ├── TextArea.tsx │ │ │ │ │ ├── TextField.css │ │ │ │ │ └── TextField.tsx │ │ │ │ ├── view-style │ │ │ │ │ ├── ViewStyleControl.css │ │ │ │ │ └── ViewStyleControl.tsx │ │ │ │ └── widget │ │ │ │ │ ├── PreviewHOC.tsx │ │ │ │ │ ├── useWidgetsFor.tsx │ │ │ │ │ ├── widgetFor.css │ │ │ │ │ └── widgetFor.tsx │ │ │ ├── entry-editor │ │ │ │ ├── Editor.tsx │ │ │ │ ├── EditorInterface.css │ │ │ │ ├── EditorInterface.tsx │ │ │ │ ├── EditorRoute.tsx │ │ │ │ ├── EditorToolbar.css │ │ │ │ ├── EditorToolbar.tsx │ │ │ │ ├── EditorWorkflowToolbarButtons.css │ │ │ │ ├── EditorWorkflowToolbarButtons.tsx │ │ │ │ ├── editor-control-pane │ │ │ │ │ ├── EditorControl.css │ │ │ │ │ ├── EditorControl.tsx │ │ │ │ │ ├── EditorControlPane.css │ │ │ │ │ ├── EditorControlPane.tsx │ │ │ │ │ ├── LocaleDropdown.css │ │ │ │ │ └── LocaleDropdown.tsx │ │ │ │ ├── editor-preview-pane │ │ │ │ │ ├── EditorPreview.tsx │ │ │ │ │ ├── EditorPreviewContent.tsx │ │ │ │ │ ├── EditorPreviewPane.css │ │ │ │ │ ├── EditorPreviewPane.tsx │ │ │ │ │ ├── PreviewFrameContent.css │ │ │ │ │ └── PreviewFrameContent.tsx │ │ │ │ └── widgets │ │ │ │ │ ├── Unknown │ │ │ │ │ ├── UnknownControl.css │ │ │ │ │ ├── UnknownControl.tsx │ │ │ │ │ └── UnknownPreview.tsx │ │ │ │ │ └── index.ts │ │ │ ├── images │ │ │ │ ├── _index.tsx │ │ │ │ ├── static-cms-icon.svg │ │ │ │ └── static-cms-logo.svg │ │ │ ├── login │ │ │ │ ├── GoBackButton.tsx │ │ │ │ ├── Login.css │ │ │ │ └── Login.tsx │ │ │ ├── media-library │ │ │ │ ├── MediaLibraryModal.css │ │ │ │ ├── MediaLibraryModal.tsx │ │ │ │ ├── MediaPage.tsx │ │ │ │ └── common │ │ │ │ │ ├── CopyToClipBoardButton.tsx │ │ │ │ │ ├── CurrentMediaDetails.tsx │ │ │ │ │ ├── EmptyMessage.tsx │ │ │ │ │ ├── FileUploadButton.tsx │ │ │ │ │ ├── FolderCreationDialog.css │ │ │ │ │ ├── FolderCreationDialog.tsx │ │ │ │ │ ├── InlineEditTextField.css │ │ │ │ │ ├── InlineEditTextField.tsx │ │ │ │ │ ├── MediaLibrary.classes.ts │ │ │ │ │ ├── MediaLibrary.css │ │ │ │ │ ├── MediaLibrary.tsx │ │ │ │ │ ├── MediaLibraryCard.css │ │ │ │ │ ├── MediaLibraryCard.tsx │ │ │ │ │ ├── MediaLibraryCardGrid.tsx │ │ │ │ │ └── MediaLibrarySearch.tsx │ │ │ ├── navbar │ │ │ │ ├── BottomNavigation.css │ │ │ │ ├── BottomNavigation.tsx │ │ │ │ ├── Breadcrumbs.css │ │ │ │ ├── Breadcrumbs.tsx │ │ │ │ ├── NavLink.css │ │ │ │ ├── NavLink.tsx │ │ │ │ ├── Navbar.css │ │ │ │ ├── Navbar.tsx │ │ │ │ ├── NavigationDrawer.css │ │ │ │ ├── NavigationDrawer.tsx │ │ │ │ ├── QuickCreate.tsx │ │ │ │ ├── SettingsDropdown.css │ │ │ │ ├── SettingsDropdown.tsx │ │ │ │ ├── Sidebar.classes.ts │ │ │ │ ├── Sidebar.css │ │ │ │ ├── Sidebar.tsx │ │ │ │ └── SidebarContent.tsx │ │ │ ├── page │ │ │ │ └── Page.tsx │ │ │ ├── snackbar │ │ │ │ ├── SnackbarAlert.css │ │ │ │ ├── SnackbarAlert.tsx │ │ │ │ └── Snackbars.tsx │ │ │ ├── theme │ │ │ │ ├── ThemeManager.tsx │ │ │ │ ├── components │ │ │ │ │ ├── ThemeCard.css │ │ │ │ │ ├── ThemeCard.tsx │ │ │ │ │ ├── ThemeSelectorDialog.css │ │ │ │ │ └── ThemeSelectorDialog.tsx │ │ │ │ ├── defaultThemes.ts │ │ │ │ ├── hooks │ │ │ │ │ ├── useTheme.ts │ │ │ │ │ └── useThemes.ts │ │ │ │ └── util │ │ │ │ │ └── createTheme.ts │ │ │ └── workflow │ │ │ │ ├── ActiveWorkflowCard.tsx │ │ │ │ ├── Dashboard.css │ │ │ │ ├── Dashboard.tsx │ │ │ │ ├── WorkflowCard.css │ │ │ │ ├── WorkflowCard.tsx │ │ │ │ ├── WorkflowColumn.css │ │ │ │ ├── WorkflowColumn.tsx │ │ │ │ ├── WorkflowStatusPill.tsx │ │ │ │ ├── hooks │ │ │ │ ├── useWorkflowBoardSections.ts │ │ │ │ └── useWorkflowEntriesByCollection.ts │ │ │ │ └── util │ │ │ │ └── workflow.util.ts │ │ ├── constants.ts │ │ ├── constants │ │ │ ├── commitProps.ts │ │ │ ├── configSchema.tsx │ │ │ ├── enums.ts │ │ │ ├── fieldInference.tsx │ │ │ ├── files.ts │ │ │ ├── mediaLibrary.ts │ │ │ ├── publishModes.ts │ │ │ ├── toolbar_buttons.ts │ │ │ ├── validationErrorTypes.ts │ │ │ └── views.ts │ │ ├── extensions.ts │ │ ├── formats │ │ │ ├── FileFormatter.ts │ │ │ ├── JsonFormatter.ts │ │ │ ├── TomlFormatter.ts │ │ │ ├── YamlFormatter.ts │ │ │ ├── __tests__ │ │ │ │ └── YamlFormatter.spec.ts │ │ │ ├── formats.ts │ │ │ ├── frontmatter.ts │ │ │ └── helpers.ts │ │ ├── index.ts │ │ ├── interface.ts │ │ ├── lib │ │ │ ├── __tests__ │ │ │ │ └── registry.spec.ts │ │ │ ├── auth │ │ │ │ ├── implicit-oauth.ts │ │ │ │ ├── index.ts │ │ │ │ ├── netlify-auth.ts │ │ │ │ ├── pkce-oauth.ts │ │ │ │ └── utils.ts │ │ │ ├── consoleError.ts │ │ │ ├── formatters.ts │ │ │ ├── hooks │ │ │ │ ├── __tests__ │ │ │ │ │ └── useMediaFiles.spec.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── useBreadcrumbs.ts │ │ │ │ ├── useCurrentBackend.ts │ │ │ │ ├── useCursor.ts │ │ │ │ ├── useDebounce.ts │ │ │ │ ├── useDebouncedCallback.ts │ │ │ │ ├── useDefaultPath.ts │ │ │ │ ├── useDragHandlers.ts │ │ │ │ ├── useElementSize.ts │ │ │ │ ├── useEntries.ts │ │ │ │ ├── useEntryCallback.ts │ │ │ │ ├── useFilters.ts │ │ │ │ ├── useFolderSupport.ts │ │ │ │ ├── useGroups.ts │ │ │ │ ├── useHasChildErrors.ts │ │ │ │ ├── useIcon.css │ │ │ │ ├── useIcon.tsx │ │ │ │ ├── useIsMediaAsset.ts │ │ │ │ ├── useMediaAsset.ts │ │ │ │ ├── useMediaFiles.ts │ │ │ │ ├── useMediaInsert.ts │ │ │ │ ├── useMediaPersist.ts │ │ │ │ ├── useMediaQuery.ts │ │ │ │ ├── useMemoCompare.ts │ │ │ │ ├── useMeta.ts │ │ │ │ ├── useNewEntryUrl.ts │ │ │ │ ├── usePublishedEntries.ts │ │ │ │ ├── useRefWithCallback.ts │ │ │ │ ├── useTranslate.ts │ │ │ │ ├── useUUID.ts │ │ │ │ └── useUnpublishedEntries.ts │ │ │ ├── i18n.ts │ │ │ ├── index.tsx │ │ │ ├── phrases.ts │ │ │ ├── registry.ts │ │ │ ├── serializeEntryValues.ts │ │ │ ├── textHelper.ts │ │ │ ├── urlHelper.ts │ │ │ ├── util │ │ │ │ ├── API.ts │ │ │ │ ├── APIError.ts │ │ │ │ ├── APIUtils.ts │ │ │ │ ├── AccessTokenError.ts │ │ │ │ ├── Cursor.ts │ │ │ │ ├── EditorialWorkflowError.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── field.util.spec.ts │ │ │ │ │ ├── filter.util.spec.ts │ │ │ │ │ ├── media.util.spec.ts │ │ │ │ │ ├── nested.util.spec.ts │ │ │ │ │ └── search.util.spec.ts │ │ │ │ ├── asyncLock.ts │ │ │ │ ├── backendUtil.ts │ │ │ │ ├── backup.util.ts │ │ │ │ ├── classNames.util.ts │ │ │ │ ├── collection.util.ts │ │ │ │ ├── dnd.util.ts │ │ │ │ ├── entry.util.ts │ │ │ │ ├── events │ │ │ │ │ ├── AlertEvent.ts │ │ │ │ │ ├── ConfirmEvent.ts │ │ │ │ │ ├── DataEvent.ts │ │ │ │ │ ├── LivePreviewLoadedEvent.ts │ │ │ │ │ └── MediaLibraryCloseEvent.ts │ │ │ │ ├── fetch.util.ts │ │ │ │ ├── field.util.ts │ │ │ │ ├── filter.util.ts │ │ │ │ ├── getBlobSHA.ts │ │ │ │ ├── git-lfs.ts │ │ │ │ ├── implementation.ts │ │ │ │ ├── index.ts │ │ │ │ ├── loadScript.ts │ │ │ │ ├── localForage.ts │ │ │ │ ├── media.util.ts │ │ │ │ ├── nested.util.ts │ │ │ │ ├── null.util.ts │ │ │ │ ├── path.ts │ │ │ │ ├── promise.ts │ │ │ │ ├── search.util.ts │ │ │ │ ├── set.util.ts │ │ │ │ ├── sort.util.ts │ │ │ │ ├── string.util.ts │ │ │ │ ├── theming.util.ts │ │ │ │ ├── unsentRequest.ts │ │ │ │ ├── validation.util.ts │ │ │ │ └── window.util.ts │ │ │ └── widgets │ │ │ │ ├── index.ts │ │ │ │ ├── stringTemplate.ts │ │ │ │ └── validations.ts │ │ ├── live │ │ │ ├── Data.tsx │ │ │ ├── index.ts │ │ │ └── useData.tsx │ │ ├── locales │ │ │ ├── bg │ │ │ │ └── index.ts │ │ │ ├── ca │ │ │ │ └── index.ts │ │ │ ├── cs │ │ │ │ └── index.ts │ │ │ ├── da │ │ │ │ └── index.ts │ │ │ ├── de │ │ │ │ └── index.ts │ │ │ ├── en │ │ │ │ └── index.ts │ │ │ ├── es │ │ │ │ └── index.ts │ │ │ ├── fa │ │ │ │ └── index.ts │ │ │ ├── fr │ │ │ │ └── index.ts │ │ │ ├── gr │ │ │ │ └── index.ts │ │ │ ├── he │ │ │ │ └── index.ts │ │ │ ├── hr │ │ │ │ └── index.ts │ │ │ ├── hu │ │ │ │ └── index.ts │ │ │ ├── index.ts │ │ │ ├── it │ │ │ │ └── index.ts │ │ │ ├── ja │ │ │ │ └── index.ts │ │ │ ├── ko │ │ │ │ └── index.ts │ │ │ ├── lt │ │ │ │ └── index.ts │ │ │ ├── nb_no │ │ │ │ └── index.ts │ │ │ ├── nl │ │ │ │ └── index.ts │ │ │ ├── nn_no │ │ │ │ └── index.ts │ │ │ ├── pl │ │ │ │ └── index.ts │ │ │ ├── pt │ │ │ │ └── index.ts │ │ │ ├── ro │ │ │ │ └── index.ts │ │ │ ├── ru │ │ │ │ └── index.ts │ │ │ ├── sl │ │ │ │ └── index.ts │ │ │ ├── sv │ │ │ │ └── index.ts │ │ │ ├── th │ │ │ │ └── index.ts │ │ │ ├── tr │ │ │ │ └── index.ts │ │ │ ├── types.ts │ │ │ ├── ua │ │ │ │ └── index.ts │ │ │ ├── uk │ │ │ │ └── index.ts │ │ │ ├── vi │ │ │ │ └── index.ts │ │ │ ├── zh_Hans │ │ │ │ └── index.ts │ │ │ └── zh_Hant │ │ │ │ └── index.ts │ │ ├── reducers │ │ │ ├── __tests__ │ │ │ │ └── entryDraft.spec.ts │ │ │ ├── auth.ts │ │ │ ├── collections.ts │ │ │ ├── combinedReducer.ts │ │ │ ├── config.ts │ │ │ ├── cursors.ts │ │ │ ├── editorialWorkflow.ts │ │ │ ├── entries.ts │ │ │ ├── entryDraft.ts │ │ │ ├── globalUI.ts │ │ │ ├── index.ts │ │ │ ├── mediaLibrary.ts │ │ │ ├── medias.ts │ │ │ ├── scroll.ts │ │ │ ├── search.ts │ │ │ ├── selectors │ │ │ │ ├── auth.ts │ │ │ │ ├── collections.ts │ │ │ │ ├── config.ts │ │ │ │ ├── cursors.ts │ │ │ │ ├── editorialWorkflow.ts │ │ │ │ ├── entries.ts │ │ │ │ ├── entryDraft.ts │ │ │ │ ├── globalUI.ts │ │ │ │ ├── mediaLibrary.ts │ │ │ │ ├── medias.ts │ │ │ │ └── scroll.ts │ │ │ └── status.ts │ │ ├── store │ │ │ ├── hooks.ts │ │ │ ├── index.ts │ │ │ ├── middleware │ │ │ │ └── waitUntilAction.ts │ │ │ └── slices │ │ │ │ └── snackbars.ts │ │ ├── styles │ │ │ └── main.css │ │ ├── tsconfig.json │ │ ├── types │ │ │ ├── constants.d.ts │ │ │ ├── css.d.ts │ │ │ ├── diacritics.d.ts │ │ │ ├── global.d.ts │ │ │ ├── ini.d.ts │ │ │ ├── react-scroll-sync.d.ts │ │ │ ├── semaphore.d.ts │ │ │ ├── svg.d.ts │ │ │ └── what-the-diff.d.ts │ │ ├── valueObjects │ │ │ ├── AssetProxy.ts │ │ │ ├── createEntry.ts │ │ │ └── index.ts │ │ └── widgets │ │ │ ├── boolean │ │ │ ├── BooleanControl.css │ │ │ ├── BooleanControl.tsx │ │ │ ├── __tests__ │ │ │ │ └── BooleanControl.spec.ts │ │ │ ├── index.ts │ │ │ └── schema.ts │ │ │ ├── code │ │ │ ├── CodeControl.css │ │ │ ├── CodeControl.tsx │ │ │ ├── CodePreview.tsx │ │ │ ├── SettingsButton.css │ │ │ ├── SettingsButton.tsx │ │ │ ├── SettingsPane.css │ │ │ ├── SettingsPane.tsx │ │ │ ├── data │ │ │ │ ├── languages-raw.yml │ │ │ │ └── languages.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── scripts │ │ │ │ └── process-languages.ts │ │ │ ├── colorstring │ │ │ ├── ColorControl.css │ │ │ ├── ColorControl.tsx │ │ │ ├── ColorPreview.tsx │ │ │ ├── __tests__ │ │ │ │ ├── ColorControl.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── validator.ts │ │ │ ├── datetime │ │ │ ├── DateTimeControl.css │ │ │ ├── DateTimeControl.tsx │ │ │ ├── DateTimePreview.tsx │ │ │ ├── __tests__ │ │ │ │ ├── DateTimeControl.spec.ts │ │ │ │ ├── getDefaultValue.spec.ts │ │ │ │ └── utc.util.spec.ts │ │ │ ├── components │ │ │ │ ├── NowButton.css │ │ │ │ └── NowButton.tsx │ │ │ ├── constants.ts │ │ │ ├── datetime.util.ts │ │ │ ├── getDefaultValue.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── utc.util.ts │ │ │ ├── file │ │ │ ├── FileImageControl.classes.ts │ │ │ ├── FileImageControl.css │ │ │ ├── FilePreview.tsx │ │ │ ├── __test__ │ │ │ │ └── FileControl.spec.ts │ │ │ ├── components │ │ │ │ ├── SortableImage.css │ │ │ │ ├── SortableImage.tsx │ │ │ │ ├── SortableLink.css │ │ │ │ └── SortableLink.tsx │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── withFileControl.tsx │ │ │ ├── image │ │ │ ├── ImagePreview.tsx │ │ │ ├── index.ts │ │ │ └── schema.ts │ │ │ ├── index.ts │ │ │ ├── keyvalue │ │ │ ├── KeyValueControl.css │ │ │ ├── KeyValueControl.tsx │ │ │ ├── KeyValuePreview.tsx │ │ │ ├── __tests__ │ │ │ │ ├── KeyValueControl.spec.ts │ │ │ │ ├── converters.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── converters.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ ├── types.ts │ │ │ ├── util.ts │ │ │ └── validator.ts │ │ │ ├── list │ │ │ ├── DelimitedListControl.tsx │ │ │ ├── ListControl.classes.ts │ │ │ ├── ListControl.css │ │ │ ├── ListControl.tsx │ │ │ ├── ListPreview.tsx │ │ │ ├── __tests__ │ │ │ │ └── ListControl.spec.tsx │ │ │ ├── components │ │ │ │ ├── ListFieldWrapper.tsx │ │ │ │ ├── ListItem.tsx │ │ │ │ ├── ListItemWrapper.css │ │ │ │ └── ListItemWrapper.tsx │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── typedListHelpers.ts │ │ │ ├── map │ │ │ ├── MapControl.css │ │ │ ├── MapPreview.tsx │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── withMapControl.tsx │ │ │ ├── markdown │ │ │ ├── MarkdownControl.classes.ts │ │ │ ├── MarkdownControl.css │ │ │ ├── MarkdownPreview.tsx │ │ │ ├── index.ts │ │ │ ├── mdx │ │ │ │ ├── index.ts │ │ │ │ └── withShortcodeMdxComponent.tsx │ │ │ ├── plate │ │ │ │ ├── PlateEditor.tsx │ │ │ │ ├── components │ │ │ │ │ ├── balloon-toolbar │ │ │ │ │ │ ├── BalloonToolbar.css │ │ │ │ │ │ ├── BalloonToolbar.tsx │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ │ └── BalloonToolbar.spec.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── buttons │ │ │ │ │ │ ├── BlockquoteToolbarButton.tsx │ │ │ │ │ │ ├── BoldToolbarButton.tsx │ │ │ │ │ │ ├── CodeBlockToolbarButtons.tsx │ │ │ │ │ │ ├── CodeToolbarButton.tsx │ │ │ │ │ │ ├── DecreaseIndentToolbarButton.tsx │ │ │ │ │ │ ├── DeleteColumnToolbarButton.tsx │ │ │ │ │ │ ├── DeleteRowToolbarButton.tsx │ │ │ │ │ │ ├── DeleteTableToolbarButton.tsx │ │ │ │ │ │ ├── FontTypeSelect.css │ │ │ │ │ │ ├── FontTypeSelect.tsx │ │ │ │ │ │ ├── IncreaseIndentToolbarButton.tsx │ │ │ │ │ │ ├── InsertColumnToolbarButton.tsx │ │ │ │ │ │ ├── InsertImageToolbarButton.tsx │ │ │ │ │ │ ├── InsertLinkToolbarButton.tsx │ │ │ │ │ │ ├── InsertRowToolbarButton.tsx │ │ │ │ │ │ ├── InsertTableToolbarButton.tsx │ │ │ │ │ │ ├── ItalicToolbarButton.tsx │ │ │ │ │ │ ├── OrderedListToolbarButton.tsx │ │ │ │ │ │ ├── ShortcodeToolbarButton.css │ │ │ │ │ │ ├── ShortcodeToolbarButton.tsx │ │ │ │ │ │ ├── StrikethroughToolbarButton.tsx │ │ │ │ │ │ ├── UnorderedListToolbarButton.tsx │ │ │ │ │ │ ├── common │ │ │ │ │ │ │ ├── AlignToolbarButton.tsx │ │ │ │ │ │ │ ├── BlockToolbarButton.tsx │ │ │ │ │ │ │ ├── ColorPickerToolbarDropdown.tsx │ │ │ │ │ │ │ ├── ListToolbarButton.tsx │ │ │ │ │ │ │ ├── MarkToolbarButton.tsx │ │ │ │ │ │ │ ├── ToolbarButton.css │ │ │ │ │ │ │ ├── ToolbarButton.tsx │ │ │ │ │ │ │ ├── dropdown │ │ │ │ │ │ │ │ ├── ToolbarDropdown.tsx │ │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── color-picker │ │ │ │ │ │ ├── ColorButton.css │ │ │ │ │ │ ├── ColorButton.tsx │ │ │ │ │ │ ├── ColorInput.tsx │ │ │ │ │ │ ├── ColorPicker.tsx │ │ │ │ │ │ ├── Colors.tsx │ │ │ │ │ │ ├── CustomColors.tsx │ │ │ │ │ │ ├── constants.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── types.ts │ │ │ │ │ ├── common │ │ │ │ │ │ ├── MediaPopover.css │ │ │ │ │ │ ├── MediaPopover.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── nodes │ │ │ │ │ │ ├── blockquote │ │ │ │ │ │ │ ├── BlockquoteElement.css │ │ │ │ │ │ │ ├── BlockquoteElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── code-block │ │ │ │ │ │ │ ├── CodeBlockElement.css │ │ │ │ │ │ │ ├── CodeBlockElement.tsx │ │ │ │ │ │ │ ├── CodeBlockFrame.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── code │ │ │ │ │ │ │ ├── Code.css │ │ │ │ │ │ │ └── Code.tsx │ │ │ │ │ │ ├── headings │ │ │ │ │ │ │ ├── Heading1.css │ │ │ │ │ │ │ ├── Heading1.tsx │ │ │ │ │ │ │ ├── Heading2.css │ │ │ │ │ │ │ ├── Heading2.tsx │ │ │ │ │ │ │ ├── Heading3.css │ │ │ │ │ │ │ ├── Heading3.tsx │ │ │ │ │ │ │ ├── Heading4.css │ │ │ │ │ │ │ ├── Heading4.tsx │ │ │ │ │ │ │ ├── Heading5.css │ │ │ │ │ │ │ ├── Heading5.tsx │ │ │ │ │ │ │ ├── Heading6.css │ │ │ │ │ │ │ ├── Heading6.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── horizontal-rule │ │ │ │ │ │ │ ├── HrElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── image │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── withImageElement.tsx │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── link │ │ │ │ │ │ │ ├── LinkElement.css │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── withLinkElement.tsx │ │ │ │ │ │ ├── list │ │ │ │ │ │ │ ├── ListItemContentElement.tsx │ │ │ │ │ │ │ ├── ListItemElement.css │ │ │ │ │ │ │ ├── ListItemElement.tsx │ │ │ │ │ │ │ ├── OrderedListElement.css │ │ │ │ │ │ │ ├── OrderedListElement.tsx │ │ │ │ │ │ │ ├── UnorderedListElement.css │ │ │ │ │ │ │ ├── UnorderedListElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── paragraph │ │ │ │ │ │ │ ├── ParagraphElement.css │ │ │ │ │ │ │ ├── ParagraphElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ ├── shortcode │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── withShortcodeElement.tsx │ │ │ │ │ │ └── table │ │ │ │ │ │ │ ├── Table.classes.ts │ │ │ │ │ │ │ ├── Table.css │ │ │ │ │ │ │ ├── TableCellElement │ │ │ │ │ │ │ ├── TableCellElement.tsx │ │ │ │ │ │ │ ├── TableHeaderCellElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── TableElement │ │ │ │ │ │ │ ├── TableElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ ├── TableRowElement │ │ │ │ │ │ │ ├── TableRowElement.tsx │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ │ │ └── index.ts │ │ │ │ │ └── toolbar │ │ │ │ │ │ ├── Toolbar.css │ │ │ │ │ │ ├── Toolbar.tsx │ │ │ │ │ │ └── index.ts │ │ │ │ ├── editableProps.ts │ │ │ │ ├── hooks │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ └── useMarkdownToSlate.ispec.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── useMarkdownToSlate.ts │ │ │ │ │ ├── useMdx.tsx │ │ │ │ │ ├── useToolbarButtons.css │ │ │ │ │ └── useToolbarButtons.tsx │ │ │ │ ├── index.ts │ │ │ │ ├── plateTypes.ts │ │ │ │ ├── plugins │ │ │ │ │ ├── align │ │ │ │ │ │ ├── alignPlugin.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── autoformat │ │ │ │ │ │ ├── autoformatBlocks.ts │ │ │ │ │ │ ├── autoformatLists.ts │ │ │ │ │ │ ├── autoformatMarks.ts │ │ │ │ │ │ ├── autoformatPlugin.ts │ │ │ │ │ │ ├── autoformatRules.ts │ │ │ │ │ │ ├── autoformatUtils.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── code-block │ │ │ │ │ │ ├── createCodeBlockPlugin.ts │ │ │ │ │ │ ├── deserializeHtmlCodeBlock.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── cursor-overlay │ │ │ │ │ │ ├── CursorOverlayContainer.tsx │ │ │ │ │ │ ├── cursorStore.ts │ │ │ │ │ │ ├── dragOverCursorPlugin.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── staticCursors.tsx │ │ │ │ │ ├── exit-break │ │ │ │ │ │ ├── exitBreakPlugin.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── indent │ │ │ │ │ │ ├── indentPlugin.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── list │ │ │ │ │ │ ├── createListPlugin.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── insertBreakList.ts │ │ │ │ │ │ ├── transforms │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ └── insertListItem.ts │ │ │ │ │ │ └── withList.ts │ │ │ │ │ ├── reset-node │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── resetBlockTypePlugin.ts │ │ │ │ │ ├── select-on-backspace │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── selectOnBackspacePlugin.ts │ │ │ │ │ ├── shortcode │ │ │ │ │ │ ├── createShortcodePlugin.ts │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── soft-break │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── softBreakPlugin.ts │ │ │ │ │ ├── table │ │ │ │ │ │ ├── createTablePlugin.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── withTable.ts │ │ │ │ │ └── trailing-block │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── trailingBlockPlugin.ts │ │ │ │ ├── serialization │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ └── serializeMarkdown.spec.ts │ │ │ │ │ ├── gfm.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── serializeMarkdown.ts │ │ │ │ │ └── slate │ │ │ │ │ │ ├── __tests__ │ │ │ │ │ │ ├── autoLinkUrls.spec.ts │ │ │ │ │ │ └── processShortcodeConfig.spec.ts │ │ │ │ │ │ ├── ast-types.ts │ │ │ │ │ │ ├── autoLinkUrls.ts │ │ │ │ │ │ ├── deserializeMarkdown.ts │ │ │ │ │ │ ├── flattenListItemParagraphs.ts │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ ├── processShortcodeConfig.ts │ │ │ │ │ │ └── toSlatePlugin.ts │ │ │ │ └── tests-util │ │ │ │ │ └── serializationTests.util.tsx │ │ │ ├── schema.ts │ │ │ └── withMarkdownControl.tsx │ │ │ ├── number │ │ │ ├── NumberControl.css │ │ │ ├── NumberControl.tsx │ │ │ ├── NumberPreview.tsx │ │ │ ├── __tests__ │ │ │ │ ├── NumberControl.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── validator.ts │ │ │ ├── object │ │ │ ├── ObjectControl.classes.ts │ │ │ ├── ObjectControl.css │ │ │ ├── ObjectControl.tsx │ │ │ ├── ObjectFieldWrapper.tsx │ │ │ ├── ObjectPreview.tsx │ │ │ ├── __tests__ │ │ │ │ └── ObjectControl.spec.tsx │ │ │ ├── index.ts │ │ │ └── schema.ts │ │ │ ├── relation │ │ │ ├── RelationControl.css │ │ │ ├── RelationControl.tsx │ │ │ ├── RelationPreview.tsx │ │ │ ├── RelationSummary.tsx │ │ │ ├── __tests__ │ │ │ │ ├── RelationControl.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ ├── types.ts │ │ │ ├── util.ts │ │ │ └── validator.ts │ │ │ ├── select │ │ │ ├── SelectControl.css │ │ │ ├── SelectControl.tsx │ │ │ ├── SelectPreview.tsx │ │ │ ├── __tests__ │ │ │ │ ├── SelectControl.spec.ts │ │ │ │ └── validator.spec.ts │ │ │ ├── index.ts │ │ │ ├── schema.ts │ │ │ └── validator.ts │ │ │ ├── string │ │ │ ├── StringControl.css │ │ │ ├── StringControl.tsx │ │ │ ├── StringPreview.tsx │ │ │ ├── __tests__ │ │ │ │ └── StringControl.spec.ts │ │ │ ├── index.ts │ │ │ └── schema.ts │ │ │ ├── text │ │ │ ├── TextControl.tsx │ │ │ ├── TextPreview.tsx │ │ │ ├── __tests__ │ │ │ │ └── TextControl.spec.ts │ │ │ ├── index.ts │ │ │ └── schema.ts │ │ │ └── uuid │ │ │ ├── UUIDControl.css │ │ │ ├── UUIDControl.tsx │ │ │ ├── UUIDPreview.tsx │ │ │ ├── __tests__ │ │ │ └── UUIDControl.spec.ts │ │ │ ├── index.ts │ │ │ └── schema.ts │ ├── tailwind.config.js │ ├── test │ │ ├── data │ │ │ ├── collections.mock.ts │ │ │ ├── config.mock.ts │ │ │ ├── entry.mock.ts │ │ │ ├── fields.mock.ts │ │ │ └── widgets.mock.ts │ │ ├── fileTransformer.js │ │ ├── globalSetup.js │ │ ├── harnesses │ │ │ └── widget.harness.tsx │ │ ├── mockFetch.ts │ │ ├── mockLocalStorage.ts │ │ ├── setupEnv.js │ │ ├── test-utils.tsx │ │ └── tsconfig.json │ ├── tsconfig.base.json │ ├── tsconfig.dev.json │ ├── tsconfig.json │ └── webpack.config.js ├── demo │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── package.json │ ├── public │ │ ├── _posts │ │ │ └── assets │ │ │ │ └── uploads │ │ │ │ ├── lobby.jpg │ │ │ │ └── moby-dick.jpg │ │ ├── assets │ │ │ └── uploads │ │ │ │ ├── Other Pics │ │ │ │ ├── lobby.jpg │ │ │ │ └── moby-dick.jpg │ │ │ │ ├── lobby.jpg │ │ │ │ └── moby-dick.jpg │ │ ├── editor-friendly-user-interface.svg │ │ ├── editorial │ │ │ └── config.yml │ │ ├── intuitive-workflow-for-content-teams.svg │ │ ├── simple │ │ │ └── config.yml │ │ └── static-cms-icon.png │ ├── src │ │ ├── cms.js │ │ ├── data.js │ │ ├── editorial │ │ │ └── index.html │ │ ├── index.html │ │ ├── index.jsx │ │ └── simple │ │ │ └── index.html │ └── vite.config.ts ├── docs │ ├── .editorconfig │ ├── .eslintignore │ ├── .eslintrc.js │ ├── .eslintrc.json │ ├── .gitignore │ ├── .prettierignore │ ├── .prettierrc │ ├── content │ │ ├── community.json │ │ ├── config.json │ │ ├── docs │ │ │ ├── add-to-your-site-bundling.mdx │ │ │ ├── add-to-your-site-cdn.mdx │ │ │ ├── add-to-your-site.mdx │ │ │ ├── additional-links.mdx │ │ │ ├── backends-overview.mdx │ │ │ ├── beta-features.mdx │ │ │ ├── bitbucket-backend.mdx │ │ │ ├── cms-events.mdx │ │ │ ├── collection-overview.mdx │ │ │ ├── collection-types.mdx │ │ │ ├── configuration-options.mdx │ │ │ ├── contributor-guide.mdx │ │ │ ├── custom-icons.mdx │ │ │ ├── custom-previews.mdx │ │ │ ├── custom-theme.mdx │ │ │ ├── custom-widgets.mdx │ │ │ ├── customization-overview.mdx │ │ │ ├── decap-migration-guide.mdx │ │ │ ├── editorial-workflow.mdx │ │ │ ├── examples.mdx │ │ │ ├── git-gateway-backend.mdx │ │ │ ├── gitea-backend.mdx │ │ │ ├── github-backend.mdx │ │ │ ├── gitlab-backend.mdx │ │ │ ├── i18n-support.mdx │ │ │ ├── intro.mdx │ │ │ ├── local-backend.mdx │ │ │ ├── migration-guide-v4.mdx │ │ │ ├── netlify-large-media.mdx │ │ │ ├── open-authoring.mdx │ │ │ ├── start-with-a-template.mdx │ │ │ ├── test-backend.mdx │ │ │ ├── typescript.mdx │ │ │ ├── updating-your-cms.mdx │ │ │ ├── widget-boolean.mdx │ │ │ ├── widget-code.mdx │ │ │ ├── widget-color.mdx │ │ │ ├── widget-datetime.mdx │ │ │ ├── widget-file.mdx │ │ │ ├── widget-hidden.mdx │ │ │ ├── widget-image.mdx │ │ │ ├── widget-keyvalue.mdx │ │ │ ├── widget-list.mdx │ │ │ ├── widget-map.mdx │ │ │ ├── widget-markdown.mdx │ │ │ ├── widget-number.mdx │ │ │ ├── widget-object.mdx │ │ │ ├── widget-relation.mdx │ │ │ ├── widget-select.mdx │ │ │ ├── widget-string.mdx │ │ │ ├── widget-text.mdx │ │ │ ├── widget-uuid.mdx │ │ │ ├── widgets.mdx │ │ │ └── writing-style-guide.mdx │ │ ├── homepage.json │ │ ├── menu.json │ │ └── releases.json │ ├── design │ │ └── your-content-your-way.xcf │ ├── next.config.js │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── icons │ │ │ ├── apple-touch-icon.png │ │ │ ├── favicon_maskable.png │ │ │ ├── icon-144x144.png │ │ │ ├── icon-192x192.png │ │ │ ├── icon-256x256.png │ │ │ ├── icon-32x32.png │ │ │ ├── icon-384x384.png │ │ │ ├── icon-48x48.png │ │ │ ├── icon-512x512.png │ │ │ ├── icon-72x72.png │ │ │ └── icon-96x96.png │ │ ├── img │ │ │ ├── 11ty-logo.svg │ │ │ ├── build-deployment.webp │ │ │ ├── cloudinary-console-details.webp │ │ │ ├── create-password.webp │ │ │ ├── create-remote-repo.webp │ │ │ ├── editor-friendly-user-interface.svg │ │ │ ├── email-subject.webp │ │ │ ├── gatsby.svg │ │ │ ├── hugo.svg │ │ │ ├── hugo_shortcode_gist.webp │ │ │ ├── instant-access-without-github-account.svg │ │ │ ├── intuitive-workflow-for-content-teams.svg │ │ │ ├── metalsmith.svg │ │ │ ├── middleman.svg │ │ │ ├── netlify-color-accent.svg │ │ │ ├── netlify-color-bg.svg │ │ │ ├── nextjs.svg │ │ │ ├── nuxt.svg │ │ │ ├── preact.svg │ │ │ ├── preview_card_grid.png │ │ │ ├── preview_draft_field_table.png │ │ │ ├── screenshot-editor.webp │ │ │ ├── widgets-markdown.webp │ │ │ └── your-content-your-way.webp │ │ ├── manifest.json │ │ ├── prism-dark.css │ │ ├── prism-light.css │ │ └── static-cms-logo.svg │ ├── src │ │ ├── components │ │ │ ├── DateDisplay.tsx │ │ │ ├── community │ │ │ │ └── CommunitySection.tsx │ │ │ ├── context │ │ │ │ └── ColorModeContext.tsx │ │ │ ├── docs │ │ │ │ ├── BetaImage.tsx │ │ │ │ ├── DeprecatedImage.tsx │ │ │ │ ├── DocsContent.tsx │ │ │ │ ├── DocsLeftNav.tsx │ │ │ │ ├── DocsLeftNavGroup.tsx │ │ │ │ ├── DocsRightNav.tsx │ │ │ │ ├── components │ │ │ │ │ ├── Alert.tsx │ │ │ │ │ ├── Anchor.tsx │ │ │ │ │ ├── Blockquote.tsx │ │ │ │ │ ├── CodeTabs.tsx │ │ │ │ │ ├── Deprecated.tsx │ │ │ │ │ ├── Template.tsx │ │ │ │ │ ├── Templates.tsx │ │ │ │ │ ├── headers │ │ │ │ │ │ ├── Header1.tsx │ │ │ │ │ │ ├── Header2.tsx │ │ │ │ │ │ ├── Header3.tsx │ │ │ │ │ │ ├── Header4.tsx │ │ │ │ │ │ ├── Header5.tsx │ │ │ │ │ │ ├── Header6.tsx │ │ │ │ │ │ ├── components │ │ │ │ │ │ │ ├── AnchorLinkIcon.tsx │ │ │ │ │ │ │ └── LinkedHeader.tsx │ │ │ │ │ │ └── hooks │ │ │ │ │ │ │ └── useAnchor.ts │ │ │ │ │ └── table │ │ │ │ │ │ ├── Table.tsx │ │ │ │ │ │ ├── TableBody.tsx │ │ │ │ │ │ ├── TableBodyCell.tsx │ │ │ │ │ │ ├── TableHead.tsx │ │ │ │ │ │ ├── TableHeaderCell.tsx │ │ │ │ │ │ └── TableRow.tsx │ │ │ │ └── table_of_contents │ │ │ │ │ ├── DocsHeadings.tsx │ │ │ │ │ └── DocsTableOfContents.tsx │ │ │ ├── layout │ │ │ │ ├── Container.tsx │ │ │ │ ├── Footer.tsx │ │ │ │ ├── Header.tsx │ │ │ │ ├── Logo.tsx │ │ │ │ ├── Page.tsx │ │ │ │ ├── mobile-drawer │ │ │ │ │ ├── MobileNavItem.tsx │ │ │ │ │ ├── MobileNavLink.tsx │ │ │ │ │ └── NavigationDrawer.tsx │ │ │ │ └── search │ │ │ │ │ ├── Search.tsx │ │ │ │ │ ├── SearchModal.tsx │ │ │ │ │ ├── SearchResult.tsx │ │ │ │ │ └── SuggestionLink.tsx │ │ │ └── meta │ │ │ │ ├── BasicMeta.tsx │ │ │ │ ├── JsonLdMeta.tsx │ │ │ │ ├── OpenGraphMeta.tsx │ │ │ │ └── TwitterCardMeta.tsx │ │ ├── constants.ts │ │ ├── interface.ts │ │ ├── lib │ │ │ ├── community.ts │ │ │ ├── config.ts │ │ │ ├── docs.ts │ │ │ ├── homepage.ts │ │ │ ├── menu.ts │ │ │ └── releases.ts │ │ ├── pages │ │ │ ├── _app.tsx │ │ │ ├── _document.tsx │ │ │ ├── community.tsx │ │ │ ├── docs │ │ │ │ └── [doc].tsx │ │ │ ├── index.tsx │ │ │ └── releases.tsx │ │ ├── styles │ │ │ ├── globals.css │ │ │ └── theme.ts │ │ └── util │ │ │ ├── node.util.ts │ │ │ ├── null.util.ts │ │ │ ├── search.util.ts │ │ │ ├── string.util.ts │ │ │ └── transientOptions.ts │ └── tsconfig.json └── tools │ ├── .gitignore │ ├── .prettierrc │ ├── babel.config.js │ ├── package.json │ ├── src │ └── localeSync.ts │ └── tsconfig.json ├── renovate.json ├── static-cms-icon.png ├── static-cms-logo.png ├── tailwind.base.config.js ├── tsconfig.json └── yarn.lock /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | dev-test 4 | *.js 5 | *.jsx 6 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [StaticJsCMS] 2 | open_collective: staticjscms 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependencies 2 | /node_modules 3 | *.log 4 | .vscode 5 | cypress/screenshots 6 | cypress/downloads 7 | .env 8 | .temp 9 | .nx 10 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | yarn lint-staged 5 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | public/ 4 | .cache/ 5 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "2.0.0", 3 | "tasks": [ 4 | { 5 | "type": "npm", 6 | "script": "dev", 7 | "problemMatcher": [], 8 | "label": "npm: dev", 9 | "detail": "lerna run dev --scope=@staticcms/core", 10 | "group": { 11 | "kind": "build", 12 | "isDefault": true 13 | } 14 | } 15 | ] 16 | } -------------------------------------------------------------------------------- /cypress.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "cypress"; 2 | import setupNodeEvents from "./cypress/plugins"; 3 | 4 | export default defineConfig({ 5 | projectId: "wvw3x3", 6 | retries: { 7 | runMode: 2, 8 | openMode: 0, 9 | }, 10 | chromeWebSecurity: false, 11 | e2e: { 12 | video: false, 13 | // We've imported your old cypress plugins here. 14 | // You may want to clean this up later by importing these. 15 | setupNodeEvents, 16 | baseUrl: "http://localhost:8080", 17 | specPattern: "cypress/e2e/*.spec.ts", 18 | }, 19 | }); 20 | -------------------------------------------------------------------------------- /cypress/e2e/_old/media_library_spec_test_backend.js: -------------------------------------------------------------------------------- 1 | import fixture from '../common/media_library'; 2 | 3 | const entries = [ 4 | { 5 | Title: 'first title', 6 | Body: 'first body', 7 | }, 8 | ]; 9 | 10 | describe('Test Backend Media Library', () => { 11 | after(() => { 12 | cy.task('teardownBackend', { backend: 'test' }); 13 | }); 14 | 15 | before(() => { 16 | Cypress.config('defaultCommandTimeout', 4000); 17 | cy.task('setupBackend', { backend: 'test' }); 18 | }); 19 | 20 | fixture({ entries }); 21 | }); 22 | -------------------------------------------------------------------------------- /cypress/e2e/simple_workflow_test_backend.spec.ts: -------------------------------------------------------------------------------- 1 | import fixture from './common/simple_workflow'; 2 | import * as specUtils from './common/spec_utils'; 3 | 4 | import type { TaskResult } from '../interface'; 5 | 6 | const backend = 'test'; 7 | 8 | describe('Test Backend Simple Workflow', () => { 9 | const taskResult: TaskResult = { data: {} }; 10 | 11 | before(() => { 12 | specUtils.before(taskResult, { publish_mode: 'simple' }, backend); 13 | }); 14 | 15 | after(() => { 16 | specUtils.after(backend); 17 | }); 18 | 19 | beforeEach(() => { 20 | specUtils.beforeEach(backend); 21 | }); 22 | 23 | afterEach(() => { 24 | specUtils.afterEach(backend); 25 | }); 26 | 27 | fixture(); 28 | }); 29 | -------------------------------------------------------------------------------- /cypress/fixtures/media/decap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/fixtures/media/decap.png -------------------------------------------------------------------------------- /cypress/fixtures/media/netlify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/fixtures/media/netlify.png -------------------------------------------------------------------------------- /cypress/plugins/testBackend.ts: -------------------------------------------------------------------------------- 1 | import merge from 'lodash/merge'; 2 | import { updateConfig } from '../utils/config'; 3 | 4 | import type { Config } from '@staticcms/core/interface'; 5 | import type { SetupBackendResponse } from '../interface'; 6 | 7 | export async function setupTestBackend(options: Partial): Promise { 8 | await updateConfig(current => { 9 | merge(current, options); 10 | }); 11 | 12 | return null; 13 | } 14 | -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can delete image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can delete image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can publish entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can publish entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can save entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can save entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can upload image from entry media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can upload image from entry media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can upload image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- can upload image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- should not show draft entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- should not show draft entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- should show published entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- should show published entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- should show published entry image in grid view.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_git-gateway_backend_large_media.js/Git Gateway Backend Media Library - Large Media -- should show published entry image in grid view.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can delete image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can delete image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can publish entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can publish entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can save entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can save entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can upload image from entry media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can upload image from entry media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can upload image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- can upload image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- should not show draft entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- should not show draft entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- should show published entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- should show published entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- should show published entry image in grid view.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_graphql.js/GitHub Backend Media Library - GraphQL API -- should show published entry image in grid view.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can delete image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can delete image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can publish entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can publish entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can save entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can save entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can upload image from entry media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can upload image from entry media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can upload image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- can upload image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- should not show draft entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- should not show draft entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- should show published entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- should show published entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- should show published entry image in grid view.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_github_backend_rest.js/GitHub Backend Media Library - REST API -- should show published entry image in grid view.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can delete image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can delete image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can publish entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can publish entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can save entry with image.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can save entry with image.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can upload image from entry media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can upload image from entry media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can upload image from global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- can upload image from global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- should not show draft entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- should not show draft entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- should show published entry image in global media library.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- should show published entry image in global media library.snap.png -------------------------------------------------------------------------------- /cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- should show published entry image in grid view.snap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/cypress/snapshots/media_library_spec_test_backend.js/Test Backend Media Library -- should show published entry image in grid view.snap.png -------------------------------------------------------------------------------- /cypress/utils/README.md: -------------------------------------------------------------------------------- 1 | ## Utilities for integration tests 2 | 3 | Utils in this dir must be explicitly included in each spec file. 4 | 5 | For routines to be executed on all tests, please use the `cypress/plugins.index.js` file instead: https://docs.cypress.io/guides/core-concepts/writing-and-organizing-tests.html#Plugin-files 6 | -------------------------------------------------------------------------------- /cypress/utils/regexp.ts: -------------------------------------------------------------------------------- 1 | export const escapeRegExp = (str: string) => { 2 | return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); 3 | }; 4 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "node_modules/lerna/schemas/lerna-schema.json", 3 | "version": "4.3.0" 4 | } 5 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [[plugins]] 2 | package = "@netlify/plugin-nextjs" 3 | 4 | [[redirects]] 5 | from = "https://staticjscms.netlify.app/*" 6 | to = "https://staticjscms.netlify.app/:splat" 7 | status = 301 8 | 9 | [[redirects]] 10 | from = "https://staticjscms.netlify.app/" 11 | to = "https://staticjscms.netlify.app/" 12 | status = 301 13 | -------------------------------------------------------------------------------- /packages/app/.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.js] 12 | quote_type = single 13 | spaces_around_operators = true 14 | 15 | [*.css] 16 | quote_type = single 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | 21 | [*.mdx] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /packages/app/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | dev-test 4 | .js 5 | -------------------------------------------------------------------------------- /packages/app/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | public/ 4 | .cache/ -------------------------------------------------------------------------------- /packages/app/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /packages/app/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'tailwindcss/nesting': {}, 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/app/src/index.ts: -------------------------------------------------------------------------------- 1 | import CMS from '@staticcms/core'; 2 | 3 | export * from '@staticcms/core'; 4 | export default CMS; 5 | -------------------------------------------------------------------------------- /packages/app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../tailwind.base.config'); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: ['../core/src/**/*.tsx'], 6 | ...baseConfig, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.js] 12 | quote_type = single 13 | spaces_around_operators = true 14 | 15 | [*.css] 16 | quote_type = single 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | 21 | [*.mdx] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /packages/core/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | dev-test 4 | *.js 5 | -------------------------------------------------------------------------------- /packages/core/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | public/ 4 | .cache/ 5 | -------------------------------------------------------------------------------- /packages/core/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/dev-test/_posts/assets/uploads/lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/_posts/assets/uploads/lobby.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/_posts/assets/uploads/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/_posts/assets/uploads/moby-dick.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/assets/uploads/Other Pics/lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/assets/uploads/Other Pics/lobby.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/assets/uploads/Other Pics/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/assets/uploads/Other Pics/moby-dick.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/assets/uploads/lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/assets/uploads/lobby.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/assets/uploads/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/assets/uploads/moby-dick.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/bitbucket/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Static CMS - Bitbucket Development Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/git-gateway/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Static CMS - Git Gateway Development Test 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/github/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Static CMS - GitHub Development Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/gitlab/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Static CMS - GitLab Development Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_data/authors.yml: -------------------------------------------------------------------------------- 1 | authors: 2 | - name: George 3 | description: A person I know 4 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_data/navigation.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | main_menu: 3 | - label: News 4 | href: /news 5 | - label: Features 6 | href: '#features' 7 | - label: About 8 | href: '#about' 9 | - label: Contact 10 | href: '#contact' 11 | footer_menus: 12 | - label: Company 13 | links: 14 | - label: Home 15 | href: / 16 | - label: 'Privacy Policy ' 17 | href: '#' 18 | - label: About us 19 | href: '#' 20 | - label: Documentation 21 | links: 22 | - label: Docs 23 | href: '#' 24 | - label: Blog 25 | href: '#' 26 | --- 27 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_data/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "front_limit": 6, 3 | "site_title": "Test", 4 | "posts": { 5 | "front_limit": 4, 6 | "author": "Bob", 7 | "thumb": "/backends/proxy/assets/upload/kanefreeman_2.jpg" 8 | }, 9 | "icons": [ 10 | { 11 | "name": "right", 12 | "icon": "/src/icons/right.svg" 13 | }, 14 | { 15 | "name": "left", 16 | "icon": "/src/icons/left.svg" 17 | }, 18 | { 19 | "name": "download", 20 | "icon": "/src/icons/download.svg" 21 | }, 22 | { 23 | "name": "zoom", 24 | "icon": "/src/icons/zoom.svg" 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_faq/a-question.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: A Question 3 | posts: 4 | --- 5 | Here's the body! 6 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_i18n_playground/file1.de.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Kaffee ist ein kleiner Baum oder Strauch, der in seiner wilden Form 3 | im Unterholz des Waldes wächst und traditionell kommerziell unter anderen 4 | Bäumen angebaut wurde, die Schatten spendeten. Die waldähnliche Struktur 5 | schattiger Kaffeefarmen bietet Lebensraum für eine Vielzahl von wandernden und 6 | ansässigen Arten. 7 | date: 2023-01-18T14:41:54.252-05:00 8 | --- 9 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_i18n_playground/file1.en.md: -------------------------------------------------------------------------------- 1 | --- 2 | slug: file1 3 | description: Coffee is a small tree or shrub that grows in the forest understory 4 | in its wild form, and traditionally was grown commercially under other trees 5 | that provided shade. The forest-like structure of shade coffee farms provides 6 | habitat for a great number of migratory and resident species. 7 | date: 2023-01-18T14:41:54.252-05:00 8 | --- 9 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_i18n_playground/file1.fr.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Le café est un petit arbre ou un arbuste qui pousse dans le 3 | sous-étage de la forêt sous sa forme sauvage et qui était traditionnellement 4 | cultivé commercialement sous d\'autres arbres qui fournissaient de l\'ombre. 5 | La structure forestière des plantations de café d\'ombre fournit un habitat à 6 | un grand nombre d\'espèces migratrices et résidentes. 7 | date: 2023-01-18T14:41:54.252-05:00 8 | --- 9 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_nested_pages/authors/author-1/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: An Author 3 | --- 4 | 5 | Author details go here!. 6 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_nested_pages/authors/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Authors 3 | --- 4 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_nested_pages/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Pages 3 | --- 4 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_nested_pages/posts/hello-world/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Hello World 3 | --- 4 | 5 | Coffee is a small tree or shrub that grows in the forest understory in its wild form, and traditionally was grown commercially under other trees that provided shade. The forest-like structure of shade coffee farms provides habitat for a great number of migratory and resident species. 6 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_nested_pages/posts/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Posts 3 | --- 4 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-01-something/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Something something something2... 3 | date: 2022-11-01 06:30 4 | image: static-cms-icon.svg 5 | --- 6 | # Welcome 7 | 8 | Here is your body! 9 | 10 | And some more information! 11 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-01-something/ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/_posts/2022-11-01-something/ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-01-test/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Test 3 | date: 2022-11-01 14:28 4 | image: kittens.jpg 5 | --- 6 | Test234t6 7 | 8 | [Test](https://example.com) 9 | 10 | ![Kittens!](kittens.jpg) 11 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-01-test/kittens.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/_posts/2022-11-01-test/kittens.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-01-test/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/_posts/2022-11-01-test/moby-dick.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-02-test/index.md: -------------------------------------------------------------------------------- 1 | --- 2 | title: Test3 3 | date: 2022-11-02 08:43 4 | image: ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg 5 | --- 6 | test2555556 7 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-02-test/kanefreeman_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/_posts/2022-11-02-test/kanefreeman_2.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/_posts/2022-11-02-test/ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/_posts/2022-11-02-test/ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/posts/kanefreeman_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/posts/kanefreeman_2.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/posts/kittens.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/posts/kittens.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/posts/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/posts/moby-dick.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/posts/ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/posts/ori_3587884_d966kldqzc6mvdeq67hyk16rnbe3gb1k8eeoy31s_shark-icon.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/document.txt: -------------------------------------------------------------------------------- 1 | Some text here! 2 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/kanefreeman-pre.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/kanefreeman-pre.png -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/kanefreeman_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/kanefreeman_2.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/kanefreeman_widescreen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/kanefreeman_widescreen.png -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/kittens.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/kittens.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/moby-dick.jpg -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/nf-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/nf-logo.png -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/shortfin-mako-shark-seas.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/shortfin-mako-shark-seas.webp -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/assets/upload/static-cms-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/core/dev-test/backends/proxy/assets/upload/static-cms-logo.png -------------------------------------------------------------------------------- /packages/core/dev-test/backends/proxy/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Static CMS - Proxy Development Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/core/dev-test/backends/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Static CMS Development Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/core/dev-test/example.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | color: #444; 4 | font-size: 14px; 5 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; 6 | } 7 | 8 | body { 9 | padding: 20px; 10 | } 11 | 12 | h1 { 13 | margin-top: 20px; 14 | color: #666; 15 | font-weight: bold; 16 | font-size: 32px; 17 | } 18 | 19 | img { 20 | max-width: 100%; 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/dev-test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Static CMS Development Test 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/core/jest.config.integration.js: -------------------------------------------------------------------------------- 1 | const { pathsToModuleNameMapper } = require('ts-jest'); 2 | 3 | const { compilerOptions } = require('./tsconfig.base'); 4 | 5 | module.exports = { 6 | preset: 'ts-jest', 7 | transform: { 8 | '\\.[jt]sx?$': ['ts-jest', { tsconfig: 'tsconfig.dev.json' }], 9 | '^.+\\.svg$': './test/fileTransformer', 10 | }, 11 | moduleNameMapper: { 12 | ...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/' }), 13 | '\\.(css|less)$': '/src/__mocks__/styleMock.ts', 14 | }, 15 | transformIgnorePatterns: [], 16 | setupFiles: ['./test/setupEnv.js'], 17 | testRegex: '\\.ispec\\.tsx?$', 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/jest.config.js: -------------------------------------------------------------------------------- 1 | const { pathsToModuleNameMapper } = require('ts-jest'); 2 | 3 | const { compilerOptions } = require('./tsconfig.base'); 4 | 5 | module.exports = { 6 | preset: 'ts-jest', 7 | transform: { 8 | '\\.[jt]sx?$': ['ts-jest', { tsconfig: 'tsconfig.dev.json' }], 9 | '^.+\\.svg$': './test/fileTransformer', 10 | }, 11 | moduleNameMapper: { 12 | ...pathsToModuleNameMapper(compilerOptions.paths, { prefix: '/' }), 13 | '\\.(css|less)$': '/src/__mocks__/styleMock.ts', 14 | }, 15 | setupFiles: ['./test/setupEnv.js'], 16 | globalSetup: './test/globalSetup.js', 17 | testRegex: '\\.spec\\.tsx?$', 18 | }; 19 | -------------------------------------------------------------------------------- /packages/core/postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | 'tailwindcss/nesting': {}, 4 | tailwindcss: {}, 5 | autoprefixer: {}, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/@udecode/plate-core.ts: -------------------------------------------------------------------------------- 1 | export const someNode = jest.fn(); 2 | export const toggleNodeType = jest.fn(); 3 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/@udecode/plate-list.ts: -------------------------------------------------------------------------------- 1 | export const getListItemEntry = jest.fn(); 2 | export const toggleList = jest.fn(); 3 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/array-move.ts: -------------------------------------------------------------------------------- 1 | export default jest.fn(); 2 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/clean-stack.ts: -------------------------------------------------------------------------------- 1 | export default function cleanStack(stack: string) { 2 | return stack; 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/copy-text-to-clipboard.ts: -------------------------------------------------------------------------------- 1 | export default jest.fn(); 2 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/localforage.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | const mock = { 3 | storage: {}, 4 | setItem: jest.fn().mockImplementation(function (this: any, key: string, data: any) { 5 | this.storage[key] = data; 6 | return Promise.resolve(); 7 | }), 8 | getItem: jest.fn().mockImplementation(function (this: any, key: string) { 9 | return Promise.resolve(this.storage[key]); 10 | }), 11 | removeItem: jest.fn().mockImplementation(function (this: any, key: string) { 12 | delete this.storage[key]; 13 | return Promise.resolve(); 14 | }), 15 | }; 16 | 17 | export default mock; 18 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/react-markdown.ts: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import type { FC, PropsWithChildren } from 'react'; 4 | 5 | const ReactMarkdown: FC = ({ children }) => { 6 | return React.createElement('div', {}, [children]); 7 | }; 8 | 9 | export default ReactMarkdown; 10 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/react-polyglot.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/display-name */ 2 | /* eslint-disable import/prefer-default-export */ 3 | import React from 'react'; 4 | 5 | import type { FC } from 'react'; 6 | 7 | export const translate = () => (Component: FC) => { 8 | const t = (key: string, _options: unknown) => key; 9 | 10 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 11 | return (props: any) => { 12 | return React.createElement(Component, { t, ...props }); 13 | }; 14 | }; 15 | 16 | export const useTranslate = () => (key: string, _options: unknown) => key; 17 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/react-virtualized-auto-sizer.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | /* eslint-disable react/display-name */ 3 | import React from 'react'; 4 | 5 | export default function (props: any) { 6 | const { children } = props; 7 | return React.createElement('div', {}, children({ width: 500, height: 1000 })); 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/react-waypoint.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import React from 'react'; 3 | 4 | export function Waypoint() { 5 | return React.createElement('div', {}, []); 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/remark-gfm.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/remark-mdx.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/remark-parse.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/slate-react.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export const useFocused = jest.fn(); 3 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/styleMock.ts: -------------------------------------------------------------------------------- 1 | export default {}; 2 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/unified.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | const unifiedMock = jest.fn().mockImplementation(() => { 3 | return { 4 | use: unifiedMock, 5 | process: unifiedMock, 6 | }; 7 | }); 8 | 9 | export { unifiedMock as unified }; 10 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/url-join.ts: -------------------------------------------------------------------------------- 1 | export default function urlJoin(...parts: string[]) { 2 | return parts.join('/'); 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/uuid.ts: -------------------------------------------------------------------------------- 1 | export const v4 = jest.fn().mockReturnValue('I_AM_A_UUID'); 2 | 3 | export const validate = jest.fn(); 4 | -------------------------------------------------------------------------------- /packages/core/src/__mocks__/yaml.ts: -------------------------------------------------------------------------------- 1 | export const isNode = jest.fn(); 2 | export const isMap = jest.fn(); 3 | export const parse = jest.fn(); 4 | 5 | export default {}; 6 | -------------------------------------------------------------------------------- /packages/core/src/actions/globalUI.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { THEME_CHANGE, USE_OPEN_AUTHORING } from '../constants'; 3 | 4 | export function useOpenAuthoring() { 5 | return { 6 | type: USE_OPEN_AUTHORING, 7 | } as const; 8 | } 9 | 10 | export function changeTheme(theme: string) { 11 | return { type: THEME_CHANGE, payload: theme } as const; 12 | } 13 | 14 | export type GlobalUIAction = ReturnType; 15 | -------------------------------------------------------------------------------- /packages/core/src/backends/bitbucket/index.ts: -------------------------------------------------------------------------------- 1 | export { default as BitbucketBackend } from './implementation'; 2 | export { default as API } from './API'; 3 | export { default as AuthenticationPage } from './AuthenticationPage'; 4 | -------------------------------------------------------------------------------- /packages/core/src/backends/git-gateway/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GitGatewayBackend } from './implementation'; 2 | export { default as AuthenticationPage } from './AuthenticationPage'; 3 | -------------------------------------------------------------------------------- /packages/core/src/backends/gitea/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GiteaBackend } from './implementation'; 2 | export { default as API } from './API'; 3 | export { default as AuthenticationPage } from './AuthenticationPage'; 4 | -------------------------------------------------------------------------------- /packages/core/src/backends/github/AuthenticationPage.css: -------------------------------------------------------------------------------- 1 | .CMS_Github_AuthenticationPage_fork-approve-container { 2 | @apply flex 3 | flex-col 4 | flex-nowrap 5 | justify-around 6 | flex-grow-[0.2]; 7 | } 8 | 9 | .CMS_Github_AuthenticationPage_fork-text { 10 | @apply max-w-[600px] 11 | w-full 12 | px-2 13 | my-2 14 | justify-center 15 | items-center 16 | text-center; 17 | } 18 | 19 | .CMS_Github_AuthenticationPage_fork-buttons { 20 | @apply flex 21 | flex-col 22 | flex-nowrap 23 | justify-around 24 | items-center 25 | gap-2; 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/backends/github/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GitHubBackend } from './implementation'; 2 | export { default as API } from './API'; 3 | export { default as AuthenticationPage } from './AuthenticationPage'; 4 | -------------------------------------------------------------------------------- /packages/core/src/backends/github/mutations.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { gql } from 'graphql-tag'; 3 | 4 | import * as fragments from './fragments'; 5 | 6 | // updateRef only works for branches at the moment 7 | export const updateBranch = gql` 8 | mutation updateRef($input: UpdateRefInput!) { 9 | updateRef(input: $input) { 10 | branch: ref { 11 | ...BranchParts 12 | } 13 | } 14 | } 15 | ${fragments.branch} 16 | `; 17 | -------------------------------------------------------------------------------- /packages/core/src/backends/gitlab/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GitLabBackend } from './implementation'; 2 | export { default as API } from './API'; 3 | export { default as AuthenticationPage } from './AuthenticationPage'; 4 | -------------------------------------------------------------------------------- /packages/core/src/backends/index.ts: -------------------------------------------------------------------------------- 1 | export { BitbucketBackend } from './bitbucket'; 2 | export { GitGatewayBackend } from './git-gateway'; 3 | export { GitHubBackend } from './github'; 4 | export { GitLabBackend } from './gitlab'; 5 | export { GiteaBackend } from './gitea'; 6 | export { ProxyBackend } from './proxy'; 7 | export { TestBackend } from './test'; 8 | -------------------------------------------------------------------------------- /packages/core/src/backends/proxy/AuthenticationPage.tsx: -------------------------------------------------------------------------------- 1 | import React, { useCallback } from 'react'; 2 | 3 | import Login from '@staticcms/core/components/login/Login'; 4 | 5 | import type { AuthenticationPageProps } from '@staticcms/core'; 6 | import type { FC, MouseEvent } from 'react'; 7 | 8 | const AuthenticationPage: FC = ({ inProgress = false, onLogin }) => { 9 | const handleLogin = useCallback( 10 | (e: MouseEvent) => { 11 | e.preventDefault(); 12 | onLogin({ token: 'fake_token' }); 13 | }, 14 | [onLogin], 15 | ); 16 | 17 | return ; 18 | }; 19 | 20 | export default AuthenticationPage; 21 | -------------------------------------------------------------------------------- /packages/core/src/backends/proxy/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ProxyBackend } from './implementation'; 2 | export { default as AuthenticationPage } from './AuthenticationPage'; 3 | -------------------------------------------------------------------------------- /packages/core/src/backends/test/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TestBackend } from './implementation'; 2 | export { default as AuthenticationPage } from './AuthenticationPage'; 3 | -------------------------------------------------------------------------------- /packages/core/src/components/App.css: -------------------------------------------------------------------------------- 1 | .CMS_App_root { 2 | @apply h-full; 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/components/MainView.css: -------------------------------------------------------------------------------- 1 | .CMS_MainView_root { 2 | background: var(--background-dark); 3 | 4 | @apply flex; 5 | } 6 | 7 | .CMS_MainView_body { 8 | @apply h-main-mobile 9 | md:h-main 10 | relative 11 | w-full; 12 | 13 | &.CMS_MainView_show-left-nav { 14 | @apply left-0 15 | md:w-main; 16 | } 17 | 18 | &:not(.CMS_MainView_no-margin) { 19 | @apply px-5 20 | py-4; 21 | } 22 | 23 | &.CMS_MainView_no-scroll { 24 | @apply overflow-hidden; 25 | } 26 | 27 | &:not(.CMS_MainView_no-scroll) { 28 | @apply overflow-y-auto; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/components/NotFoundPage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import useTranslate from '../lib/hooks/useTranslate'; 4 | 5 | import type { FC } from 'react'; 6 | 7 | const NotFoundPage: FC = () => { 8 | const t = useTranslate(); 9 | 10 | return ( 11 |
12 |

{t('app.notFoundPage.header')}

13 |
14 | ); 15 | }; 16 | 17 | export default NotFoundPage; 18 | -------------------------------------------------------------------------------- /packages/core/src/components/collections/Collection.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const collectionClasses = generateClassNames('Collection', [ 4 | 'root', 5 | 'content', 6 | 'search-query', 7 | 'description', 8 | 'description-card', 9 | 'controls', 10 | 'header-wrapper', 11 | 'header', 12 | 'header-icon', 13 | 'header-label', 14 | 'new-entry-button', 15 | 'new-entry-button-text', 16 | ]); 17 | 18 | export default collectionClasses; 19 | -------------------------------------------------------------------------------- /packages/core/src/components/collections/entries/Entries.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const entriesClasses = generateClassNames('Entries', [ 4 | 'no-entries', 5 | 'group', 6 | 'group-content-wrapper', 7 | 'group-content', 8 | 'group-button', 9 | 'entry-listing', 10 | 'entry-listing-loading', 11 | 'entry-listing-grid', 12 | 'entry-listing-grid-container', 13 | 'entry-listing-cards', 14 | 'entry-listing-cards-grid-wrapper', 15 | 'entry-listing-cards-grid', 16 | 'entry-listing-table', 17 | 'entry-listing-table-content', 18 | 'entry-listing-table-row', 19 | 'entry-listing-local-backup', 20 | ]); 21 | 22 | export default entriesClasses; 23 | -------------------------------------------------------------------------------- /packages/core/src/components/collections/mobile/MobileCollectionControls.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const mobileCollectionControlsClasses = generateClassNames('MobileCollectionControls', [ 4 | 'root', 5 | 'content', 6 | 'toggle', 7 | ]); 8 | 9 | export default mobileCollectionControlsClasses; 10 | -------------------------------------------------------------------------------- /packages/core/src/components/collections/mobile/MobileCollectionControls.css: -------------------------------------------------------------------------------- 1 | .CMS_MobileCollectionControls_root { 2 | @apply w-[80%] 3 | max-w-[240px]; 4 | 5 | & .MuiBackdrop-root { 6 | @apply w-full; 7 | } 8 | 9 | & .MuiDrawer-paper { 10 | @apply box-border 11 | w-[80%] 12 | max-w-[240px]; 13 | } 14 | } 15 | 16 | .CMS_MobileCollectionControls_content { 17 | background: var(--background-main); 18 | 19 | @apply px-5 20 | py-4 21 | flex 22 | flex-col 23 | gap-6 24 | h-full 25 | w-full 26 | overflow-y-auto; 27 | } 28 | 29 | .CMS_MobileCollectionControls_toggle { 30 | @apply flex 31 | lg:!hidden; 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/components/common/alert/Alert.css: -------------------------------------------------------------------------------- 1 | .CMS_Alert_root { 2 | @apply w-[50%] 3 | min-w-[300px] 4 | max-w-[600px]; 5 | } 6 | 7 | .CMS_Alert_title { 8 | @apply px-6 9 | py-4 10 | text-xl; 11 | } 12 | 13 | .CMS_Alert_content { 14 | color: var(--text-secondary); 15 | 16 | @apply px-6 17 | pb-4 18 | text-sm; 19 | } 20 | 21 | .CMS_Alert_actions { 22 | @apply p-2 23 | flex 24 | justify-end 25 | gap-2; 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/components/common/button/Button.css: -------------------------------------------------------------------------------- 1 | .CMS_Button_start-icon { 2 | @apply w-5 3 | h-5 4 | mr-2; 5 | } 6 | 7 | .CMS_Button_end-icon { 8 | @apply w-5 9 | h-5 10 | ml-2; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/components/common/button/IconButton.css: -------------------------------------------------------------------------------- 1 | .CMS_IconButton_root { 2 | &.CMS_IconButton_sm { 3 | @apply px-0.5; 4 | 5 | & .CMS_IconButton_icon { 6 | @apply h-4 7 | w-4; 8 | } 9 | } 10 | 11 | &.CMS_IconButton_md { 12 | @apply px-1.5; 13 | 14 | & .CMS_IconButton_icon { 15 | @apply h-5 16 | w-5; 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/components/common/card/Card.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const cardClasses = generateClassNames('Card', [ 4 | 'root', 5 | 'header', 6 | 'content', 7 | 'media', 8 | 'link-action', 9 | 'button-action', 10 | ]); 11 | 12 | export default cardClasses; 13 | -------------------------------------------------------------------------------- /packages/core/src/components/common/card/Card.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import classNames from '@staticcms/core/lib/util/classNames.util'; 4 | import cardClasses from './Card.classes'; 5 | 6 | import type { FC, ReactNode } from 'react'; 7 | 8 | import './Card.css'; 9 | 10 | interface CardProps { 11 | children: ReactNode | ReactNode[]; 12 | className?: string; 13 | title?: string; 14 | } 15 | 16 | const Card: FC = ({ children, className, title }) => { 17 | return ( 18 |
19 | {children} 20 |
21 | ); 22 | }; 23 | 24 | export default Card; 25 | -------------------------------------------------------------------------------- /packages/core/src/components/common/card/CardContent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import cardClasses from './Card.classes'; 4 | 5 | import type { FC, ReactNode } from 'react'; 6 | 7 | interface CardContentProps { 8 | children: ReactNode; 9 | } 10 | 11 | const CardContent: FC = ({ children }) => { 12 | return
{children}
; 13 | }; 14 | 15 | export default CardContent; 16 | -------------------------------------------------------------------------------- /packages/core/src/components/common/card/CardHeader.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import cardClasses from './Card.classes'; 4 | 5 | import type { FC, ReactNode } from 'react'; 6 | 7 | interface CardHeaderProps { 8 | children: ReactNode; 9 | } 10 | 11 | const CardHeader: FC = ({ children }) => { 12 | return
{children}
; 13 | }; 14 | 15 | export default CardHeader; 16 | -------------------------------------------------------------------------------- /packages/core/src/components/common/confirm/Confirm.css: -------------------------------------------------------------------------------- 1 | .CMS_Confirm_root { 2 | @apply w-[85%] 3 | min-w-[300px] 4 | max-w-[560px] 5 | shadow-xl 6 | drop-shadow-lg; 7 | } 8 | 9 | .CMS_Confirm_title { 10 | @apply px-6 11 | py-4 12 | text-xl; 13 | } 14 | 15 | .CMS_Confirm_content { 16 | color: var(--text-secondary); 17 | 18 | @apply px-6 19 | pb-4 20 | text-sm; 21 | } 22 | 23 | .CMS_Confirm_actions { 24 | @apply p-2 25 | flex 26 | justify-end 27 | gap-2; 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/components/common/field/ErrorMessage.css: -------------------------------------------------------------------------------- 1 | .CMS_ErrorMessage_root { 2 | color: var(--error-main); 3 | 4 | @apply flex 5 | w-full 6 | text-xs 7 | px-3 8 | pt-2; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/components/common/field/Hint.css: -------------------------------------------------------------------------------- 1 | .CMS_Hint_root { 2 | @apply w-full 3 | text-xs; 4 | 5 | &:not(.CMS_Hint_inline) { 6 | @apply px-3 7 | py-1; 8 | } 9 | } 10 | 11 | .CMS_Hint_link { 12 | color: inherit; 13 | 14 | &:hover { 15 | @apply underline; 16 | } 17 | } 18 | 19 | .CMS_Hint_cursor-pointer { 20 | @apply cursor-pointer; 21 | } 22 | 23 | .CMS_Hint_cursor-text { 24 | @apply cursor-text; 25 | } 26 | 27 | .CMS_Hint_cursor-default { 28 | @apply cursor-default; 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/components/common/field/Label.css: -------------------------------------------------------------------------------- 1 | .CMS_Label_root { 2 | @apply w-full 3 | flex 4 | text-xs 5 | font-bold; 6 | 7 | &:not(.CMS_Label_inline) { 8 | @apply px-3 9 | pt-3; 10 | } 11 | } 12 | 13 | .CMS_Label_cursor-pointer { 14 | @apply cursor-pointer; 15 | } 16 | 17 | .CMS_Label_cursor-text { 18 | @apply cursor-text; 19 | } 20 | 21 | .CMS_Label_cursor-default { 22 | @apply cursor-default; 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/components/common/image/Image.css: -------------------------------------------------------------------------------- 1 | .CMS_Image_root { 2 | &:not(.CMS_Image_empty) { 3 | @apply object-cover 4 | max-w-full 5 | overflow-hidden; 6 | } 7 | 8 | &.CMS_Image_empty { 9 | border-color: var(--background-main); 10 | 11 | @apply p-10 12 | rounded-md 13 | border 14 | max-w-full; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/components/common/menu/MenuGroup.css: -------------------------------------------------------------------------------- 1 | .CMS_MenuGroup_root { 2 | border-color: var(--background-divider); 3 | 4 | @apply py-1 5 | border-b 6 | last:border-0; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/components/common/menu/MenuGroup.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { FC, ReactNode } from 'react'; 6 | 7 | import './MenuGroup.css'; 8 | 9 | export const classes = generateClassNames('MenuGroup', ['root']); 10 | 11 | export interface MenuGroupProps { 12 | children: ReactNode | ReactNode[]; 13 | } 14 | 15 | const MenuGroup: FC = ({ children }) => { 16 | return
{children}
; 17 | }; 18 | 19 | export default MenuGroup; 20 | -------------------------------------------------------------------------------- /packages/core/src/components/common/modal/Backdrop.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import classNames from '@staticcms/core/lib/util/classNames.util'; 4 | import modalClasses from './Modal.classes'; 5 | 6 | const Backdrop = React.forwardRef< 7 | HTMLDivElement, 8 | { open?: boolean; className: string; ownerState: unknown } 9 | >((props, ref) => { 10 | const { open, className, ownerState: _ownerState, ...other } = props; 11 | return ( 12 |
17 | ); 18 | }); 19 | 20 | Backdrop.displayName = 'Backdrop'; 21 | 22 | export default Backdrop; 23 | -------------------------------------------------------------------------------- /packages/core/src/components/common/modal/Modal.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const modalClasses = generateClassNames('Modal', ['root', 'content', 'backdrop']); 4 | 5 | export default modalClasses; 6 | -------------------------------------------------------------------------------- /packages/core/src/components/common/modal/Modal.css: -------------------------------------------------------------------------------- 1 | .CMS_Modal_root { 2 | @apply fixed 3 | inset-0 4 | overflow-y-auto 5 | z-[100] 6 | flex 7 | min-h-full 8 | items-center 9 | justify-center 10 | text-center; 11 | } 12 | 13 | .CMS_Modal_backdrop { 14 | background: color-mix(in srgb, var(--background-dark) 50%, transparent); 15 | 16 | @apply fixed 17 | inset-0 18 | z-[100]; 19 | } 20 | 21 | .CMS_Modal_content { 22 | background: var(--background-main); 23 | 24 | @apply transform 25 | overflow-visible 26 | rounded-lg 27 | text-left 28 | align-middle 29 | shadow-xl 30 | transition-all 31 | z-[101] 32 | outline-none; 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/components/common/progress/CircularProgress.css: -------------------------------------------------------------------------------- 1 | .CMS_CircularProgress_svg { 2 | color: var(--text-secondary); 3 | fill: var(--primary-main); 4 | 5 | @apply animate-spin; 6 | 7 | &.CMS_CircularProgress_md { 8 | @apply w-8 9 | h-8; 10 | } 11 | 12 | &.CMS_CircularProgress_sm { 13 | @apply w-5 14 | h-5; 15 | } 16 | } 17 | 18 | .CMS_CircularProgress_sr-label { 19 | @apply sr-only; 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/components/common/progress/Loader.css: -------------------------------------------------------------------------------- 1 | .CMS_Loader_root { 2 | background: var(--background-dark); 3 | 4 | @apply absolute 5 | inset-0 6 | flex 7 | flex-col 8 | gap-2 9 | items-center 10 | justify-center; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/components/common/select/Option.css: -------------------------------------------------------------------------------- 1 | .CMS_SelectOption_root { 2 | color: var(--text-primary); 3 | 4 | @apply relative 5 | select-none 6 | py-2 7 | px-4 8 | cursor-pointer; 9 | 10 | &:hover { 11 | background: var(--primary-main); 12 | } 13 | 14 | &.CMS_SelectOption_selected { 15 | background: var(--primary-main); 16 | 17 | & .CMS_SelectOption_label { 18 | @apply font-medium; 19 | } 20 | } 21 | 22 | &:not(.CMS_SelectOption_selected) { 23 | & .CMS_SelectOption_label { 24 | @apply font-normal; 25 | } 26 | } 27 | } 28 | 29 | .CMS_SelectOption_label { 30 | @apply block 31 | truncate; 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/components/common/table/Table.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const tableClasses = generateClassNames('Table', [ 4 | 'root', 5 | 'table', 6 | 'header', 7 | 'header-row', 8 | 'header-cell', 9 | 'header-cell-content', 10 | 'body', 11 | 'body-row', 12 | 'body-cell', 13 | 'body-cell-has-link', 14 | 'body-cell-emphasis', 15 | 'body-cell-shrink', 16 | 'body-cell-content', 17 | 'body-cell-link', 18 | ]); 19 | 20 | export default tableClasses; 21 | -------------------------------------------------------------------------------- /packages/core/src/components/common/table/TableHeaderCell.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { isEmpty } from '@staticcms/core/lib/util/string.util'; 4 | import tableClasses from './Table.classes'; 5 | 6 | import type { FC, ReactNode } from 'react'; 7 | 8 | interface TableHeaderCellProps { 9 | children: ReactNode; 10 | } 11 | 12 | const TableHeaderCell: FC = ({ children }) => { 13 | return ( 14 | 15 |
16 | {typeof children === 'string' && isEmpty(children) ? <>  : children} 17 |
18 | 19 | ); 20 | }; 21 | 22 | export default TableHeaderCell; 23 | -------------------------------------------------------------------------------- /packages/core/src/components/common/text-field/TextArea.css: -------------------------------------------------------------------------------- 1 | .CMS_TextArea_root { 2 | @apply flex 3 | w-full; 4 | } 5 | 6 | .CMS_TextArea_input { 7 | color: var(--text-primary); 8 | 9 | &:disabled { 10 | color: var(--text-disabled); 11 | } 12 | 13 | @apply w-full 14 | min-h-[80px] 15 | px-3 16 | bg-transparent 17 | outline-none 18 | text-sm 19 | font-medium; 20 | } 21 | -------------------------------------------------------------------------------- /packages/core/src/components/common/view-style/ViewStyleControl.css: -------------------------------------------------------------------------------- 1 | .CMS_ViewStyleControl_root { 2 | @apply flex 3 | items-center 4 | gap-1.5 5 | lg:mr-1; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/components/common/widget/widgetFor.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetPreview_label { 2 | color: var(--text-secondary); 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/EditorWorkflowToolbarButtons.css: -------------------------------------------------------------------------------- 1 | .CMS_EditorWorkflowToolbarButtons_not-checked { 2 | @apply ms-7; 3 | } 4 | 5 | .CMS_EditorWorkflowToolbarButtons_status-label { 6 | @apply flex 7 | items-center; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/editor-control-pane/EditorControl.css: -------------------------------------------------------------------------------- 1 | .CMS_EditorControl_root { 2 | } 3 | 4 | .CMS_EditorControl_hidden { 5 | @apply hidden; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/editor-control-pane/EditorControlPane.css: -------------------------------------------------------------------------------- 1 | .CMS_EditorControlPane_root { 2 | @apply flex 3 | flex-col 4 | min-h-full 5 | w-full; 6 | 7 | &:not(.CMS_EditorControlPane_no-border) { 8 | border-color: var(--background-main); 9 | 10 | @apply lg:border-r; 11 | } 12 | } 13 | 14 | .CMS_EditorControlPane_locale_dropdown_wrapper { 15 | @apply p-3; 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/editor-control-pane/LocaleDropdown.css: -------------------------------------------------------------------------------- 1 | .CMS_LocaleDropdown_root { 2 | &:not(.CMS_LocaleDropdown_no-edit) { 3 | @apply flex 4 | gap-2 5 | items-center; 6 | } 7 | } 8 | 9 | .CMS_LocaleDropdown_dropdown { 10 | } 11 | 12 | .CMS_LocaleDropdown_errors-icon { 13 | color: var(--error-main); 14 | 15 | @apply w-7 16 | h-7; 17 | } 18 | 19 | .CMS_LocaleDropdown_no-edit { 20 | color: var(--text-secondary); 21 | 22 | @apply border 23 | border-transparent; 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/editor-preview-pane/EditorPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import type { ObjectValue, TemplatePreviewProps } from '@staticcms/core'; 4 | import type { FC } from 'react'; 5 | 6 | const Preview: FC> = ({ collection, fields, widgetFor }) => { 7 | if (!collection || !fields) { 8 | return null; 9 | } 10 | 11 | return ( 12 |
13 | {fields.map(field => ( 14 |
{widgetFor(field.name)}
15 | ))} 16 |
17 | ); 18 | }; 19 | 20 | export default Preview; 21 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/editor-preview-pane/EditorPreviewPane.css: -------------------------------------------------------------------------------- 1 | .CMS_Preview_root { 2 | background: var(--background-dark); 3 | 4 | @apply h-full 5 | md:h-main 6 | top-16 7 | right-0 8 | w-full; 9 | 10 | &.CMS_Preview_show-mobile-preview { 11 | @apply hidden 12 | lg:block; 13 | } 14 | } 15 | 16 | .CMS_Preview_live-preview { 17 | @apply w-full 18 | h-full; 19 | } 20 | 21 | .CMS_Preview_frame { 22 | @apply w-full 23 | h-full; 24 | } 25 | 26 | .CMS_Preview_inline { 27 | @apply overflow-y-auto 28 | h-full 29 | py-2 30 | pe-3; 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/editor-preview-pane/PreviewFrameContent.css: -------------------------------------------------------------------------------- 1 | .CMS_PreviewFrameContent_content { 2 | color: var(--text-primary); 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/widgets/Unknown/UnknownControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WdigetUnknown_root { 2 | color: var(--text-primary); 3 | 4 | @apply px-4 5 | py-2 6 | text-sm; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/components/entry-editor/widgets/index.ts: -------------------------------------------------------------------------------- 1 | import { registerWidget } from '@staticcms/core/lib/registry'; 2 | import UnknownControl from './Unknown/UnknownControl'; 3 | import UnknownPreview from './Unknown/UnknownPreview'; 4 | 5 | registerWidget('unknown', UnknownControl, UnknownPreview); 6 | -------------------------------------------------------------------------------- /packages/core/src/components/images/_index.tsx: -------------------------------------------------------------------------------- 1 | export { default as StaticCmsIcon } from './static-cms-icon.svg'; 2 | export { default as StaticCmsLogo } from './static-cms-logo.svg'; 3 | -------------------------------------------------------------------------------- /packages/core/src/components/login/GoBackButton.tsx: -------------------------------------------------------------------------------- 1 | import { ArrowBack as ArrowBackIcon } from '@styled-icons/material/ArrowBack'; 2 | import React from 'react'; 3 | 4 | import useTranslate from '@staticcms/core/lib/hooks/useTranslate'; 5 | import Button from '../common/button/Button'; 6 | 7 | import type { FC } from 'react'; 8 | 9 | interface GoBackButtonProps { 10 | href: string; 11 | } 12 | 13 | const GoBackButton: FC = ({ href }) => { 14 | const t = useTranslate(); 15 | 16 | return ( 17 | 20 | ); 21 | }; 22 | 23 | export default GoBackButton; 24 | -------------------------------------------------------------------------------- /packages/core/src/components/media-library/MediaLibraryModal.css: -------------------------------------------------------------------------------- 1 | .CMS_MediaLibraryModal_root { 2 | @apply w-media-library-dialog 3 | h-media-library-dialog; 4 | 5 | & .CMS_MediaLibraryModal_close-button { 6 | background: var(--background-main); 7 | border-color: var(--background-light); 8 | 9 | &:hover { 10 | background: var(--background-light); 11 | } 12 | 13 | @apply absolute 14 | -top-3.5 15 | -left-3.5 16 | z-[1]; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/components/media-library/common/EmptyMessage.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import mediaLibraryClasses from './MediaLibrary.classes'; 4 | 5 | import type { FC } from 'react'; 6 | 7 | export interface EmptyMessageProps { 8 | content: string; 9 | } 10 | 11 | const EmptyMessage: FC = ({ content }) => { 12 | return ( 13 |
14 |

{content}

15 |
16 | ); 17 | }; 18 | 19 | export default EmptyMessage; 20 | -------------------------------------------------------------------------------- /packages/core/src/components/navbar/NavLink.css: -------------------------------------------------------------------------------- 1 | .CMS_NavLink_link { 2 | @apply w-full 3 | justify-start; 4 | } 5 | 6 | .CMS_NavLink_external { 7 | @apply flex 8 | justify-between 9 | w-full; 10 | } 11 | 12 | .CMS_NavLink_external-content { 13 | @apply flex 14 | items-center 15 | justify-start; 16 | } 17 | 18 | .CMS_NavLink_external-icon { 19 | color: var(--text-secondary); 20 | 21 | @apply h-5 22 | w-5; 23 | } 24 | 25 | .CMS_NavLink_content { 26 | @apply flex 27 | w-full 28 | gap-3 29 | items-center; 30 | } 31 | 32 | .CMS_NavLink_icon { 33 | @apply w-6 h-6; 34 | } 35 | 36 | .CMS_NavLink_label { 37 | @apply flex-grow; 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/components/navbar/NavigationDrawer.css: -------------------------------------------------------------------------------- 1 | .CMS_NavigationDrawer_root { 2 | @apply w-[80%] 3 | max-w-[320px]; 4 | } 5 | 6 | .CMS_NavigationDrawer_content { 7 | @apply w-full 8 | h-full; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/components/navbar/SettingsDropdown.css: -------------------------------------------------------------------------------- 1 | .CMS_SettingsDropdown_root { 2 | &.CMS_SettingsDropdown_in-editor { 3 | @apply hidden 4 | md:flex; 5 | } 6 | } 7 | 8 | .CMS_SettingsDropdown_avatar-image { 9 | @apply w-9 10 | h-9 11 | rounded-full; 12 | } 13 | 14 | .CMS_SettingsDropdown_avatar-icon { 15 | @apply w-6 16 | h-6; 17 | } 18 | 19 | .CMS_SettingsDropdown_sr-label { 20 | @apply sr-only; 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/components/navbar/Sidebar.classes.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 3 | 4 | const sidebarClasses = generateClassNames('Sidebar', ['root', 'content', 'items', 'icon']); 5 | 6 | export default sidebarClasses; 7 | -------------------------------------------------------------------------------- /packages/core/src/components/navbar/Sidebar.css: -------------------------------------------------------------------------------- 1 | .CMS_Sidebar_root { 2 | @apply w-sidebar-expanded 3 | h-main-mobile 4 | md:h-main 5 | hidden 6 | md:block; 7 | } 8 | 9 | .CMS_Sidebar_content { 10 | background: var(--background-main); 11 | 12 | @apply px-3 13 | py-4 14 | h-full 15 | w-full 16 | overflow-y-auto; 17 | } 18 | 19 | .CMS_Sidebar_items { 20 | @apply space-y-2; 21 | } 22 | 23 | .CMS_Sidebar_icon { 24 | @apply h-6 25 | w-6; 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/components/navbar/Sidebar.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import sidebarClasses from './Sidebar.classes'; 4 | import SidebarContent from './SidebarContent'; 5 | 6 | import type { FC } from 'react'; 7 | 8 | import './Sidebar.css'; 9 | 10 | const Sidebar: FC = () => { 11 | return ( 12 | 15 | ); 16 | }; 17 | 18 | export default Sidebar; 19 | -------------------------------------------------------------------------------- /packages/core/src/components/theme/components/ThemeSelectorDialog.css: -------------------------------------------------------------------------------- 1 | .CMS_ThemeSelectorDialog_root { 2 | @apply pt-3 3 | pb-5 4 | px-4 5 | w-[600px] 6 | max-w-[80%]; 7 | } 8 | 9 | .CMS_ThemeSelectorDialog_header { 10 | @apply flex 11 | justify-between 12 | items-center 13 | mb-2 14 | ps-1; 15 | } 16 | 17 | .CMS_ThemeSelectorDialog_title { 18 | @apply text-lg 19 | font-bold; 20 | } 21 | 22 | .CMS_ThemeSelectorDialog_grid { 23 | @apply grid 24 | grid-cols-1 25 | md:grid-cols-2 26 | gap-3; 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/components/theme/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { selectTheme } from '@staticcms/core/reducers/selectors/globalUI'; 4 | import { useAppSelector } from '@staticcms/core/store/hooks'; 5 | import { DEFAULT_THEMES } from '../defaultThemes'; 6 | import useThemes from './useThemes'; 7 | 8 | import type { Theme } from '@staticcms/core'; 9 | 10 | export default function useTheme(): Theme { 11 | const themes = useThemes(); 12 | 13 | const themeName = useAppSelector(selectTheme); 14 | 15 | return useMemo( 16 | () => themes.find(t => t.name.toLowerCase() === themeName) ?? DEFAULT_THEMES[0], 17 | [themes, themeName], 18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/components/workflow/WorkflowCard.css: -------------------------------------------------------------------------------- 1 | .CMS_WorkflowCard_root { 2 | @apply w-full 3 | min-h-[100px]; 4 | 5 | &:hover { 6 | & .CMS_WorkflowCard_actions { 7 | @apply visible; 8 | } 9 | } 10 | } 11 | 12 | .CMS_WorkflowCard_actions { 13 | background: color-mix(in srgb, var(--background-dark) 95%, transparent); 14 | 15 | @apply py-1 16 | px-2 17 | gap-2 18 | absolute 19 | top-3 20 | right-3 21 | flex 22 | transition-all 23 | rounded-md 24 | invisible; 25 | } 26 | 27 | .CMS_WorkflowCard_action-button { 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/components/workflow/util/workflow.util.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import type { Entry } from '@staticcms/core'; 3 | 4 | export function getEntryId(entry: Entry) { 5 | return `${entry.collection}|${entry.slug}`; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/constants/commitProps.ts: -------------------------------------------------------------------------------- 1 | export const COMMIT_AUTHOR = 'commit_author'; 2 | export const COMMIT_DATE = 'commit_date'; 3 | 4 | export type CollectionProps = typeof COMMIT_AUTHOR | typeof COMMIT_DATE; 5 | -------------------------------------------------------------------------------- /packages/core/src/constants/enums.ts: -------------------------------------------------------------------------------- 1 | export enum PreviewState { 2 | Other = 'other', 3 | Success = 'success', 4 | } 5 | 6 | export enum CollectionType { 7 | FOLDER, 8 | FILES, 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/constants/files.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export const IMAGE_EXTENSION_REGEX = 3 | /(\.apng|\.avif|\.gif|\.jpg|\.jpeg|\.jfif|\.pjpeg|\.pjp|\.png|\.svg|\.webp)$/g; 4 | -------------------------------------------------------------------------------- /packages/core/src/constants/mediaLibrary.ts: -------------------------------------------------------------------------------- 1 | export const MEDIA_CARD_WIDTH = 240; 2 | export const MEDIA_CARD_HEIGHT = 240; 3 | export const MEDIA_CARD_IMAGE_HEIGHT = 196; 4 | export const MEDIA_CARD_MARGIN = 10; 5 | 6 | export const MEDIA_LIBRARY_PADDING = 20; 7 | 8 | export const MAX_LINK_DISPLAY_LENGTH = 28; 9 | 10 | export const DRAFT_MEDIA_FILES = 'DRAFT_MEDIA_FILES'; 11 | -------------------------------------------------------------------------------- /packages/core/src/constants/validationErrorTypes.ts: -------------------------------------------------------------------------------- 1 | const ValidationErrorTypes = { 2 | PRESENCE: 'PRESENCE', 3 | PATTERN: 'PATTERN', 4 | RANGE: 'RANGE', 5 | CUSTOM: 'CUSTOM', 6 | } as const; 7 | 8 | export default ValidationErrorTypes; 9 | -------------------------------------------------------------------------------- /packages/core/src/formats/FileFormatter.ts: -------------------------------------------------------------------------------- 1 | import type { ConfigWithDefaults } from '../interface'; 2 | 3 | export default abstract class FileFormatter { 4 | abstract name: string; 5 | abstract fromFile(content: string, config: ConfigWithDefaults): object; 6 | abstract toFile( 7 | data: object, 8 | config: ConfigWithDefaults, 9 | sortedKeys?: string[], 10 | comments?: Record, 11 | ): string; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/formats/JsonFormatter.ts: -------------------------------------------------------------------------------- 1 | import { isEmpty } from '../lib/util/string.util'; 2 | import FileFormatter from './FileFormatter'; 3 | 4 | class JsonFormatter extends FileFormatter { 5 | name = 'json'; 6 | 7 | fromFile(content: string) { 8 | if (isEmpty(content)) { 9 | return {}; 10 | } 11 | 12 | return JSON.parse(content); 13 | } 14 | 15 | toFile(data: object) { 16 | return JSON.stringify(data, null, 2); 17 | } 18 | } 19 | 20 | export default new JsonFormatter(); 21 | -------------------------------------------------------------------------------- /packages/core/src/formats/TomlFormatter.ts: -------------------------------------------------------------------------------- 1 | import toml from '@iarna/toml'; 2 | 3 | import FileFormatter from './FileFormatter'; 4 | 5 | class TomlFormatter extends FileFormatter { 6 | name = 'toml'; 7 | 8 | fromFile(content: string) { 9 | return toml.parse(content) as object; 10 | } 11 | 12 | toFile(data: object): string { 13 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 14 | return toml.stringify(data as any); 15 | } 16 | } 17 | 18 | export default new TomlFormatter(); 19 | -------------------------------------------------------------------------------- /packages/core/src/formats/helpers.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export function sortKeys( 3 | sortedKeys: string[], 4 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 5 | selector: (a: Item) => string = (a: any) => a, 6 | ) { 7 | return (a: Item, b: Item) => { 8 | const idxA = sortedKeys.indexOf(selector(a)); 9 | const idxB = sortedKeys.indexOf(selector(b)); 10 | if (idxA === -1 || idxB === -1) return 0; 11 | if (idxA > idxB) return 1; 12 | if (idxA < idxB) return -1; 13 | return 0; 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/lib/auth/index.ts: -------------------------------------------------------------------------------- 1 | export { default as NetlifyAuthenticator } from './netlify-auth'; 2 | export { default as ImplicitAuthenticator } from './implicit-oauth'; 3 | export { default as PkceAuthenticator } from './pkce-oauth'; 4 | -------------------------------------------------------------------------------- /packages/core/src/lib/consoleError.ts: -------------------------------------------------------------------------------- 1 | export default function consoleError(title: string, description: string) { 2 | console.error( 3 | `%c ⛔ ${title}\n` + `%c${description}\n\n`, 4 | 'color: black; font-weight: bold; font-size: 16px; line-height: 50px;', 5 | 'color: black;', 6 | ); 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useCurrentBackend.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { currentBackend } from '@staticcms/core/backend'; 4 | import { selectConfig } from '@staticcms/core/reducers/selectors/config'; 5 | import { useAppSelector } from '@staticcms/core/store/hooks'; 6 | 7 | import type { Backend } from '@staticcms/core/backend'; 8 | 9 | export default function useCurrentBackend(): Backend | undefined { 10 | const config = useAppSelector(selectConfig); 11 | 12 | return useMemo(() => { 13 | if (!config) { 14 | return undefined; 15 | } 16 | 17 | return currentBackend(config); 18 | }, [config]); 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useCursor.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | export default function useCursor(cursor: 'default' | 'pointer' | 'text', disabled: boolean) { 4 | return useMemo(() => { 5 | if (disabled) { 6 | return 'default'; 7 | } 8 | 9 | return cursor; 10 | }, [cursor, disabled]); 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | 3 | export default function useDebounce(value: T, delay: number): T { 4 | const [debouncedValue, setDebouncedValue] = useState(value); 5 | 6 | useEffect(() => { 7 | if (delay === 0) { 8 | setDebouncedValue(value); 9 | return; 10 | } 11 | 12 | const handler = setTimeout(() => { 13 | setDebouncedValue(value); 14 | }, delay); 15 | 16 | return () => { 17 | clearTimeout(handler); 18 | }; 19 | }, [value, delay]); 20 | 21 | return delay === 0 ? value : debouncedValue; 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useDefaultPath.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { selectUseWorkflow } from '@staticcms/core/reducers/selectors/config'; 4 | import { useAppSelector } from '@staticcms/core/store/hooks'; 5 | import { getDefaultPath } from '../util/collection.util'; 6 | 7 | import type { CollectionsWithDefaults } from '@staticcms/core'; 8 | 9 | export default function useDefaultPath(collections: CollectionsWithDefaults) { 10 | const useWorkflow = useAppSelector(selectUseWorkflow); 11 | 12 | return useMemo(() => getDefaultPath(collections, useWorkflow), [collections, useWorkflow]); 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useFilters.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { selectEntriesFilter } from '@staticcms/core/reducers/selectors/entries'; 4 | import { useAppSelector } from '@staticcms/core/store/hooks'; 5 | 6 | export default function useFilters(collectionName: string) { 7 | const filters = useAppSelector(state => selectEntriesFilter(state, collectionName)); 8 | 9 | return useMemo(() => { 10 | return Object.values(filters ?? {}).filter(v => v?.active === true) || []; 11 | }, [filters]); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useHasChildErrors.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { getEntryDataPath } from '@staticcms/core/reducers/selectors/entryDraft'; 4 | 5 | import type { FieldsErrors, I18nSettings } from '@staticcms/core'; 6 | 7 | export default function useHasChildErrors( 8 | path: string, 9 | fieldsErrors: FieldsErrors, 10 | i18n: I18nSettings | undefined, 11 | isMeta: boolean | undefined, 12 | ) { 13 | return useMemo(() => { 14 | const dataPath = getEntryDataPath(i18n, isMeta); 15 | const fullPath = `${dataPath}.${path}`; 16 | 17 | return Boolean(Object.keys(fieldsErrors).find(key => key.startsWith(fullPath))); 18 | }, [fieldsErrors, i18n, isMeta, path]); 19 | } 20 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useIcon.css: -------------------------------------------------------------------------------- 1 | .CMS_Icon_root { 2 | @apply h-6 3 | w-6 4 | flex 5 | items-center 6 | justify-center; 7 | } 8 | 9 | .CMS_Icon_icon { 10 | @apply h-6 11 | w-6; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useMediaQuery.ts: -------------------------------------------------------------------------------- 1 | import { useMediaQuery } from '@mui/material'; 2 | 3 | export function useIsMobile() { 4 | return useMediaQuery('(max-width: 768px)'); 5 | } 6 | 7 | export function useIsSmallScreen() { 8 | return useMediaQuery('(max-width: 1024px)'); 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useMeta.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | 3 | export interface MetaProps { 4 | name: string; 5 | content: string; 6 | } 7 | 8 | export default function useMeta({ name, content }: MetaProps) { 9 | useEffect(() => { 10 | const head = document.querySelector('head'); 11 | if (!head) { 12 | return; 13 | } 14 | 15 | const meta = document.createElement('meta'); 16 | 17 | meta.setAttribute('name', name); 18 | meta.setAttribute('content', content); 19 | head.appendChild(meta); 20 | 21 | return () => { 22 | head.removeChild(meta); 23 | }; 24 | }, [content, name]); 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useNewEntryUrl.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { getNewEntryUrl } from '../urlHelper'; 4 | import { isNotEmpty } from '../util/string.util'; 5 | 6 | import type { CollectionWithDefaults } from '@staticcms/core'; 7 | 8 | export default function useNewEntryUrl( 9 | collection: CollectionWithDefaults | undefined, 10 | filterTerm: string | undefined, 11 | ) { 12 | return useMemo(() => { 13 | if (!collection) { 14 | return undefined; 15 | } 16 | 17 | return 'fields' in collection && collection.create 18 | ? `${getNewEntryUrl(collection.name)}${isNotEmpty(filterTerm) ? `/${filterTerm}` : ''}` 19 | : ''; 20 | }, [collection, filterTerm]); 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/usePublishedEntries.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { 4 | selectEntriesBySlugs, 5 | selectPublishedSlugs, 6 | } from '@staticcms/core/reducers/selectors/entries'; 7 | import { useAppSelector } from '@staticcms/core/store/hooks'; 8 | 9 | export default function usePublishedEntries(collectionName: string) { 10 | const slugs = useAppSelector(state => selectPublishedSlugs(state, collectionName)); 11 | const entries = useAppSelector(selectEntriesBySlugs); 12 | 13 | return useMemo( 14 | () => slugs && slugs.map(slug => entries[`${collectionName}.${slug}`]), 15 | [collectionName, entries, slugs], 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useRefWithCallback.ts: -------------------------------------------------------------------------------- 1 | import { useCallback, useRef } from 'react'; 2 | 3 | export default function useRefWithCallback(cb: (node: T) => void) { 4 | const ref = useRef(null); 5 | const setRef = useCallback( 6 | (node: T | null) => { 7 | if (node) { 8 | cb(node); 9 | } 10 | 11 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 12 | (ref as any).current = node; 13 | }, 14 | [cb], 15 | ); 16 | 17 | return setRef; 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useTranslate.ts: -------------------------------------------------------------------------------- 1 | import { useTranslate as useReactPolyglotTranslate } from 'react-polyglot'; 2 | 3 | import type { t } from 'react-polyglot'; 4 | 5 | export default function useTranslate(): t { 6 | return useReactPolyglotTranslate() as t; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useUUID.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | import { v4 as uuid } from 'uuid'; 3 | 4 | export default function useUUID() { 5 | return useMemo(() => uuid(), []); 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/lib/hooks/useUnpublishedEntries.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { selectUnpublishedEntries } from '@staticcms/core/reducers/selectors/editorialWorkflow'; 4 | import { useAppSelector } from '@staticcms/core/store/hooks'; 5 | 6 | export default function useUnpublishedEntries(collectionName: string, onlyNew = false) { 7 | const allEntries = useAppSelector(selectUnpublishedEntries); 8 | 9 | return useMemo( 10 | () => 11 | Object.values(allEntries).filter( 12 | e => e.collection === collectionName && (!onlyNew || !e.isModification), 13 | ), 14 | [allEntries, collectionName, onlyNew], 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/lib/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './auth'; 2 | export * from './util'; 3 | export * from './widgets'; 4 | export * from './hooks'; 5 | -------------------------------------------------------------------------------- /packages/core/src/lib/phrases.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import merge from 'lodash/merge'; 3 | 4 | import { getLocale } from './registry'; 5 | 6 | export function getPhrases(locale: string) { 7 | const phrases = merge({}, getLocale('en'), getLocale(locale)); 8 | return phrases; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/lib/textHelper.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export function stringToRGB(str: string) { 3 | if (!str) return '000000'; 4 | let hash = 0; 5 | for (let i = 0; i < str.length; i++) { 6 | hash = str.charCodeAt(i) + ((hash << 5) - hash); 7 | } 8 | 9 | const c = (hash & 0x00ffffff).toString(16).toUpperCase(); 10 | 11 | return `00000${c}`.slice(-6); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/APIError.ts: -------------------------------------------------------------------------------- 1 | export const API_ERROR = 'API_ERROR'; 2 | 3 | export default class APIError extends Error { 4 | message: string; 5 | status: null | number; 6 | api: string; 7 | meta: {}; 8 | 9 | constructor(message: string, status: null | number, api: string, meta = {}) { 10 | super(message); 11 | this.message = message; 12 | this.status = status; 13 | this.api = api; 14 | this.name = API_ERROR; 15 | this.meta = meta; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/AccessTokenError.ts: -------------------------------------------------------------------------------- 1 | export const ACCESS_TOKEN_ERROR = 'ACCESS_TOKEN_ERROR'; 2 | 3 | export default class AccessTokenError extends Error { 4 | message: string; 5 | 6 | constructor(message: string) { 7 | super(message); 8 | this.message = message; 9 | this.name = ACCESS_TOKEN_ERROR; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/EditorialWorkflowError.ts: -------------------------------------------------------------------------------- 1 | export const EDITORIAL_WORKFLOW_ERROR = 'EDITORIAL_WORKFLOW_ERROR'; 2 | 3 | export default class EditorialWorkflowError extends Error { 4 | message: string; 5 | notUnderEditorialWorkflow: boolean; 6 | constructor(message: string, notUnderEditorialWorkflow: boolean) { 7 | super(message); 8 | this.message = message; 9 | this.notUnderEditorialWorkflow = notUnderEditorialWorkflow; 10 | this.name = EDITORIAL_WORKFLOW_ERROR; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/backup.util.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export function getEntryBackupKey(collectionName?: string, slug?: string) { 3 | const baseKey = 'backup'; 4 | if (!collectionName) { 5 | return baseKey; 6 | } 7 | const suffix = slug ? `.${slug}` : ''; 8 | return `${baseKey}.${collectionName}${suffix}`; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/classNames.util.ts: -------------------------------------------------------------------------------- 1 | export default function classNames(...classes: (string | undefined | null | false)[]) { 2 | const filteredClasses = classes.filter(Boolean) as string[]; 3 | 4 | return filteredClasses 5 | .map(value => value.replace(/\n/g, ' ').replace(/[ ]+/g, ' ').trim()) 6 | .join(' '); 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/events/AlertEvent.ts: -------------------------------------------------------------------------------- 1 | import type { AlertDialogProps } from '@staticcms/core/components/common/alert/Alert'; 2 | 3 | export default class AlertEvent extends CustomEvent { 4 | constructor(detail: AlertDialogProps) { 5 | super('alert', { detail }); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/events/ConfirmEvent.ts: -------------------------------------------------------------------------------- 1 | import type { ConfirmDialogProps } from '@staticcms/core/components/common/confirm/Confirm'; 2 | 3 | export default class ConfirmEvent extends CustomEvent { 4 | constructor(detail: ConfirmDialogProps) { 5 | super('confirm', { detail }); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/events/DataEvent.ts: -------------------------------------------------------------------------------- 1 | import type { ValueOrNestedValue } from '@staticcms/core'; 2 | 3 | export interface DataUpdateEventProps { 4 | field: string; 5 | fieldPath: string; 6 | value: ValueOrNestedValue; 7 | } 8 | 9 | export default class DataUpdateEvent extends CustomEvent { 10 | constructor(detail: DataUpdateEventProps) { 11 | super('data:update', { detail }); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/events/LivePreviewLoadedEvent.ts: -------------------------------------------------------------------------------- 1 | export default class LivePreviewLoadedEvent extends CustomEvent<{}> { 2 | constructor() { 3 | super('livePreviewLoaded', {}); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/events/MediaLibraryCloseEvent.ts: -------------------------------------------------------------------------------- 1 | export default class MediaLibraryCloseEvent extends CustomEvent<{}> { 2 | constructor() { 3 | super('mediaLibraryClose', {}); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/fetch.util.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export async function doesUrlFileExist(url: string): Promise<{ type: string; exists: boolean }> { 3 | const cleanUrl = url.replace(/^blob:/g, ''); 4 | 5 | const baseUrl = `${window.location.protocol}//${window.location.host}/`; 6 | 7 | if (!cleanUrl.startsWith('/') && !cleanUrl.startsWith(baseUrl)) { 8 | return { type: 'Unknown', exists: true }; 9 | } 10 | 11 | const response = await fetch(cleanUrl, { method: 'HEAD' }); 12 | return { type: response.headers.get('Content-Type') ?? 'text', exists: response.ok }; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/getBlobSHA.ts: -------------------------------------------------------------------------------- 1 | import { sha256 } from 'js-sha256'; 2 | 3 | export default (blob: Blob): Promise => 4 | new Promise(resolve => { 5 | const fr = new FileReader(); 6 | fr.onload = ({ target }) => resolve(sha256(target?.result || '')); 7 | fr.onerror = () => { 8 | fr.abort(); 9 | resolve(''); 10 | }; 11 | fr.readAsArrayBuffer(blob); 12 | }); 13 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/loadScript.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Simple script loader that returns a promise. 3 | */ 4 | export default function loadScript(url: string) { 5 | return new Promise((resolve, reject) => { 6 | const head = document.getElementsByTagName('head')[0]; 7 | const script: HTMLScriptElement = document.createElement('script'); 8 | script.src = url; 9 | script.onload = () => { 10 | resolve(); 11 | }; 12 | script.onerror = error => { 13 | reject(error); 14 | }; 15 | head.appendChild(script); 16 | }); 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/localForage.ts: -------------------------------------------------------------------------------- 1 | import localForage from 'localforage'; 2 | 3 | function localForageTest() { 4 | const testKey = 'localForageTest'; 5 | localForage 6 | .setItem(testKey, { expires: Date.now() + 300000 }) 7 | .then(() => { 8 | localForage.removeItem(testKey); 9 | }) 10 | .catch(err => { 11 | if (err.code === 22) { 12 | const message = 'Unable to set localStorage key. Quota exceeded! Full disk?'; 13 | console.warn('[StaticCMS]', message); 14 | } 15 | console.info(err); 16 | }); 17 | } 18 | 19 | localForageTest(); 20 | 21 | export default localForage; 22 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/null.util.ts: -------------------------------------------------------------------------------- 1 | export function isNotNullish(value: T | null | undefined): value is T { 2 | return value !== undefined && value !== null; 3 | } 4 | 5 | export function isNullish(value: T | null | undefined): value is null | undefined { 6 | return value === undefined || value === null; 7 | } 8 | 9 | export function filterNullish(value: (T | null | undefined)[] | null | undefined): T[] { 10 | return value?.filter(isNotNullish) ?? []; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/promise.ts: -------------------------------------------------------------------------------- 1 | import flow from 'lodash/flow'; 2 | 3 | export function then(fn: (r: T) => V) { 4 | return (p: Promise) => Promise.resolve(p).then(fn); 5 | } 6 | 7 | const filterPromiseSymbol = Symbol('filterPromiseSymbol'); 8 | 9 | export function onlySuccessfulPromises(promises: Promise[]) { 10 | return Promise.all(promises.map(p => p.catch(() => filterPromiseSymbol))).then(results => 11 | results.filter(result => result !== filterPromiseSymbol), 12 | ); 13 | } 14 | 15 | function wrapFlowAsync(fn: Function) { 16 | return async (arg: unknown) => fn(await arg); 17 | } 18 | 19 | export function flowAsync(fns: Function[]) { 20 | return flow(fns.map(fn => wrapFlowAsync(fn))); 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/set.util.ts: -------------------------------------------------------------------------------- 1 | import { setIn } from 'immutable'; 2 | 3 | export default function set(object: T, path: string, value: unknown): T { 4 | return setIn(object, path.split('.'), value); 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/sort.util.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { COMMIT_AUTHOR, COMMIT_DATE } from '@staticcms/core/constants/commitProps'; 3 | import { selectField } from './field.util'; 4 | 5 | import type { CollectionWithDefaults } from '@staticcms/core'; 6 | 7 | export function selectSortDataPath(collection: CollectionWithDefaults, key: string) { 8 | if (key === COMMIT_DATE) { 9 | return 'updatedOn'; 10 | } else if (key === COMMIT_AUTHOR && !selectField(collection, key)) { 11 | return 'author'; 12 | } else { 13 | return `data.${key}`; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/lib/util/theming.util.ts: -------------------------------------------------------------------------------- 1 | export const GLOBAL_CLASS_NAME_PREFIX = 'CMS'; 2 | 3 | export function generateClassNames( 4 | base: string, 5 | classNames: T[], 6 | ): Record { 7 | return classNames.reduce( 8 | (acc, className) => { 9 | acc[className] = `${GLOBAL_CLASS_NAME_PREFIX}_${base}_${className}`; 10 | return acc; 11 | }, 12 | {} as Record, 13 | ); 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/lib/widgets/index.ts: -------------------------------------------------------------------------------- 1 | export * as stringTemplate from './stringTemplate'; 2 | export * as validations from './validations'; 3 | -------------------------------------------------------------------------------- /packages/core/src/live/Data.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import useData from './useData'; 4 | 5 | import type { ValueOrNestedValue } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | export interface DataProps { 9 | path: string; 10 | value: ValueOrNestedValue; 11 | } 12 | 13 | const Data: FC = ({ path, value }) => { 14 | const data = useData(value, path); 15 | return <>{data}; 16 | }; 17 | 18 | export default Data; 19 | -------------------------------------------------------------------------------- /packages/core/src/live/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Data, type DataProps } from './Data'; 2 | export { default as useData } from './useData'; 3 | -------------------------------------------------------------------------------- /packages/core/src/locales/types.ts: -------------------------------------------------------------------------------- 1 | export interface LocalePhrasesRoot { 2 | [property: string]: LocalePhrases; 3 | } 4 | export type LocalePharsesSection = { [property: string]: LocalePhrases }; 5 | export type LocalePhrases = string | undefined | LocalePharsesSection; 6 | 7 | export interface BaseLocalePhrasesRoot { 8 | [property: string]: BaseLocalePhrases; 9 | } 10 | export type BaseLocalePharsesSection = { [property: string]: BaseLocalePhrases }; 11 | export type BaseLocalePhrases = string | BaseLocalePharsesSection; 12 | -------------------------------------------------------------------------------- /packages/core/src/reducers/combinedReducer.ts: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | 3 | import snackbarReducer from '../store/slices/snackbars'; 4 | import reducers from './index'; 5 | 6 | function createRootReducer() { 7 | return combineReducers({ 8 | ...reducers, 9 | snackbar: snackbarReducer, 10 | }); 11 | } 12 | 13 | export default createRootReducer; 14 | -------------------------------------------------------------------------------- /packages/core/src/reducers/selectors/auth.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import type { RootState } from '@staticcms/core/store'; 3 | 4 | export const selectUser = (state: RootState) => { 5 | return state.auth.user; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/core/src/reducers/selectors/cursors.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | 3 | import Cursor from '@staticcms/core/lib/util/Cursor'; 4 | 5 | import type { RootState } from '@staticcms/core/store'; 6 | 7 | // Since pagination can be used for a variety of views (collections 8 | // and searches are the most common examples), we namespace cursors by 9 | // their type before storing them in the state. 10 | export function selectCollectionEntriesCursor(state: RootState, collectionName: string) { 11 | return new Cursor(state.cursors.cursorsByType.collectionEntries[collectionName]); 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/reducers/selectors/globalUI.ts: -------------------------------------------------------------------------------- 1 | import type { RootState } from '@staticcms/core/store'; 2 | 3 | export function selectIsFetching(state: RootState) { 4 | return state.globalUI.isFetching; 5 | } 6 | 7 | export function selectTheme(state: RootState) { 8 | return state.globalUI.theme; 9 | } 10 | 11 | export function selectUseOpenAuthoring(state: RootState) { 12 | return state.globalUI.useOpenAuthoring; 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/reducers/selectors/medias.ts: -------------------------------------------------------------------------------- 1 | import { createSelector } from '@reduxjs/toolkit'; 2 | 3 | import type { RootState } from '@staticcms/core/store'; 4 | 5 | export function selectMedias(state: RootState) { 6 | return state.medias; 7 | } 8 | 9 | export const selectIsLoadingAsset = createSelector([selectMedias], medias => { 10 | return Object.values(medias).some(state => state.isLoading); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/core/src/reducers/selectors/scroll.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import type { RootState } from '@staticcms/core/store'; 3 | 4 | export const selectIsScrolling = (state: RootState) => { 5 | return state.scroll.isScrolling; 6 | }; 7 | -------------------------------------------------------------------------------- /packages/core/src/store/hooks.ts: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux'; 2 | 3 | import type { RootState } from './index'; 4 | import type { TypedUseSelectorHook } from 'react-redux'; 5 | import type { AnyAction, ThunkDispatch } from '@reduxjs/toolkit'; 6 | 7 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 8 | type AppDispatch = ThunkDispatch; 9 | 10 | export const useAppDispatch = () => useDispatch(); 11 | export const useAppSelector: TypedUseSelectorHook = useSelector; 12 | -------------------------------------------------------------------------------- /packages/core/src/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.dev.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/src/types/constants.d.ts: -------------------------------------------------------------------------------- 1 | declare const STATIC_CMS_CORE_VERSION: string; 2 | declare const STATIC_CMS_APP_VERSION: string; 3 | -------------------------------------------------------------------------------- /packages/core/src/types/css.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.css' { 2 | const content: string; 3 | export default content; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/types/diacritics.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'diacritics'; 2 | -------------------------------------------------------------------------------- /packages/core/src/types/ini.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ini' { 2 | const ini: { decode: (ini: string) => T }; 3 | export default ini; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/types/semaphore.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'semaphore' { 2 | export type Semaphore = { take: (f: Function) => void; leave: () => void }; 3 | const semaphore: (count: number) => Semaphore; 4 | export default semaphore; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/types/svg.d.ts: -------------------------------------------------------------------------------- 1 | declare module '*.svg' { 2 | import type { FC } from 'react'; 3 | import type { SvgProps } from '@staticcms/core'; 4 | 5 | const content: FC; 6 | export default content; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/types/what-the-diff.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'what-the-diff' { 2 | // eslint-disable-next-line import/prefer-default-export 3 | export const parse: ( 4 | rawDiff: string, 5 | ) => { oldPath?: string; newPath?: string; binary: boolean; status: string }[]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/valueObjects/index.ts: -------------------------------------------------------------------------------- 1 | export * from './AssetProxy'; 2 | export { default as AssetProxy } from './AssetProxy'; 3 | export { default as createEntry } from './createEntry'; 4 | -------------------------------------------------------------------------------- /packages/core/src/widgets/boolean/BooleanControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetBoolean_content { 2 | @apply flex 3 | gap-2 4 | items-center; 5 | } 6 | 7 | .CMS_WidgetBoolean_prefix { 8 | color: var(--text-secondary); 9 | 10 | @apply text-sm 11 | whitespace-nowrap; 12 | 13 | line-height: 100%; 14 | } 15 | 16 | .CMS_WidgetBoolean_suffix { 17 | color: var(--text-secondary); 18 | 19 | @apply text-sm 20 | whitespace-nowrap; 21 | 22 | line-height: 100%; 23 | } 24 | -------------------------------------------------------------------------------- /packages/core/src/widgets/boolean/index.ts: -------------------------------------------------------------------------------- 1 | import controlComponent from './BooleanControl'; 2 | import schema from './schema'; 3 | 4 | import type { BooleanField, WidgetParam } from '@staticcms/core'; 5 | 6 | const BooleanWidget = (): WidgetParam => { 7 | return { 8 | name: 'boolean', 9 | controlComponent, 10 | options: { 11 | schema, 12 | getDefaultValue: (defaultValue: boolean | undefined | null) => { 13 | return typeof defaultValue === 'boolean' ? defaultValue : false; 14 | }, 15 | }, 16 | }; 17 | }; 18 | 19 | export { controlComponent as BooleanControl, schema as BooleanSchema }; 20 | 21 | export default BooleanWidget; 22 | -------------------------------------------------------------------------------- /packages/core/src/widgets/boolean/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'boolean' }, 4 | prefix: { type: 'string' }, 5 | suffix: { type: 'string' }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/widgets/code/SettingsButton.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetCode_SettingsButton_root { 2 | } 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/code/SettingsPane.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetCodeSettings_root { 2 | background: var(--background-light); 3 | 4 | @apply absolute 5 | top-[41px] 6 | bottom-0 7 | right-0 8 | w-40 9 | flex 10 | flex-col 11 | gap-2 12 | z-10 13 | shadow-sm; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/code/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { 4 | oneOf: [{ type: 'string' }, { type: 'object' }], 5 | }, 6 | default_language: { type: 'string' }, 7 | allow_language_selection: { type: 'boolean' }, 8 | keys: { 9 | type: 'object', 10 | properties: { code: { type: 'string' }, lang: { type: 'string' } }, 11 | }, 12 | output_code_only: { type: 'boolean' }, 13 | code_mirror_config: { type: 'object' }, 14 | }, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/core/src/widgets/colorstring/ColorPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { ColorField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetColorPreview', ['root']); 9 | 10 | const ColorPreview: FC> = ({ value }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default ColorPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/colorstring/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'string' }, 4 | allow_input: { type: 'boolean' }, 5 | enable_alpha: { type: 'boolean' }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/widgets/datetime/DateTimePreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { DateTimeField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetDateTimePreview', ['root']); 9 | 10 | const DatePreview: FC> = ({ value }) => { 11 | return
{value ? value.toString() : null}
; 12 | }; 13 | 14 | export default DatePreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/datetime/__tests__/utc.util.spec.ts: -------------------------------------------------------------------------------- 1 | import { localToUTC, utcToLocal } from '../utc.util'; 2 | 3 | describe('utc util', () => { 4 | it('converts local (Hawaii time) to UTC', () => { 5 | expect(localToUTC(new Date(2023, 1, 12, 10, 5, 35)).toString()).toEqual( 6 | new Date(2023, 1, 12, 20, 5, 35).toString(), 7 | ); 8 | }); 9 | 10 | it('converts UTC to local (Hawaii time)', () => { 11 | expect(utcToLocal(new Date(2023, 1, 12, 15, 5, 35)).toString()).toEqual( 12 | new Date(2023, 1, 12, 5, 5, 35).toString(), 13 | ); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/core/src/widgets/datetime/components/NowButton.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetDateTime_NowButton_root { 2 | @apply flex 3 | items-center; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/datetime/constants.ts: -------------------------------------------------------------------------------- 1 | export const DEFAULT_DATE_FORMAT = 'yyyy-MM-dd'; 2 | export const DEFAULT_TIME_FORMAT = 'HH:mm:ss'; 3 | export const DEFAULT_DATETIME_FORMAT = "yyyy-MM-dd'T'HH:mm:ss"; 4 | export const DEFAULT_TIMEZONE_FORMAT = 'XXX'; 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/datetime/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'string' }, 4 | format: { type: 'string' }, 5 | date_format: { oneOf: [{ type: 'string' }, { type: 'boolean' }] }, 6 | time_format: { oneOf: [{ type: 'string' }, { type: 'boolean' }] }, 7 | picker_utc: { type: 'boolean' }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/core/src/widgets/datetime/utc.util.ts: -------------------------------------------------------------------------------- 1 | import { addMinutes } from 'date-fns/addMinutes'; 2 | 3 | export function localToUTC(dateTime: Date): Date { 4 | return addMinutes(dateTime.getTime(), getTimezoneOffset(dateTime)); 5 | } 6 | 7 | export function utcToLocal(dateTime: Date): Date { 8 | return addMinutes(dateTime.getTime(), getTimezoneOffset(dateTime) * -1); 9 | } 10 | 11 | export function getTimezoneOffset(dateTime: Date): number { 12 | return dateTime.getTimezoneOffset(); 13 | } 14 | -------------------------------------------------------------------------------- /packages/core/src/widgets/file/FileImageControl.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const widgetFileImageClasses = generateClassNames('WidgetFileImage', [ 4 | 'root', 5 | 'error', 6 | 'required', 7 | 'disabled', 8 | 'for-single-list', 9 | 'drag-over-active', 10 | 'for-image', 11 | 'multiple', 12 | 'wrapper', 13 | 'drop-area', 14 | 'for-image', 15 | 'image-grid', 16 | 'empty-content', 17 | 'content', 18 | 'actions', 19 | ]); 20 | 21 | export default widgetFileImageClasses; 22 | -------------------------------------------------------------------------------- /packages/core/src/widgets/image/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | allow_multiple: { type: 'boolean' }, 4 | default: { 5 | oneOf: [ 6 | { type: 'string' }, 7 | { 8 | type: 'array', 9 | items: { 10 | type: 'string', 11 | }, 12 | }, 13 | ], 14 | }, 15 | media_folder: { type: 'string' }, 16 | public_folder: { type: 'string' }, 17 | choose_url: { type: 'boolean' }, 18 | multiple: { type: 'boolean' }, 19 | media_library: { 20 | type: 'object', 21 | properties: { 22 | max_file_size: { type: 'number' }, 23 | folder_support: { type: 'boolean' }, 24 | }, 25 | }, 26 | }, 27 | }; 28 | -------------------------------------------------------------------------------- /packages/core/src/widgets/keyvalue/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'object' }, 4 | label_singular: { type: 'string' }, 5 | key_label: { type: 'string' }, 6 | value_label: { type: 'string' }, 7 | max: { type: 'number' }, 8 | min: { type: 'number' }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/core/src/widgets/keyvalue/types.ts: -------------------------------------------------------------------------------- 1 | export interface Pair { 2 | key: string; 3 | value: string; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/keyvalue/util.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import type { Pair } from './types'; 3 | 4 | export const createEmptyPair: () => Pair = () => ({ key: '', value: '' }); 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/list/ListControl.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const widgetListClasses = generateClassNames('WidgetList', [ 4 | 'root', 5 | 'disabled', 6 | 'error', 7 | 'required', 8 | 'for-single-list', 9 | 'open', 10 | 'summary', 11 | 'field-wrapper', 12 | 'field', 13 | 'expand-button', 14 | 'expand-button-icon', 15 | 'content', 16 | 'error-message', 17 | 'delimited', 18 | 'delimited-input', 19 | 'fields', 20 | 'actions', 21 | 'add-types-button', 22 | 'add-button', 23 | 'sortable-item', 24 | 'multi-field-item', 25 | ]); 26 | 27 | export default widgetListClasses; 28 | -------------------------------------------------------------------------------- /packages/core/src/widgets/list/index.ts: -------------------------------------------------------------------------------- 1 | import controlComponent from './ListControl'; 2 | import previewComponent from './ListPreview'; 3 | import schema from './schema'; 4 | 5 | import type { ListField, ValueOrNestedValue, WidgetParam } from '@staticcms/core'; 6 | 7 | const ListWidget = (): WidgetParam => { 8 | return { 9 | name: 'list', 10 | controlComponent, 11 | previewComponent, 12 | options: { 13 | schema, 14 | }, 15 | }; 16 | }; 17 | 18 | export { default as ListItem } from './components/ListItem'; 19 | export * from './typedListHelpers'; 20 | export { controlComponent as ListControl, previewComponent as ListPreview, schema as ListSchema }; 21 | 22 | export default ListWidget; 23 | -------------------------------------------------------------------------------- /packages/core/src/widgets/map/MapControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMap_root { 2 | } 3 | 4 | .CMS_WidgetMap_map { 5 | @apply relative 6 | w-full 7 | mt-2; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/widgets/map/MapPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { MapField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetMapPreview', ['root']); 9 | 10 | const MapPreview: FC> = ({ value }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default MapPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/map/index.ts: -------------------------------------------------------------------------------- 1 | import previewComponent from './MapPreview'; 2 | import schema from './schema'; 3 | import withMapControl from './withMapControl'; 4 | 5 | import type { MapField, WidgetParam } from '@staticcms/core'; 6 | 7 | const controlComponent = withMapControl(); 8 | 9 | const MapWidget = (): WidgetParam => { 10 | return { 11 | name: 'map', 12 | controlComponent, 13 | previewComponent, 14 | options: { 15 | schema, 16 | }, 17 | }; 18 | }; 19 | 20 | export { previewComponent as MapPreview, schema as MapSchema, withMapControl }; 21 | 22 | export default MapWidget; 23 | -------------------------------------------------------------------------------- /packages/core/src/widgets/map/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | decimals: { type: 'integer' }, 4 | type: { type: 'string', enum: ['Point', 'LineString', 'Polygon'] }, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/MarkdownControl.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const widgetMarkdownClasses = generateClassNames('WidgetMarkdown', [ 4 | 'root', 5 | 'error', 6 | 'required', 7 | 'disabled', 8 | 'for-single-list', 9 | 'raw-editor', 10 | 'rich-editor', 11 | 'plate-editor-wrapper', 12 | 'plate-editor', 13 | 'controls', 14 | ]); 15 | 16 | export default widgetMarkdownClasses; 17 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/MarkdownControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_root { 2 | } 3 | 4 | .CMS_WidgetMarkdown_raw-editor { 5 | @apply mt-2; 6 | } 7 | 8 | .CMS_WidgetMarkdown_rich-editor { 9 | @apply relative 10 | px-3 11 | py-5 12 | pb-0; 13 | } 14 | 15 | .CMS_WidgetMarkdown_plate-editor-wrapper { 16 | @apply w-full; 17 | } 18 | 19 | .CMS_WidgetMarkdown_plate-editor { 20 | @apply !outline-none; 21 | } 22 | 23 | .CMS_WidgetMarkdown_controls { 24 | @apply px-3 25 | mt-2 26 | flex 27 | gap-2; 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/mdx/index.ts: -------------------------------------------------------------------------------- 1 | export * from './withShortcodeMdxComponent'; 2 | export { default as withShortcodeElement } from './withShortcodeMdxComponent'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/balloon-toolbar/BalloonToolbar.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_BalloonToolbar_root { 2 | @apply fixed; 3 | } 4 | 5 | .CMS_WidgetMarkdown_BalloonToolbar_popper { 6 | background: var(--background-main); 7 | 8 | @apply absolute 9 | max-h-60 10 | overflow-hidden 11 | rounded-md 12 | p-1 13 | text-base 14 | shadow-md 15 | focus:outline-none 16 | sm:text-sm 17 | z-[100]; 18 | } 19 | 20 | .CMS_WidgetMarkdown_BalloonToolbar_content { 21 | @apply flex 22 | gap-0.5 23 | items-center; 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/balloon-toolbar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './BalloonToolbar'; 2 | export { default as BalloonToolbar } from './BalloonToolbar'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/buttons/ShortcodeToolbarButton.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_ShortcodeToolbarButton_button { 2 | @apply py-0.5 3 | px-0.5 4 | h-6 5 | w-6; 6 | } 7 | 8 | .CMS_WidgetMarkdown_ShortcodeToolbarButton_label-icon { 9 | @apply h-5 10 | w-5; 11 | } 12 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/buttons/common/ToolbarButton.css: -------------------------------------------------------------------------------- 1 | .CMS_Button_root { 2 | &.CMS_WidgetMarkdown_ToolbarButton_root { 3 | @apply py-0.5 4 | px-0.5; 5 | 6 | &.CMS_WidgetMarkdown_ToolbarButton_active { 7 | &:not(.CMS_WidgetMarkdown_ToolbarButton_custom-active-color) { 8 | color: var(--primary-main); 9 | background: color-mix(in srgb, var(--primary-main) 15%, transparent); 10 | } 11 | } 12 | } 13 | } 14 | 15 | .CMS_WidgetMarkdown_ToolbarButton_icon { 16 | @apply w-5 17 | h-5; 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/buttons/common/dropdown/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ToolbarDropdown'; 2 | export { default as ToolbarDropdown } from './ToolbarDropdown'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/color-picker/ColorButton.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_ColorButton_root { 2 | &.CMS_WidgetMarkdown_ColorButton_is-bright-color { 3 | & .CMS_WidgetMarkdown_ColorButton_avatar { 4 | border-color: var(--background-light); 5 | 6 | @apply border; 7 | } 8 | 9 | & .CMS_WidgetMarkdown_ColorButton_check-icon { 10 | color: var(--text-primary); 11 | } 12 | } 13 | } 14 | 15 | .CMS_WidgetMarkdown_ColorButton_avatar { 16 | @apply w-8 17 | h-8 18 | border-transparent; 19 | } 20 | 21 | .CMS_WidgetMarkdown_ColorButton_check-icon { 22 | color: var(--primary-contrast-color); 23 | 24 | @apply h-5 25 | w-5; 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/color-picker/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ColorButton'; 2 | export { default as ColorButton } from './ColorButton'; 3 | export * from './ColorInput'; 4 | export { default as ColorInput } from './ColorInput'; 5 | export * from './ColorPicker'; 6 | export { default as ColorPicker } from './ColorPicker'; 7 | export * from './Colors'; 8 | export { default as Colors } from './Colors'; 9 | export * from './CustomColors'; 10 | export { default as CustomColors } from './CustomColors'; 11 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/color-picker/types.ts: -------------------------------------------------------------------------------- 1 | export type ColorType = { 2 | name: string; 3 | value: string; 4 | isBrightColor: boolean; 5 | }; 6 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/common/MediaPopover.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_MediaPopover_root { 2 | background: var(--background-main); 3 | 4 | @apply absolute 5 | max-h-60 6 | overflow-auto 7 | rounded-md 8 | p-1 9 | text-base 10 | shadow-md 11 | focus:outline-none 12 | sm:text-sm 13 | z-40; 14 | } 15 | 16 | .CMS_WidgetMarkdown_MediaPopover_content { 17 | @apply flex 18 | gap-0.5; 19 | } 20 | 21 | .CMS_WidgetMarkdown_MediaPopover_icon { 22 | @apply w-4 23 | h-4; 24 | } 25 | 26 | .CMS_WidgetMarkdown_MediaPopover_divider { 27 | border-color: var(--background-light); 28 | 29 | @apply w-[1px] 30 | border; 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/common/index.ts: -------------------------------------------------------------------------------- 1 | export { default as MediaPopover } from './MediaPopover'; 2 | export * from './MediaPopover'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/index.ts: -------------------------------------------------------------------------------- 1 | export * from './balloon-toolbar'; 2 | export * from './buttons'; 3 | export * from './color-picker'; 4 | export * from './common'; 5 | export * from './nodes'; 6 | export * from './toolbar'; 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/blockquote/BlockquoteElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Blockquote_root { 2 | border-color: var(--background-light); 3 | 4 | @apply border-l-2 5 | ml-2 6 | pl-2 7 | my-2; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/blockquote/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export { default as BlockquoteElement } from './BlockquoteElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/code-block/CodeBlockElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_CodeBlock_root { 2 | @apply my-2; 3 | } 4 | 5 | .CMS_WidgetMarkdown_CodeBlock_language-input { 6 | background: var(--background-main); 7 | 8 | @apply w-full 9 | rounded-t-md 10 | px-2 11 | py-1 12 | h-6 13 | outline-none; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/code-block/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CodeBlockElement } from './CodeBlockElement'; 2 | export * from './CodeBlockFrame'; 3 | export { default as CodeBlockFrame } from './CodeBlockFrame'; 4 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/code/Code.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Code_root { 2 | color: var(--text-primary); 3 | background: var(--primary-main); 4 | 5 | @apply py-[0.2em] 6 | px-[0.4em] 7 | m-0 8 | text-sm 9 | whitespace-break-spaces 10 | rounded-md 11 | font-mono; 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/code/Code.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { MdValue } from '@staticcms/markdown'; 6 | import type { PlateRenderElementProps } from '@udecode/plate'; 7 | import type { FC } from 'react'; 8 | 9 | import './Code.css'; 10 | 11 | const classes = generateClassNames('WidgetMarkdown_Code', ['root']); 12 | 13 | const CodeElement: FC> = ({ attributes, children, nodeProps }) => { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | export default CodeElement; 22 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/Heading1.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Heading1_root { 2 | @apply text-[2em] 3 | leading-[1.5em] 4 | font-bold 5 | my-[0.67em]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/Heading2.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Heading2_root { 2 | @apply text-[1.5em] 3 | leading-[1.25em] 4 | font-bold 5 | my-[0.83em]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/Heading3.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Heading3_root { 2 | @apply text-[1.17em] 3 | leading-[1.25em] 4 | font-bold 5 | my-[1em]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/Heading4.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Heading4_root { 2 | @apply leading-[1.25em] 3 | font-bold 4 | my-[1.33em]; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/Heading5.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Heading5_root { 2 | @apply text-[0.83em] 3 | leading-[1.25em] 4 | font-bold 5 | my-[1.67em]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/Heading6.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Heading6_root { 2 | @apply text-[0.67em] 3 | leading-[1.25em] 4 | font-bold 5 | my-[2.33em]; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/headings/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Heading1 } from './Heading1'; 2 | export { default as Heading2 } from './Heading2'; 3 | export { default as Heading3 } from './Heading3'; 4 | export { default as Heading4 } from './Heading4'; 5 | export { default as Heading5 } from './Heading5'; 6 | export { default as Heading6 } from './Heading6'; 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/horizontal-rule/HrElement.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import type { PlateRenderElementProps } from '@udecode/plate'; 4 | import type { MdValue, MdHrElement } from '@staticcms/markdown'; 5 | import type { FC } from 'react'; 6 | 7 | const HrElement: FC> = props => { 8 | const { attributes, children, nodeProps } = props; 9 | 10 | return ( 11 |
12 |
13 | {children} 14 |
15 | ); 16 | }; 17 | 18 | export default HrElement; 19 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/horizontal-rule/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export { default as HrElement } from './HrElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/image/index.ts: -------------------------------------------------------------------------------- 1 | export { default as withImageElement } from './withImageElement'; 2 | export * from './withImageElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/index.ts: -------------------------------------------------------------------------------- 1 | export * from './blockquote'; 2 | export * from './code-block'; 3 | export * from './headings'; 4 | export * from './horizontal-rule'; 5 | export * from './image'; 6 | export * from './link'; 7 | export * from './list'; 8 | export * from './paragraph'; 9 | export * from './shortcode'; 10 | export * from './table'; 11 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/link/LinkElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Link_root { 2 | color: var(--primary-main); 3 | 4 | @apply cursor-pointer 5 | hover:underline; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/link/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as withLinkElement } from './withLinkElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/list/ListItemContentElement.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import type { MdListItemElement, MdValue } from '@staticcms/markdown'; 4 | import type { PlateRenderElementProps } from '@udecode/plate'; 5 | import type { FC } from 'react'; 6 | 7 | const ListItemContentElement: FC> = ({ 8 | children, 9 | }) => { 10 | return {children}; 11 | }; 12 | 13 | export default ListItemContentElement; 14 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/list/ListItemElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_ListItem_checkbox { 2 | @apply m-[5px] 3 | mr-2; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/list/OrderedListElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_OrderedList_root { 2 | @apply list-decimal 3 | pl-10; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/list/UnorderedListElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_UnorderedList_root { 2 | @apply list-disc 3 | pl-10; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/list/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ListItemContentElement } from './ListItemContentElement'; 2 | export { default as ListItemElement } from './ListItemElement'; 3 | export { default as OrderedListElement } from './OrderedListElement'; 4 | export { default as UnorderedListElement } from './UnorderedListElement'; 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/paragraph/ParagraphElement.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Paragraph_root { 2 | @apply block 3 | my-[1em]; 4 | } 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/paragraph/index.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | export { default as Paragraph } from './ParagraphElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/shortcode/index.ts: -------------------------------------------------------------------------------- 1 | export * from './withShortcodeElement'; 2 | export { default as withShortcodeElement } from './withShortcodeElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/table/Table.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const widgetMarkdownTableClasses = generateClassNames('WidgetMarkdown_Table', [ 4 | 'root', 5 | 'header', 6 | 'body', 7 | 'row', 8 | 'header-cell', 9 | 'body-cell', 10 | ]); 11 | 12 | export default widgetMarkdownTableClasses; 13 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/table/TableCellElement/index.ts: -------------------------------------------------------------------------------- 1 | export { default as TableCellElement } from './TableCellElement'; 2 | export { default as TableHeaderCellElement } from './TableHeaderCellElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/table/TableElement/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as TableElement } from './TableElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/table/TableRowElement/TableRowElement.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import widgetMarkdownTableClasses from '../Table.classes'; 4 | 5 | import type { MdTableRowElement, MdValue } from '@staticcms/markdown'; 6 | import type { PlateRenderElementProps } from '@udecode/plate'; 7 | import type { FC } from 'react'; 8 | 9 | const TableRowElement: FC> = ({ 10 | attributes, 11 | children, 12 | nodeProps, 13 | }) => { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | }; 20 | 21 | export default TableRowElement; 22 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/table/TableRowElement/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as TableRowElement } from './TableRowElement'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/nodes/table/index.ts: -------------------------------------------------------------------------------- 1 | export * from './TableCellElement/index'; 2 | export * from './TableElement/index'; 3 | export * from './TableRowElement/index'; 4 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/toolbar/Toolbar.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_Toolbar_root { 2 | background: var(--background-dark); 3 | border-color: color-mix(in srgb, var(--background-divider) 35%, transparent); 4 | 5 | @apply flex 6 | flex-wrap 7 | items-center 8 | select-none 9 | min-h-markdown-toolbar 10 | -mx-3 11 | -my-5 12 | px-2 13 | pt-2 14 | pb-1.5 15 | mb-1.5 16 | border-b 17 | gap-0.5 18 | shadow-sm 19 | sticky 20 | top-0 21 | z-10; 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/components/toolbar/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Toolbar'; 2 | export { default as Toolbar } from './Toolbar'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/editableProps.ts: -------------------------------------------------------------------------------- 1 | import type { TEditableProps } from '@udecode/plate'; 2 | import type { MdValue } from './plateTypes'; 3 | 4 | const editableProps: TEditableProps = { 5 | spellCheck: false, 6 | autoFocus: false, 7 | readOnly: false, 8 | }; 9 | 10 | export default editableProps; 11 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/hooks/index.ts: -------------------------------------------------------------------------------- 1 | export * from './useMarkdownToSlate'; 2 | export { default as useMarkdownToSlate } from './useMarkdownToSlate'; 3 | export * from './useMdx'; 4 | export { default as useMdx } from './useMdx'; 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/hooks/useToolbarButtons.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetMarkdown_MenuToolbar_root { 2 | } 3 | 4 | .CMS_WidgetMarkdown_MenuToolbar_button { 5 | @apply py-0.5 6 | px-0.5 7 | h-6 8 | w-6; 9 | } 10 | 11 | .CMS_WidgetMarkdown_MenuToolbar_label-icon { 12 | @apply h-5 13 | w-5; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/index.ts: -------------------------------------------------------------------------------- 1 | export * from './components'; 2 | export { default as editableProps } from './editableProps'; 3 | export * from './hooks'; 4 | export * from './PlateEditor'; 5 | export { default as PlateEditor } from './PlateEditor'; 6 | export * from './plateTypes'; 7 | export * from './plugins'; 8 | export * from './serialization'; 9 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/align/alignPlugin.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ELEMENT_H1, 3 | ELEMENT_H2, 4 | ELEMENT_H3, 5 | ELEMENT_H4, 6 | ELEMENT_H5, 7 | ELEMENT_H6, 8 | ELEMENT_PARAGRAPH, 9 | } from '@udecode/plate'; 10 | 11 | import type { MdPlatePlugin } from '@staticcms/markdown'; 12 | 13 | const alignPlugin: Partial = { 14 | inject: { 15 | props: { 16 | validTypes: [ 17 | ELEMENT_PARAGRAPH, 18 | ELEMENT_H1, 19 | ELEMENT_H2, 20 | ELEMENT_H3, 21 | ELEMENT_H4, 22 | ELEMENT_H5, 23 | ELEMENT_H6, 24 | ], 25 | }, 26 | }, 27 | }; 28 | 29 | export default alignPlugin; 30 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/align/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as alignPlugin } from './alignPlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/autoformat/autoformatPlugin.ts: -------------------------------------------------------------------------------- 1 | import autoformatRules from './autoformatRules'; 2 | 3 | import type { AutoformatPlugin } from '@udecode/plate'; 4 | import type { MdEditor, MdPlatePlugin, MdValue } from '@staticcms/markdown'; 5 | 6 | const autoformatPlugin: Partial>> = { 7 | options: { 8 | rules: autoformatRules, 9 | enableUndoOnDelete: true, 10 | }, 11 | }; 12 | 13 | export default autoformatPlugin; 14 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/autoformat/index.ts: -------------------------------------------------------------------------------- 1 | export { default as autoformatBlocks } from './autoformatBlocks'; 2 | export { default as autoformatLists } from './autoformatLists'; 3 | export { default as autoformatMarks } from './autoformatMarks'; 4 | export { default as autoformatPlugin } from './autoformatPlugin'; 5 | export { default as autoformatRules } from './autoformatRules'; 6 | export * from './autoformatUtils'; 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/code-block/index.ts: -------------------------------------------------------------------------------- 1 | export { default as createCodeBlockPlugin } from './createCodeBlockPlugin'; 2 | export { default as deserializeHtmlCodeBlock } from './deserializeHtmlCodeBlock'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/cursor-overlay/CursorOverlayContainer.tsx: -------------------------------------------------------------------------------- 1 | import { CursorOverlay } from '@udecode/plate-cursor'; 2 | import React from 'react'; 3 | 4 | import cursorStore from './cursorStore'; 5 | 6 | import type { CursorOverlayProps } from '@udecode/plate-cursor'; 7 | import type { FC } from 'react'; 8 | 9 | const CursorOverlayContainer: FC = ({ cursors, ...props }) => { 10 | const dynamicCursors = cursorStore.use.cursors(); 11 | 12 | const allCursors = { ...cursors, ...dynamicCursors }; 13 | 14 | return ; 15 | }; 16 | 17 | export default CursorOverlayContainer; 18 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/cursor-overlay/cursorStore.ts: -------------------------------------------------------------------------------- 1 | import { createStore } from '@udecode/plate'; 2 | 3 | const cursorStore = createStore('cursor')({ 4 | cursors: {}, 5 | }); 6 | 7 | export default cursorStore; 8 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/cursor-overlay/index.ts: -------------------------------------------------------------------------------- 1 | export { default as CursorOverlayContainer } from './CursorOverlayContainer'; 2 | export { default as cursorStore } from './cursorStore'; 3 | export { default as dragOverCursorPlugin } from './dragOverCursorPlugin'; 4 | export { default as staticCursors } from './staticCursors'; 5 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/cursor-overlay/staticCursors.tsx: -------------------------------------------------------------------------------- 1 | const staticCursors = { 2 | one: { 3 | key: 'one', 4 | data: { style: { backgroundColor: 'red' } }, 5 | selection: { 6 | anchor: { 7 | path: [0, 0], 8 | offset: 5, 9 | }, 10 | focus: { 11 | path: [0, 0], 12 | offset: 12, 13 | }, 14 | }, 15 | }, 16 | two: { 17 | key: 'two', 18 | data: { style: { backgroundColor: 'red' } }, 19 | selection: { 20 | anchor: { 21 | path: [0, 0], 22 | offset: 18, 23 | }, 24 | focus: { 25 | path: [0, 0], 26 | offset: 18, 27 | }, 28 | }, 29 | }, 30 | }; 31 | 32 | export default staticCursors; 33 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/exit-break/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as exitBreakPlugin } from './exitBreakPlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/indent/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as indentPlugin } from './indentPlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/index.ts: -------------------------------------------------------------------------------- 1 | export * from './align'; 2 | export * from './autoformat'; 3 | export * from './code-block'; 4 | export * from './cursor-overlay'; 5 | export * from './exit-break'; 6 | export * from './indent'; 7 | export * from './list'; 8 | export * from './reset-node'; 9 | export * from './select-on-backspace'; 10 | export * from './shortcode'; 11 | export * from './soft-break'; 12 | export * from './table'; 13 | export * from './trailing-block'; 14 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/list/index.ts: -------------------------------------------------------------------------------- 1 | export * from './createListPlugin'; 2 | export { default as createListPlugin } from './createListPlugin'; 3 | export { default as insertBreakList } from './insertBreakList'; 4 | export * from './transforms'; 5 | export { default as withList } from './withList'; 6 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/list/transforms/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as insertListItem } from './insertListItem'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/reset-node/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as resetBlockTypePlugin } from './resetBlockTypePlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/select-on-backspace/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as selectOnBackspacePlugin } from './selectOnBackspacePlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/select-on-backspace/selectOnBackspacePlugin.ts: -------------------------------------------------------------------------------- 1 | import { ELEMENT_HR, ELEMENT_IMAGE } from '@udecode/plate'; 2 | 3 | import type { SelectOnBackspacePlugin } from '@udecode/plate'; 4 | import type { MdPlatePlugin } from '@staticcms/markdown'; 5 | 6 | const selectOnBackspacePlugin: Partial> = { 7 | options: { 8 | query: { 9 | allow: [ELEMENT_IMAGE, ELEMENT_HR], 10 | }, 11 | }, 12 | }; 13 | 14 | export default selectOnBackspacePlugin; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/shortcode/createShortcodePlugin.ts: -------------------------------------------------------------------------------- 1 | import { createPluginFactory } from '@udecode/plate'; 2 | 3 | import { ELEMENT_SHORTCODE } from '../../plateTypes'; 4 | 5 | const createShortcodePlugin = createPluginFactory({ 6 | key: ELEMENT_SHORTCODE, 7 | isElement: true, 8 | isInline: true, 9 | type: ELEMENT_SHORTCODE, 10 | }); 11 | 12 | export default createShortcodePlugin; 13 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/shortcode/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as createShortcodePlugin } from './createShortcodePlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/soft-break/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as softBreakPlugin } from './softBreakPlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/soft-break/softBreakPlugin.ts: -------------------------------------------------------------------------------- 1 | import { ELEMENT_BLOCKQUOTE, ELEMENT_CODE_BLOCK, ELEMENT_TD } from '@udecode/plate'; 2 | 3 | import type { SoftBreakPlugin } from '@udecode/plate'; 4 | import type { MdPlatePlugin } from '@staticcms/markdown'; 5 | 6 | const softBreakPlugin: Partial> = { 7 | options: { 8 | rules: [ 9 | { 10 | hotkey: 'enter', 11 | query: { 12 | allow: [ELEMENT_CODE_BLOCK, ELEMENT_BLOCKQUOTE, ELEMENT_TD], 13 | }, 14 | }, 15 | ], 16 | }, 17 | }; 18 | 19 | export default softBreakPlugin; 20 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/table/index.ts: -------------------------------------------------------------------------------- 1 | export { default as createTablePlugin } from './createTablePlugin'; 2 | export { default as withTable } from './withTable'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/trailing-block/index.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line import/prefer-default-export 2 | export { default as trailingBlockPlugin } from './trailingBlockPlugin'; 3 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/plugins/trailing-block/trailingBlockPlugin.ts: -------------------------------------------------------------------------------- 1 | import { ELEMENT_PARAGRAPH } from '@udecode/plate'; 2 | 3 | import type { TrailingBlockPlugin } from '@udecode/plate'; 4 | import type { MdPlatePlugin } from '@staticcms/markdown'; 5 | 6 | const trailingBlockPlugin: Partial> = { 7 | options: { type: ELEMENT_PARAGRAPH }, 8 | }; 9 | 10 | export default trailingBlockPlugin; 11 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/serialization/index.ts: -------------------------------------------------------------------------------- 1 | export * from './serializeMarkdown'; 2 | export { default as serializeMarkdown } from './serializeMarkdown'; 3 | export * from './slate'; 4 | -------------------------------------------------------------------------------- /packages/core/src/widgets/markdown/plate/serialization/slate/index.ts: -------------------------------------------------------------------------------- 1 | export * from './ast-types'; 2 | export * from './deserializeMarkdown'; 3 | export { default as deserializeMarkdown } from './deserializeMarkdown'; 4 | export { default as flattenListItemParagraphs } from './flattenListItemParagraphs'; 5 | export * from './toSlatePlugin'; 6 | export { default as toSlatePlugin } from './toSlatePlugin'; 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/number/NumberControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetNumber_root { 2 | & .CMS_WidgetNumber_input { 3 | &.CMS_WidgetNumber_with-prefix { 4 | @apply ps-0.5; 5 | } 6 | 7 | &.CMS_WidgetNumber_with-suffix { 8 | @apply pe-0.5; 9 | } 10 | } 11 | } 12 | 13 | .CMS_WidgetNumber_prefix { 14 | color: var(--text-secondary); 15 | 16 | @apply ps-3 17 | text-sm 18 | flex 19 | items-center 20 | whitespace-nowrap; 21 | 22 | line-height: 100%; 23 | } 24 | 25 | .CMS_WidgetNumber_suffix { 26 | color: var(--text-secondary); 27 | 28 | @apply pe-3 29 | text-sm 30 | flex 31 | items-center 32 | whitespace-nowrap; 33 | 34 | line-height: 100%; 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/widgets/number/NumberPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { NumberField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetNumberPreview', ['root']); 9 | 10 | const NumberPreview: FC> = ({ value }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default NumberPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/number/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | step: { type: 'number' }, 4 | value_type: { type: 'string' }, 5 | min: { type: 'number' }, 6 | max: { type: 'number' }, 7 | default: { type: 'number' }, 8 | prefix: { type: 'string' }, 9 | suffix: { type: 'string' }, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/core/src/widgets/object/ObjectControl.classes.ts: -------------------------------------------------------------------------------- 1 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 2 | 3 | const widgetObjectClasses = generateClassNames('WidgetObject', [ 4 | 'root', 5 | 'list-root', 6 | 'error', 7 | 'disabled', 8 | 'required', 9 | 'for-single-list', 10 | 'open', 11 | 'expand', 12 | 'expand-icon', 13 | 'summary', 14 | 'fields', 15 | 'error-message', 16 | ]); 17 | 18 | export default widgetObjectClasses; 19 | -------------------------------------------------------------------------------- /packages/core/src/widgets/object/ObjectPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { ObjectField, ObjectValue, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetObjectPreview', ['root']); 9 | 10 | const ObjectPreview: FC> = ({ field }) => { 11 | return
{field.renderedFields ?? null}
; 12 | }; 13 | 14 | export default ObjectPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/object/index.ts: -------------------------------------------------------------------------------- 1 | import controlComponent from './ObjectControl'; 2 | import previewComponent from './ObjectPreview'; 3 | import schema from './schema'; 4 | 5 | import type { ObjectField, ObjectValue, WidgetParam } from '@staticcms/core'; 6 | 7 | const ObjectWidget = (): WidgetParam => { 8 | return { 9 | name: 'object', 10 | controlComponent, 11 | previewComponent, 12 | options: { 13 | schema, 14 | }, 15 | }; 16 | }; 17 | 18 | export { 19 | controlComponent as ObjectControl, 20 | previewComponent as ObjectPreview, 21 | schema as ObjectSchema, 22 | }; 23 | 24 | export default ObjectWidget; 25 | -------------------------------------------------------------------------------- /packages/core/src/widgets/object/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'object' }, 4 | collapsed: { type: 'boolean' }, 5 | summary: { type: 'string' }, 6 | i18n: { type: 'boolean' }, 7 | fields: { type: 'array', items: { type: 'object' } }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/core/src/widgets/relation/RelationControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetRelation_root { 2 | } 3 | 4 | .CMS_WidgetRelation_values { 5 | @apply flex 6 | gap-2 7 | pr-0 8 | min-w-0; 9 | 10 | .CMS_Field_wrapper & { 11 | @apply flex-wrap 12 | p-2; 13 | } 14 | } 15 | 16 | .CMS_WidgetRelation_loading { 17 | @apply flex 18 | items-center 19 | w-6 20 | h-6; 21 | } 22 | -------------------------------------------------------------------------------- /packages/core/src/widgets/relation/RelationPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { RelationField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetRelationPreview', ['root']); 9 | 10 | const RelationPreview: FC> = ({ value }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default RelationPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/relation/types.ts: -------------------------------------------------------------------------------- 1 | import type { EntryData } from '@staticcms/core'; 2 | 3 | export interface HitOption { 4 | data: EntryData; 5 | value: string; 6 | label: string; 7 | } 8 | -------------------------------------------------------------------------------- /packages/core/src/widgets/relation/validator.ts: -------------------------------------------------------------------------------- 1 | import { validateMinMax } from '@staticcms/core/lib/widgets/validations'; 2 | 3 | import type { FieldValidationMethod, RelationField } from '@staticcms/core'; 4 | 5 | const validator: FieldValidationMethod = ({ 6 | field, 7 | value, 8 | t, 9 | }) => { 10 | const min = field.min; 11 | const max = field.max; 12 | 13 | if (!field.multiple || typeof value === 'string') { 14 | return false; 15 | } 16 | 17 | const error = validateMinMax(t, field.label ?? field.name, value, min, max); 18 | 19 | return error ? error : false; 20 | }; 21 | 22 | export default validator; 23 | -------------------------------------------------------------------------------- /packages/core/src/widgets/select/SelectControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetSelect_values { 2 | @apply flex 3 | flex-wrap 4 | gap-2 5 | max-w-fit; 6 | } 7 | -------------------------------------------------------------------------------- /packages/core/src/widgets/select/validator.ts: -------------------------------------------------------------------------------- 1 | import { validateMinMax } from '@staticcms/core/lib/widgets/validations'; 2 | 3 | import type { FieldValidationMethod, SelectField } from '@staticcms/core'; 4 | 5 | const validator: FieldValidationMethod = ({ 6 | field, 7 | value, 8 | t, 9 | }) => { 10 | const min = field.min; 11 | const max = field.max; 12 | 13 | if (!field.multiple || typeof value === 'string') { 14 | return false; 15 | } 16 | 17 | const error = validateMinMax(t, field.label ?? field.name, value, min, max); 18 | 19 | return error ? error : false; 20 | }; 21 | 22 | export default validator; 23 | -------------------------------------------------------------------------------- /packages/core/src/widgets/string/StringControl.css: -------------------------------------------------------------------------------- 1 | .CMS_WidgetString_root { 2 | & .CMS_WidgetString_input { 3 | &.CMS_WidgetString_with-prefix { 4 | @apply ps-0.5; 5 | } 6 | 7 | &.CMS_WidgetString_with-suffix { 8 | @apply pe-0.5; 9 | } 10 | } 11 | } 12 | 13 | .CMS_WidgetString_prefix { 14 | color: var(--text-secondary); 15 | 16 | @apply ps-3 17 | text-sm 18 | flex 19 | items-center 20 | whitespace-nowrap; 21 | 22 | line-height: 100%; 23 | } 24 | 25 | .CMS_WidgetString_suffix { 26 | color: var(--text-secondary); 27 | 28 | @apply pe-3 29 | text-sm 30 | flex 31 | items-center 32 | whitespace-nowrap; 33 | 34 | line-height: 100%; 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/widgets/string/StringPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { StringField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetStringPreview', ['root']); 9 | 10 | const StringPreview: FC> = ({ value = '' }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default StringPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/string/index.ts: -------------------------------------------------------------------------------- 1 | import schema from './schema'; 2 | import controlComponent from './StringControl'; 3 | import previewComponent from './StringPreview'; 4 | 5 | import type { StringField, WidgetParam } from '@staticcms/core'; 6 | 7 | const StringWidget = (): WidgetParam => { 8 | return { 9 | name: 'string', 10 | controlComponent, 11 | previewComponent, 12 | options: { 13 | schema, 14 | }, 15 | }; 16 | }; 17 | 18 | export { 19 | controlComponent as StringControl, 20 | previewComponent as StringPreview, 21 | schema as stringSchema, 22 | }; 23 | 24 | export default StringWidget; 25 | -------------------------------------------------------------------------------- /packages/core/src/widgets/string/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'string' }, 4 | prefix: { type: 'string' }, 5 | suffix: { type: 'string' }, 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/src/widgets/text/TextPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { TextField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetTextPreview', ['root']); 9 | 10 | const TextPreview: FC> = ({ value }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default TextPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/text/index.ts: -------------------------------------------------------------------------------- 1 | import schema from './schema'; 2 | import controlComponent from './TextControl'; 3 | import previewComponent from './TextPreview'; 4 | 5 | import type { TextField, WidgetParam } from '@staticcms/core'; 6 | 7 | const TextWidget = (): WidgetParam => { 8 | return { 9 | name: 'text', 10 | controlComponent, 11 | previewComponent, 12 | options: { 13 | schema, 14 | }, 15 | }; 16 | }; 17 | 18 | export { controlComponent as TextControl, previewComponent as TextPreview, schema as textSchema }; 19 | 20 | export default TextWidget; 21 | -------------------------------------------------------------------------------- /packages/core/src/widgets/text/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | default: { type: 'string' }, 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/core/src/widgets/uuid/UUIDControl.css: -------------------------------------------------------------------------------- 1 | .CMS_UUID_root { 2 | } 3 | 4 | .CMS_UUID_refresh-button { 5 | } 6 | 7 | .CMS_UUID_input { 8 | @apply truncate; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/widgets/uuid/UUIDPreview.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import { generateClassNames } from '@staticcms/core/lib/util/theming.util'; 4 | 5 | import type { UUIDField, WidgetPreviewProps } from '@staticcms/core'; 6 | import type { FC } from 'react'; 7 | 8 | const classes = generateClassNames('WidgetUUIDPreview', ['root']); 9 | 10 | const StringPreview: FC> = ({ value = '' }) => { 11 | return
{value}
; 12 | }; 13 | 14 | export default StringPreview; 15 | -------------------------------------------------------------------------------- /packages/core/src/widgets/uuid/index.ts: -------------------------------------------------------------------------------- 1 | import schema from './schema'; 2 | import controlComponent from './UUIDControl'; 3 | import previewComponent from './UUIDPreview'; 4 | 5 | import type { UUIDField, WidgetParam } from '@staticcms/core'; 6 | 7 | const UUIDWidget = (): WidgetParam => { 8 | return { 9 | name: 'uuid', 10 | controlComponent, 11 | previewComponent, 12 | options: { 13 | schema, 14 | }, 15 | }; 16 | }; 17 | 18 | export { controlComponent as UUIDControl, previewComponent as UUIDPreview, schema as uuidSchema }; 19 | 20 | export default UUIDWidget; 21 | -------------------------------------------------------------------------------- /packages/core/src/widgets/uuid/schema.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | properties: { 3 | allow_regenerate: { type: 'boolean' }, 4 | prefix: { type: 'string' }, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/core/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const baseConfig = require('../../tailwind.base.config'); 2 | 3 | /** @type {import('tailwindcss').Config} */ 4 | module.exports = { 5 | content: ['./src/**/*.tsx'], 6 | ...baseConfig, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/core/test/fileTransformer.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | process(src, filename) { 5 | return { 6 | code: `module.exports = ${JSON.stringify(path.basename(filename))};`, 7 | }; 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/core/test/globalSetup.js: -------------------------------------------------------------------------------- 1 | module.exports = async () => { 2 | process.env.TZ = 'US/Hawaii'; 3 | }; 4 | 5 | globalThis.IS_REACT_ACT_ENVIRONMENT = true; 6 | -------------------------------------------------------------------------------- /packages/core/test/mockLocalStorage.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-explicit-any */ 2 | const mockLocalStorage = (function () { 3 | let store: Record = {}; 4 | return { 5 | getItem(key: string) { 6 | return store[key] ?? null; 7 | }, 8 | setItem(key: string, value: any) { 9 | store[key] = value.toString(); 10 | }, 11 | clear() { 12 | store = {}; 13 | }, 14 | removeItem(key: string) { 15 | delete store[key]; 16 | }, 17 | }; 18 | })(); 19 | 20 | Object.defineProperty(window, 'localStorage', { value: mockLocalStorage }); 21 | 22 | export {}; 23 | -------------------------------------------------------------------------------- /packages/core/test/test-utils.tsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/prefer-default-export */ 2 | import { render } from '@testing-library/react'; 3 | import React from 'react'; 4 | import { Provider } from 'react-redux'; 5 | 6 | import { store } from '@staticcms/core/store'; 7 | 8 | import type { PropsWithChildren } from 'react'; 9 | 10 | export function renderWithProviders(ui: React.ReactElement) { 11 | function Wrapper({ children }: PropsWithChildren<{}>): JSX.Element { 12 | return {children}; 13 | } 14 | 15 | // Return an object with the store and all of RTL's query functions 16 | return { store, ...render(ui, { wrapper: Wrapper }) }; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/test/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.dev.json" 3 | } 4 | -------------------------------------------------------------------------------- /packages/core/tsconfig.dev.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["src/*", "src/**/*", "test/**/*"], 4 | "exclude": ["node_modules"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "include": ["src/**/*"], 4 | "exclude": ["node_modules", "**/*.spec.ts*"] 5 | } 6 | -------------------------------------------------------------------------------- /packages/demo/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | public/ 4 | .cache/ 5 | -------------------------------------------------------------------------------- /packages/demo/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /packages/demo/public/_posts/assets/uploads/lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/_posts/assets/uploads/lobby.jpg -------------------------------------------------------------------------------- /packages/demo/public/_posts/assets/uploads/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/_posts/assets/uploads/moby-dick.jpg -------------------------------------------------------------------------------- /packages/demo/public/assets/uploads/Other Pics/lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/assets/uploads/Other Pics/lobby.jpg -------------------------------------------------------------------------------- /packages/demo/public/assets/uploads/Other Pics/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/assets/uploads/Other Pics/moby-dick.jpg -------------------------------------------------------------------------------- /packages/demo/public/assets/uploads/lobby.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/assets/uploads/lobby.jpg -------------------------------------------------------------------------------- /packages/demo/public/assets/uploads/moby-dick.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/assets/uploads/moby-dick.jpg -------------------------------------------------------------------------------- /packages/demo/public/static-cms-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/demo/public/static-cms-icon.png -------------------------------------------------------------------------------- /packages/demo/src/editorial/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Static CMS Demo - Editorial 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/demo/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | 4 | import "./cms"; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById("root")); 7 | root.render( 8 | 9 |
10 | 11 | ); 12 | -------------------------------------------------------------------------------- /packages/demo/src/simple/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Static CMS Demo - Simple 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/docs/.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | indent_size = 2 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | 11 | [*.js] 12 | quote_type = single 13 | spaces_around_operators = true 14 | 15 | [*.css] 16 | quote_type = single 17 | 18 | [*.md] 19 | trim_trailing_whitespace = false 20 | 21 | [*.mdx] 22 | trim_trailing_whitespace = false 23 | -------------------------------------------------------------------------------- /packages/docs/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | dev-test 4 | prism.js 5 | next.config.js 6 | -------------------------------------------------------------------------------- /packages/docs/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "next/core-web-vitals" 3 | } 4 | -------------------------------------------------------------------------------- /packages/docs/.prettierignore: -------------------------------------------------------------------------------- 1 | dist/ 2 | bin/ 3 | public/ 4 | .cache/ -------------------------------------------------------------------------------- /packages/docs/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /packages/docs/content/docs/customization-overview.mdx: -------------------------------------------------------------------------------- 1 | --- 2 | group: Customization 3 | title: Overview 4 | weight: 1 5 | --- 6 | 7 | Static CMS exposes a `window.CMS` global object that you can use to customize your CMS. The same object is also the default export if you import Static CMS as an npm module. Available options are: 8 | 9 | - Register [custom widgets](/docs/custom-widgets) 10 | - Register [custom previews](/docs/custom-previews) 11 | - Register [editor customizations](/docs/widget-markdown#customization) 12 | - Register [additional menu links or custom pages](/docs/additional-links) 13 | - Register [custom icons](/docs/custom-icons) 14 | -------------------------------------------------------------------------------- /packages/docs/design/your-content-your-way.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/design/your-content-your-way.xcf -------------------------------------------------------------------------------- /packages/docs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/favicon.ico -------------------------------------------------------------------------------- /packages/docs/public/icons/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/apple-touch-icon.png -------------------------------------------------------------------------------- /packages/docs/public/icons/favicon_maskable.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/favicon_maskable.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-144x144.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-192x192.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-256x256.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-32x32.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-384x384.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-384x384.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-48x48.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-512x512.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-72x72.png -------------------------------------------------------------------------------- /packages/docs/public/icons/icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/icons/icon-96x96.png -------------------------------------------------------------------------------- /packages/docs/public/img/build-deployment.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/build-deployment.webp -------------------------------------------------------------------------------- /packages/docs/public/img/cloudinary-console-details.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/cloudinary-console-details.webp -------------------------------------------------------------------------------- /packages/docs/public/img/create-password.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/create-password.webp -------------------------------------------------------------------------------- /packages/docs/public/img/create-remote-repo.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/create-remote-repo.webp -------------------------------------------------------------------------------- /packages/docs/public/img/email-subject.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/email-subject.webp -------------------------------------------------------------------------------- /packages/docs/public/img/hugo_shortcode_gist.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/hugo_shortcode_gist.webp -------------------------------------------------------------------------------- /packages/docs/public/img/metalsmith.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Path 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/docs/public/img/preview_card_grid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/preview_card_grid.png -------------------------------------------------------------------------------- /packages/docs/public/img/preview_draft_field_table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/preview_draft_field_table.png -------------------------------------------------------------------------------- /packages/docs/public/img/screenshot-editor.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/screenshot-editor.webp -------------------------------------------------------------------------------- /packages/docs/public/img/widgets-markdown.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/widgets-markdown.webp -------------------------------------------------------------------------------- /packages/docs/public/img/your-content-your-way.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/packages/docs/public/img/your-content-your-way.webp -------------------------------------------------------------------------------- /packages/docs/src/components/context/ColorModeContext.tsx: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | // eslint-disable-next-line @typescript-eslint/no-empty-function 4 | const ColorModeContext = createContext({ toggleColorMode: () => {} }); 5 | 6 | export default ColorModeContext; 7 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/DocsRightNav.tsx: -------------------------------------------------------------------------------- 1 | import DocsTableOfContents from './table_of_contents/DocsTableOfContents'; 2 | 3 | const DocsRightNav = () => { 4 | return ; 5 | }; 6 | 7 | export default DocsRightNav; 8 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/Alert.tsx: -------------------------------------------------------------------------------- 1 | import MuiAlert from '@mui/material/Alert'; 2 | 3 | import type { AlertProps } from '@mui/material/Alert'; 4 | 5 | const Alert = ({ children, ...props }: AlertProps) => { 6 | return ( 7 | 22 | {children} 23 | 24 | ); 25 | }; 26 | 27 | export default Alert; 28 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/Templates.tsx: -------------------------------------------------------------------------------- 1 | import Box from '@mui/material/Box'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface TemplatesProps { 6 | children: ReactNode; 7 | } 8 | 9 | const Templates = ({ children }: TemplatesProps) => { 10 | return ( 11 | 19 | {children} 20 | 21 | ); 22 | }; 23 | 24 | export default Templates; 25 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/Header1.tsx: -------------------------------------------------------------------------------- 1 | import Typography from '@mui/material/Typography'; 2 | 3 | import { useNodeText } from '../../../../util/node.util'; 4 | import useAnchor from './hooks/useAnchor'; 5 | 6 | import type { ReactNode } from 'react'; 7 | 8 | interface Header1Props { 9 | children?: ReactNode; 10 | } 11 | 12 | const Header1 = ({ children }: Header1Props) => { 13 | const textContent = useNodeText(children); 14 | const anchor = useAnchor(textContent); 15 | 16 | return ( 17 | 18 | {children} 19 | 20 | ); 21 | }; 22 | 23 | export default Header1; 24 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/Header2.tsx: -------------------------------------------------------------------------------- 1 | import LinkedHeader from './components/LinkedHeader'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface Header2Props { 6 | children?: ReactNode; 7 | } 8 | 9 | const Header2 = ({ children }: Header2Props) => { 10 | return {children}; 11 | }; 12 | 13 | export default Header2; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/Header3.tsx: -------------------------------------------------------------------------------- 1 | import LinkedHeader from './components/LinkedHeader'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface Header3Props { 6 | children?: ReactNode; 7 | } 8 | 9 | const Header3 = ({ children }: Header3Props) => { 10 | return {children}; 11 | }; 12 | 13 | export default Header3; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/Header4.tsx: -------------------------------------------------------------------------------- 1 | import LinkedHeader from './components/LinkedHeader'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface Header4Props { 6 | children?: ReactNode; 7 | } 8 | 9 | const Header4 = ({ children }: Header4Props) => { 10 | return {children}; 11 | }; 12 | 13 | export default Header4; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/Header5.tsx: -------------------------------------------------------------------------------- 1 | import LinkedHeader from './components/LinkedHeader'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface Header5Props { 6 | children?: ReactNode; 7 | } 8 | 9 | const Header5 = ({ children }: Header5Props) => { 10 | return {children}; 11 | }; 12 | 13 | export default Header5; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/Header6.tsx: -------------------------------------------------------------------------------- 1 | import LinkedHeader from './components/LinkedHeader'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface Header6Props { 6 | children?: ReactNode; 7 | } 8 | 9 | const Header6 = ({ children }: Header6Props) => { 10 | return {children}; 11 | }; 12 | 13 | export default Header6; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/headers/hooks/useAnchor.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | export const getAnchor = (text: string) => { 4 | return text 5 | .trim() 6 | .toLowerCase() 7 | .replace(/[^a-z0-9 \-_]/g, '') 8 | .replace(/[ ]/g, '-'); 9 | }; 10 | 11 | const useAnchor = (text: string) => { 12 | return useMemo(() => getAnchor(text), [text]); 13 | }; 14 | 15 | export default useAnchor; 16 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/table/TableBody.tsx: -------------------------------------------------------------------------------- 1 | import MuiTableBody from '@mui/material/TableBody'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface TableBodyProps { 6 | children?: ReactNode; 7 | } 8 | 9 | const TableBody = ({ children }: TableBodyProps) => { 10 | return {children}; 11 | }; 12 | 13 | export default TableBody; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/table/TableHead.tsx: -------------------------------------------------------------------------------- 1 | import MuiTableHead from '@mui/material/TableHead'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface TableHeadProps { 6 | children?: ReactNode; 7 | } 8 | 9 | const TableHead = ({ children }: TableHeadProps) => { 10 | return {children}; 11 | }; 12 | 13 | export default TableHead; 14 | -------------------------------------------------------------------------------- /packages/docs/src/components/docs/components/table/TableRow.tsx: -------------------------------------------------------------------------------- 1 | import MuiTableRow from '@mui/material/TableRow'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | interface TableRowProps { 6 | children?: ReactNode; 7 | } 8 | 9 | const TableRow = ({ children }: TableRowProps) => { 10 | return ( 11 | {children} 12 | ); 13 | }; 14 | 15 | export default TableRow; 16 | -------------------------------------------------------------------------------- /packages/docs/src/components/layout/Container.tsx: -------------------------------------------------------------------------------- 1 | import { styled } from '@mui/material/styles'; 2 | 3 | import type { ReactNode } from 'react'; 4 | 5 | const StyledContainer = styled('div')( 6 | ({ theme }) => ` 7 | max-width: 1280px; 8 | width: 100%; 9 | padding: 0 40px; 10 | display: flex; 11 | flex-direction: column; 12 | align-items: center; 13 | 14 | ${theme.breakpoints.down('md')} { 15 | padding: 0 32px; 16 | } 17 | `, 18 | ); 19 | 20 | export interface PageProps { 21 | children: ReactNode; 22 | } 23 | 24 | const Container = ({ children }: PageProps) => { 25 | return {children}; 26 | }; 27 | 28 | export default Container; 29 | -------------------------------------------------------------------------------- /packages/docs/src/constants.ts: -------------------------------------------------------------------------------- 1 | export const SEARCH_RESULTS_TO_SHOW = 10; 2 | 3 | export const SUMMARY_MIN_PARAGRAPH_LENGTH = 150; 4 | -------------------------------------------------------------------------------- /packages/docs/src/lib/community.ts: -------------------------------------------------------------------------------- 1 | import data from '../../content/community.json'; 2 | 3 | import type { CommunityData } from '../interface'; 4 | 5 | export default data as CommunityData; 6 | -------------------------------------------------------------------------------- /packages/docs/src/lib/config.ts: -------------------------------------------------------------------------------- 1 | import config from '../../content/config.json'; 2 | 3 | import type { SiteConfig } from '../interface'; 4 | 5 | export default config as SiteConfig; 6 | -------------------------------------------------------------------------------- /packages/docs/src/lib/homepage.ts: -------------------------------------------------------------------------------- 1 | import data from '../../content/homepage.json'; 2 | 3 | import type { HomepageData } from '../interface'; 4 | 5 | export default data as HomepageData; 6 | -------------------------------------------------------------------------------- /packages/docs/src/lib/menu.ts: -------------------------------------------------------------------------------- 1 | import data from '../../content/menu.json'; 2 | 3 | import type { Menu } from '../interface'; 4 | 5 | export default data.menu as Menu; 6 | -------------------------------------------------------------------------------- /packages/docs/src/lib/releases.ts: -------------------------------------------------------------------------------- 1 | import data from '../../content/releases.json'; 2 | 3 | import type { Release } from '../interface'; 4 | 5 | export default data.releases as Release[]; 6 | -------------------------------------------------------------------------------- /packages/docs/src/pages/_document.tsx: -------------------------------------------------------------------------------- 1 | import { Head, Html, Main, NextScript } from 'next/document'; 2 | 3 | export default function Document() { 4 | return ( 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /packages/docs/src/util/node.util.ts: -------------------------------------------------------------------------------- 1 | import { useMemo } from 'react'; 2 | 3 | import { isNotEmpty } from './string.util'; 4 | 5 | import type { ReactNode } from 'react'; 6 | 7 | export const getNodeText = (node: ReactNode): string => { 8 | if (['string', 'number'].includes(typeof node)) { 9 | return `${node}`; 10 | } 11 | 12 | if (node instanceof Array) { 13 | return node.map(getNodeText).filter(isNotEmpty).join(''); 14 | } 15 | 16 | if (typeof node === 'object' && node && 'props' in node) { 17 | return getNodeText(node.props.children); 18 | } 19 | 20 | return ''; 21 | }; 22 | 23 | export const useNodeText = (node: ReactNode) => useMemo(() => getNodeText(node), [node]); 24 | -------------------------------------------------------------------------------- /packages/docs/src/util/null.util.ts: -------------------------------------------------------------------------------- 1 | export function isNotNullish(value: T | null | undefined): value is T { 2 | return value !== undefined && value !== null; 3 | } 4 | 5 | export function isNullish(value: T | null | undefined): value is null | undefined { 6 | return value === undefined || value === null; 7 | } 8 | 9 | export function filterNullish(value: (T | null | undefined)[] | null | undefined): T[] { 10 | return value?.filter(isNotNullish) ?? []; 11 | } 12 | -------------------------------------------------------------------------------- /packages/docs/src/util/transientOptions.ts: -------------------------------------------------------------------------------- 1 | import type { CreateMUIStyled } from '@mui/material/styles'; 2 | 3 | const transientOptions: Parameters[1] = { 4 | shouldForwardProp: (propName: string) => !propName.startsWith('$'), 5 | }; 6 | 7 | export default transientOptions; 8 | -------------------------------------------------------------------------------- /packages/docs/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "noEmit": true, 10 | "esModuleInterop": true, 11 | "module": "esnext", 12 | "moduleResolution": "node", 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "jsx": "preserve", 16 | "incremental": true 17 | }, 18 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], 19 | "exclude": ["node_modules"] 20 | } 21 | -------------------------------------------------------------------------------- /packages/tools/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "avoid", 3 | "trailingComma": "all", 4 | "singleQuote": true, 5 | "printWidth": 100 6 | } 7 | -------------------------------------------------------------------------------- /static-cms-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/static-cms-icon.png -------------------------------------------------------------------------------- /static-cms-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/StaticJsCMS/static-cms/c42e4e9bd867dd9c3a69f8372ee0b69f0f61877a/static-cms-logo.png --------------------------------------------------------------------------------