├── .VSCodeCounter ├── 2021-11-21 - Angular src folder │ ├── details.md │ ├── results.csv │ ├── results.md │ └── results.txt └── 2021-11-21 - React src folder │ ├── details.md │ ├── results.csv │ ├── results.md │ └── results.txt ├── .github └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── README.md ├── app ├── .env.development ├── .env.production ├── .eslintrc.js ├── .gitignore ├── config-overrides.js ├── craco.config.js ├── package.json ├── public │ ├── assets │ │ ├── icons │ │ │ ├── android │ │ │ │ ├── android-launchericon-144-144.png │ │ │ │ ├── android-launchericon-192-192.png │ │ │ │ ├── android-launchericon-48-48.png │ │ │ │ ├── android-launchericon-512-512.png │ │ │ │ ├── android-launchericon-72-72.png │ │ │ │ ├── android-launchericon-96-96.png │ │ │ │ └── maskable_icon.png │ │ │ ├── favicon │ │ │ │ ├── android-chrome-192x192.png │ │ │ │ ├── android-chrome-512x512.png │ │ │ │ ├── apple-touch-icon.png │ │ │ │ ├── browserconfig.xml │ │ │ │ ├── favicon-16x16.png │ │ │ │ ├── favicon-32x32.png │ │ │ │ ├── favicon.ico │ │ │ │ ├── mstile-150x150.png │ │ │ │ └── safari-pinned-tab.svg │ │ │ ├── ios │ │ │ │ ├── 100.png │ │ │ │ ├── 1024.png │ │ │ │ ├── 114.png │ │ │ │ ├── 120.png │ │ │ │ ├── 128.png │ │ │ │ ├── 144.png │ │ │ │ ├── 152.png │ │ │ │ ├── 16.png │ │ │ │ ├── 167.png │ │ │ │ ├── 180.png │ │ │ │ ├── 192.png │ │ │ │ ├── 20.png │ │ │ │ ├── 256.png │ │ │ │ ├── 29.png │ │ │ │ ├── 32.png │ │ │ │ ├── 40.png │ │ │ │ ├── 50.png │ │ │ │ ├── 512.png │ │ │ │ ├── 57.png │ │ │ │ ├── 58.png │ │ │ │ ├── 60.png │ │ │ │ ├── 64.png │ │ │ │ ├── 72.png │ │ │ │ ├── 76.png │ │ │ │ ├── 80.png │ │ │ │ └── 87.png │ │ │ ├── svg │ │ │ │ ├── format_align_center.svg │ │ │ │ ├── format_align_justify.svg │ │ │ │ ├── format_align_left.svg │ │ │ │ ├── format_align_right.svg │ │ │ │ ├── format_bold.svg │ │ │ │ ├── format_code.svg │ │ │ │ ├── format_italic.svg │ │ │ │ ├── format_list_bulleted.svg │ │ │ │ ├── format_list_numbered.svg │ │ │ │ ├── format_looks_one.svg │ │ │ │ ├── format_looks_two.svg │ │ │ │ ├── format_quote.svg │ │ │ │ ├── format_underlined.svg │ │ │ │ └── table.svg │ │ │ └── windows11 │ │ │ │ ├── LargeTile.scale-100.png │ │ │ │ ├── LargeTile.scale-125.png │ │ │ │ ├── LargeTile.scale-150.png │ │ │ │ ├── LargeTile.scale-200.png │ │ │ │ ├── LargeTile.scale-400.png │ │ │ │ ├── SmallTile.scale-100.png │ │ │ │ ├── SmallTile.scale-125.png │ │ │ │ ├── SmallTile.scale-150.png │ │ │ │ ├── SmallTile.scale-200.png │ │ │ │ ├── SmallTile.scale-400.png │ │ │ │ ├── SplashScreen.scale-100.png │ │ │ │ ├── SplashScreen.scale-125.png │ │ │ │ ├── SplashScreen.scale-150.png │ │ │ │ ├── SplashScreen.scale-200.png │ │ │ │ ├── SplashScreen.scale-400.png │ │ │ │ ├── Square150x150Logo.scale-100.png │ │ │ │ ├── Square150x150Logo.scale-125.png │ │ │ │ ├── Square150x150Logo.scale-150.png │ │ │ │ ├── Square150x150Logo.scale-200.png │ │ │ │ ├── Square150x150Logo.scale-400.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-16.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-20.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-24.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-256.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-30.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-32.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-36.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-40.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-44.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-48.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-60.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-64.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-72.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-80.png │ │ │ │ ├── Square44x44Logo.altform-lightunplated_targetsize-96.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-16.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-20.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-24.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-256.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-30.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-32.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-36.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-40.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-44.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-48.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-60.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-64.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-72.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-80.png │ │ │ │ ├── Square44x44Logo.altform-unplated_targetsize-96.png │ │ │ │ ├── Square44x44Logo.scale-100.png │ │ │ │ ├── Square44x44Logo.scale-125.png │ │ │ │ ├── Square44x44Logo.scale-150.png │ │ │ │ ├── Square44x44Logo.scale-200.png │ │ │ │ ├── Square44x44Logo.scale-400.png │ │ │ │ ├── Square44x44Logo.targetsize-16.png │ │ │ │ ├── Square44x44Logo.targetsize-20.png │ │ │ │ ├── Square44x44Logo.targetsize-24.png │ │ │ │ ├── Square44x44Logo.targetsize-256.png │ │ │ │ ├── Square44x44Logo.targetsize-30.png │ │ │ │ ├── Square44x44Logo.targetsize-32.png │ │ │ │ ├── Square44x44Logo.targetsize-36.png │ │ │ │ ├── Square44x44Logo.targetsize-40.png │ │ │ │ ├── Square44x44Logo.targetsize-44.png │ │ │ │ ├── Square44x44Logo.targetsize-48.png │ │ │ │ ├── Square44x44Logo.targetsize-60.png │ │ │ │ ├── Square44x44Logo.targetsize-64.png │ │ │ │ ├── Square44x44Logo.targetsize-72.png │ │ │ │ ├── Square44x44Logo.targetsize-80.png │ │ │ │ ├── Square44x44Logo.targetsize-96.png │ │ │ │ ├── StoreLogo.scale-100.png │ │ │ │ ├── StoreLogo.scale-125.png │ │ │ │ ├── StoreLogo.scale-150.png │ │ │ │ ├── StoreLogo.scale-200.png │ │ │ │ ├── StoreLogo.scale-400.png │ │ │ │ ├── Wide310x150Logo.scale-100.png │ │ │ │ ├── Wide310x150Logo.scale-125.png │ │ │ │ ├── Wide310x150Logo.scale-150.png │ │ │ │ ├── Wide310x150Logo.scale-200.png │ │ │ │ └── Wide310x150Logo.scale-400.png │ │ ├── logo │ │ │ ├── logo_circle.svg │ │ │ └── logo_square.svg │ │ └── sql-wasm.wasm │ ├── index.html │ ├── manifest.json │ └── robots.txt ├── src │ ├── app │ │ ├── components │ │ │ ├── admin-layout │ │ │ │ ├── admin-layout-content.component.tsx │ │ │ │ ├── admin-layout-sidebar-edit-menu-button.component.tsx │ │ │ │ ├── admin-layout-sidebar-menu-item-icon.component.tsx │ │ │ │ ├── admin-layout-sidebar-menu-item.component.tsx │ │ │ │ ├── admin-layout-sidebar-menu.component.tsx │ │ │ │ ├── admin-layout-sidebar-project-picker.component.tsx │ │ │ │ ├── admin-layout-sidebar.component.tsx │ │ │ │ ├── admin-layout.component.tsx │ │ │ │ ├── assistant │ │ │ │ │ ├── assistant-provider.tsx │ │ │ │ │ ├── assistant-toolbar-enter-api-key.tsx │ │ │ │ │ ├── assistant-toolbar-enter-prompt.tsx │ │ │ │ │ ├── assistant-toolbar-open-button.tsx │ │ │ │ │ ├── assistant-toolbar-responses-list-item.tsx │ │ │ │ │ ├── assistant-toolbar-responses-list.tsx │ │ │ │ │ └── assistant-toolbar.tsx │ │ │ │ ├── components │ │ │ │ │ └── sidebar-menu.component.tsx │ │ │ │ └── header │ │ │ │ │ ├── cloud-sync-button-connect.tsx │ │ │ │ │ ├── cloud-sync-button-do-sync.tsx │ │ │ │ │ ├── cloud-sync-button-open-file-picker.tsx │ │ │ │ │ ├── cloud-sync-button.tsx │ │ │ │ │ ├── cloud-sync-file-picker.tsx │ │ │ │ │ ├── header.component.tsx │ │ │ │ │ └── local-fs-info.tsx │ │ │ ├── auth │ │ │ │ └── o-auth.component.tsx │ │ │ ├── editor-mode.enum.ts │ │ │ ├── hooks │ │ │ │ ├── click-outside.hook.ts │ │ │ │ ├── id-last-changed-by-sync.ts │ │ │ │ ├── multi-state.hook.ts │ │ │ │ ├── shortcut.ts │ │ │ │ └── tippy-tooltip.ts │ │ │ ├── project │ │ │ │ ├── details-card.tsx │ │ │ │ ├── details.component.tsx │ │ │ │ ├── project-section-elements-list.component.tsx │ │ │ │ └── section │ │ │ │ │ ├── element │ │ │ │ │ ├── add-edit-form-manager.component.tsx │ │ │ │ │ ├── add-edit-save-cancel-buttons.component.tsx │ │ │ │ │ ├── add-edit.component.tsx │ │ │ │ │ ├── delete-button.component.tsx │ │ │ │ │ ├── details-parents-links.component.tsx │ │ │ │ │ └── details.component.tsx │ │ │ │ │ ├── list │ │ │ │ │ ├── grid │ │ │ │ │ │ ├── grid-element.tsx │ │ │ │ │ │ └── grid.tsx │ │ │ │ │ ├── list.component.tsx │ │ │ │ │ ├── table │ │ │ │ │ │ ├── table-body-tr-td-with-link-to-details.component.tsx │ │ │ │ │ │ ├── table-body-tr-td-without-link.component.tsx │ │ │ │ │ │ ├── table-body-tr-td.component.tsx │ │ │ │ │ │ ├── table-body-tr.component.tsx │ │ │ │ │ │ ├── table-container.component.tsx │ │ │ │ │ │ ├── table-head-th.component.tsx │ │ │ │ │ │ └── table.component.tsx │ │ │ │ │ └── tabs │ │ │ │ │ │ ├── header-right-add-field.component.tsx │ │ │ │ │ │ ├── header-right-edit-view-table.component.tsx │ │ │ │ │ │ ├── header-right-edit-view.component.tsx │ │ │ │ │ │ ├── header-right-view-selector.component.tsx │ │ │ │ │ │ ├── header-right.component.tsx │ │ │ │ │ │ └── list-tabs.component.tsx │ │ │ │ │ └── no-data.component.tsx │ │ │ ├── projects │ │ │ │ ├── add-edit-project-components │ │ │ │ │ ├── form-project-manager.component.tsx │ │ │ │ │ ├── project-editor-mode-toggle.component.tsx │ │ │ │ │ ├── section-form-model-manager.component.tsx │ │ │ │ │ └── section-manager.component.tsx │ │ │ │ ├── add-edit-project.component.tsx │ │ │ │ ├── no-projects.component.tsx │ │ │ │ ├── project-card-components │ │ │ │ │ ├── project-loaded-footer.component.tsx │ │ │ │ │ └── project-not-loaded-footer.component.tsx │ │ │ │ ├── project-card.component.tsx │ │ │ │ ├── project-importer-components │ │ │ │ │ ├── import-project-button.component.tsx │ │ │ │ │ └── import-project-modal-content.component.tsx │ │ │ │ └── projects-list.component.tsx │ │ │ └── shared-components │ │ │ │ ├── alerts │ │ │ │ └── alert-with-description.tsx │ │ │ │ ├── buttons │ │ │ │ ├── add-edit-element-button.component.tsx │ │ │ │ ├── delete-project.component.tsx │ │ │ │ ├── edit-project-button.component.tsx │ │ │ │ └── export-project-button.component.tsx │ │ │ │ ├── common-ui-eles │ │ │ │ ├── button.component.tsx │ │ │ │ ├── card-footer.component.tsx │ │ │ │ ├── checkbox.component.tsx │ │ │ │ ├── components.const.ts │ │ │ │ ├── dropdown.component.tsx │ │ │ │ ├── main-content-container.component.tsx │ │ │ │ └── toggle.component.tsx │ │ │ │ ├── file-explorer │ │ │ │ └── file-explorer.tsx │ │ │ │ ├── forms-automator │ │ │ │ ├── form-automator.component.tsx │ │ │ │ ├── form-automator.types.ts │ │ │ │ ├── form-builder │ │ │ │ │ ├── calc-width.function.ts │ │ │ │ │ ├── form-eles-switcher.function.tsx │ │ │ │ │ └── prerequisites-checker.class.ts │ │ │ │ ├── form-component-codes.enum.ts │ │ │ │ ├── form-fields │ │ │ │ │ ├── basic-checkbox.component.tsx │ │ │ │ │ ├── basic-input.component.css │ │ │ │ │ ├── basic-input.component.tsx │ │ │ │ │ ├── basic-radio-info-icon.component.tsx │ │ │ │ │ ├── basic-radio.component.tsx │ │ │ │ │ ├── basic-select.component.tsx │ │ │ │ │ ├── basic-textarea.component copy.tsx │ │ │ │ │ ├── child-of-selector-for-section.component.tsx │ │ │ │ │ ├── hidden-input.component.tsx │ │ │ │ │ ├── options-maker-single-option.component.tsx │ │ │ │ │ ├── options-maker.component.tsx │ │ │ │ │ ├── parents-selector.component.tsx │ │ │ │ │ └── rich-text.component.tsx │ │ │ │ ├── form-layout │ │ │ │ │ ├── field-selector.component.tsx │ │ │ │ │ ├── fom-elements-css-classes.const.ts │ │ │ │ │ ├── form-ele-container.component.tsx │ │ │ │ │ └── form-element-label.component.tsx │ │ │ │ ├── form-validation │ │ │ │ │ ├── supported-validators.enum.ts │ │ │ │ │ ├── validators-container.component.tsx │ │ │ │ │ ├── validators.ts │ │ │ │ │ └── validators │ │ │ │ │ │ ├── email-format.component.tsx │ │ │ │ │ │ ├── required-field.component.tsx │ │ │ │ │ │ ├── telephone-number.component.tsx │ │ │ │ │ │ └── url-format.component.tsx │ │ │ │ ├── hooks │ │ │ │ │ ├── use-set-default-value.ts │ │ │ │ │ └── use-validators.hook.ts │ │ │ │ └── input-supported-types.const.ts │ │ │ │ ├── loader │ │ │ │ └── loader.component.tsx │ │ │ │ ├── modals │ │ │ │ ├── modal-context.ts │ │ │ │ ├── modal-footer.component.tsx │ │ │ │ ├── modal-header.component.tsx │ │ │ │ └── modal.component.tsx │ │ │ │ ├── rich-text-editor │ │ │ │ ├── rich-text-editor-element.tsx │ │ │ │ ├── rich-text-editor-helpers.class.ts │ │ │ │ ├── rich-text-editor-leaf.tsx │ │ │ │ ├── rich-text-editor.css │ │ │ │ └── rich-text-editor.tsx │ │ │ │ └── values-renderers │ │ │ │ ├── basic-text.component.tsx │ │ │ │ ├── checkbox-editable.component.tsx │ │ │ │ ├── color.component.tsx │ │ │ │ ├── custom-render-picker.component.tsx │ │ │ │ ├── email.component.tsx │ │ │ │ ├── formatted-date-time.component.tsx │ │ │ │ ├── formatted-date.component.tsx │ │ │ │ ├── month.component.tsx │ │ │ │ ├── password.component.tsx │ │ │ │ ├── range.component.tsx │ │ │ │ ├── rich-text.component.tsx │ │ │ │ ├── tel.component.tsx │ │ │ │ ├── text-from-options-by-value.component.tsx │ │ │ │ ├── url.component.tsx │ │ │ │ └── week.component.tsx │ │ ├── const │ │ │ ├── keyboard.const.ts │ │ │ └── platform.const.ts │ │ ├── data │ │ │ ├── local-dbs │ │ │ │ ├── db-initializer.class.ts │ │ │ │ ├── db-instances.const.ts │ │ │ │ └── local-storage-systems.enum.ts │ │ │ ├── project-form-builder │ │ │ │ ├── options-builder.constant.ts │ │ │ │ ├── project-editor-form-builder.const.ts │ │ │ │ ├── project-info-builder.constant.ts │ │ │ │ ├── section-fields-builder.constant.ts │ │ │ │ └── section-info-builder.constant.ts │ │ │ └── system-local-db │ │ │ │ ├── auds-system-sections.enum.ts │ │ │ │ ├── client-sections.enum.ts │ │ │ │ ├── data-structure-extender.class.ts │ │ │ │ └── sections │ │ │ │ ├── auds-sections.const.ts │ │ │ │ ├── auds-settings.const.ts │ │ │ │ ├── local-settings.const.ts │ │ │ │ └── projects.const.ts │ │ ├── libs │ │ │ ├── assistant │ │ │ │ └── open-ai-helper.ts │ │ │ ├── cloud-sync │ │ │ │ ├── cloud-sync-base.class.ts │ │ │ │ ├── cloud-sync.const.ts │ │ │ │ ├── dropbox │ │ │ │ │ └── dropbox-helper.class.ts │ │ │ │ ├── o-auth-utils.class.ts │ │ │ │ ├── remote-and-local-merger.class.ts │ │ │ │ ├── sync-manager.class.ts │ │ │ │ └── sync-manager.const.ts │ │ │ ├── db-connector │ │ │ │ ├── common-helpers │ │ │ │ │ ├── query-maker.class.ts │ │ │ │ │ └── where-builder.class.ts │ │ │ │ ├── crypter │ │ │ │ │ ├── crypt-helper.class.ts │ │ │ │ │ ├── decrypter.class.ts │ │ │ │ │ ├── encrypter.class.ts │ │ │ │ │ ├── options.constant.ts │ │ │ │ │ ├── string-crypter.function.ts │ │ │ │ │ └── string-decrypter.function.ts │ │ │ │ ├── db-builder │ │ │ │ │ ├── data-structure-builder.class.ts │ │ │ │ │ ├── default-values.constant.ts │ │ │ │ │ ├── sez-builder.class.ts │ │ │ │ │ └── sez-definition.ts │ │ │ │ ├── db-connector.class.ts │ │ │ │ ├── models │ │ │ │ │ ├── abstract-model.d.ts │ │ │ │ │ └── executers.d.ts │ │ │ │ └── plugins │ │ │ │ │ ├── deta │ │ │ │ │ ├── db-deletor │ │ │ │ │ │ └── db-deletor.class.ts │ │ │ │ │ ├── db-insertor │ │ │ │ │ │ └── db-insertor.class.ts │ │ │ │ │ ├── db-selector │ │ │ │ │ │ └── db-selector.class.ts │ │ │ │ │ ├── db-store │ │ │ │ │ │ └── dbstore.class.ts │ │ │ │ │ ├── db-updator │ │ │ │ │ │ └── db-updator.class.ts │ │ │ │ │ └── exporter.constant.ts │ │ │ │ │ ├── file-handles │ │ │ │ │ ├── db-deletor │ │ │ │ │ │ └── db-deletor.class.ts │ │ │ │ │ ├── db-insertor │ │ │ │ │ │ └── db-insertor.class.ts │ │ │ │ │ ├── db-selector │ │ │ │ │ │ └── db-selector.class.ts │ │ │ │ │ ├── db-store │ │ │ │ │ │ └── dbstore.class.ts │ │ │ │ │ ├── db-updator │ │ │ │ │ │ └── db-updator.class.ts │ │ │ │ │ ├── exporter.constant.ts │ │ │ │ │ └── helpers │ │ │ │ │ │ ├── element-adder-to-collection.class.ts │ │ │ │ │ │ ├── file-handle-checker.function.ts │ │ │ │ │ │ ├── file-system-access-api.d.ts │ │ │ │ │ │ ├── fs-helper.ts │ │ │ │ │ │ └── project-file-handle-saver.class.ts │ │ │ │ │ ├── indexed-db │ │ │ │ │ ├── db-deletor │ │ │ │ │ │ └── db-deletor.class.ts │ │ │ │ │ ├── db-insertor │ │ │ │ │ │ └── db-insertor.class.ts │ │ │ │ │ ├── db-selector │ │ │ │ │ │ └── db-selector.class.ts │ │ │ │ │ ├── db-store │ │ │ │ │ │ └── dbstore.class.ts │ │ │ │ │ ├── db-updator │ │ │ │ │ │ └── db-updator.class.ts │ │ │ │ │ ├── exporter.constant.ts │ │ │ │ │ └── query-makers │ │ │ │ │ │ ├── query-helper.class.ts │ │ │ │ │ │ └── query-maker.class.ts │ │ │ │ │ └── sqlite │ │ │ │ │ ├── db-deletor │ │ │ │ │ └── db-deletor.class.ts │ │ │ │ │ ├── db-insertor │ │ │ │ │ └── db-insertor.class.ts │ │ │ │ │ ├── db-selector │ │ │ │ │ └── db-selector.class.ts │ │ │ │ │ ├── db-store │ │ │ │ │ └── dbstore.class.ts │ │ │ │ │ ├── db-updator │ │ │ │ │ └── db-updator.class.ts │ │ │ │ │ ├── exporter.constant.ts │ │ │ │ │ └── helpers │ │ │ │ │ ├── execute-query-no-return.function.ts │ │ │ │ │ ├── execute-query-with-return.function.ts │ │ │ │ │ ├── schema-creator.class.tsx │ │ │ │ │ ├── schema-exporter.function.ts │ │ │ │ │ └── serializer.function.ts │ │ │ ├── icons │ │ │ │ └── icons.class.tsx │ │ │ ├── id-creator │ │ │ │ └── id-creator.class.ts │ │ │ ├── logger │ │ │ │ └── logger.class.ts │ │ │ ├── manager │ │ │ │ └── manager.class.ts │ │ │ ├── projects-helpers │ │ │ │ ├── project-importers │ │ │ │ │ ├── project-data-importer.class.ts │ │ │ │ │ └── project-file-importer.class.ts │ │ │ │ ├── projects-handlers │ │ │ │ │ └── projects-list-loader.class.ts │ │ │ │ └── sections-forms-helpers │ │ │ │ │ └── section-form-ele-generator.class.ts │ │ │ ├── redux │ │ │ │ ├── action.type.ts │ │ │ │ ├── reducers.const.ts │ │ │ │ ├── reducers │ │ │ │ │ ├── form-element.reducer.ts │ │ │ │ │ ├── form-eles-valid-state.reducer.ts │ │ │ │ │ ├── form-project.reducer.ts │ │ │ │ │ ├── layout.reducer.ts │ │ │ │ │ ├── project.reducer.ts │ │ │ │ │ ├── projects.reducer.ts │ │ │ │ │ └── sections-for-child-of-selector.reducer.ts │ │ │ │ ├── redux-actions.const.ts │ │ │ │ ├── state.store.ts │ │ │ │ └── store-dispatcher.function.ts │ │ │ ├── routing │ │ │ │ ├── anita-routes.component.tsx │ │ │ │ ├── anita-routes.constant.ts │ │ │ │ └── url-param-fillers.function.ts │ │ │ ├── shortcuts │ │ │ │ ├── shortcuts-listener.ts │ │ │ │ ├── shortcuts-manager.ts │ │ │ │ └── shortcuts-store.ts │ │ │ ├── startupper │ │ │ │ └── startupper.class.ts │ │ │ └── tools │ │ │ │ ├── array-tools.class.ts │ │ │ │ ├── date-tools.class.ts │ │ │ │ ├── keyboard.class.ts │ │ │ │ └── text-tools.class.ts │ │ ├── models │ │ │ ├── parent-element │ │ │ │ ├── parent-element.class.ts │ │ │ │ └── parent-element.declarations.ts │ │ │ ├── project │ │ │ │ ├── get-options-for-parents-selector.class.ts │ │ │ │ ├── get-parent-info-for-details-view.class.ts │ │ │ │ ├── project-deletor.class.ts │ │ │ │ ├── project-exporter.class.ts │ │ │ │ ├── project-loader.class.ts │ │ │ │ ├── project-saver.class.ts │ │ │ │ ├── project.class.ts │ │ │ │ ├── project.declarations.ts │ │ │ │ ├── save-project-settings-in-indexed-db.class.ts │ │ │ │ └── syncing │ │ │ │ │ ├── project-comparator.ts │ │ │ │ │ ├── project-uploader.ts │ │ │ │ │ └── sync-info.class.ts │ │ │ ├── reserved-fields.constant.ts │ │ │ ├── section-element │ │ │ │ ├── section-element.class.ts │ │ │ │ └── section-element.declarations.ts │ │ │ └── section │ │ │ │ ├── parent-info-form-ele-builder.class.ts │ │ │ │ ├── section-element-saver.class.ts │ │ │ │ ├── section.class.ts │ │ │ │ ├── section.declarations.ts │ │ │ │ ├── view-settings.class.ts │ │ │ │ └── view-settings.const.ts │ │ └── workers │ │ │ └── comparator.worker.ts │ ├── index.css │ ├── index.tsx │ ├── react-app-env.d.ts │ ├── reportWebVitals.ts │ ├── service-worker.ts │ ├── serviceWorkerRegistration.ts │ ├── setupTests.ts │ └── types │ │ ├── date-format.d.ts │ │ └── index.d.ts ├── tailwind.config.js └── tsconfig.json ├── assets ├── AnitaLogo.sketch ├── DataSync.sketch └── LandingImages.sketch ├── changelog.json ├── landing ├── .vscode │ └── ltex.disabledRules.en-US.txt ├── config │ └── default.ts ├── package-lock.json ├── package.json ├── src │ ├── app │ │ ├── blog │ │ │ └── articles │ │ │ │ ├── 2021-11-03 hosting-pwa-for-free.md │ │ │ │ ├── 2021-11-10 porting-from-angular-to-react.md │ │ │ │ ├── 2021-12-01 sqlite-in-a-pwa.md │ │ │ │ ├── 2022-02-01 switch-with-types.md │ │ │ │ ├── 2022-09-07 most-secure-cloud.md │ │ │ │ ├── 2022-09-27 data-portability-is-a-hard-problem.md │ │ │ │ └── 2022-12-18 a-simpler-way-to-use-tippyjs-with-react.md │ │ ├── components │ │ │ ├── footer.component.html │ │ │ ├── get-started.component.html │ │ │ ├── head │ │ │ │ └── head.component.html │ │ │ ├── instructions │ │ │ │ ├── builds.md │ │ │ │ ├── installation.md │ │ │ │ └── new-project.md │ │ │ ├── landing │ │ │ │ ├── features.component.html │ │ │ │ ├── live-demo.html │ │ │ │ ├── overlapping-cards.html │ │ │ │ └── read-the-docs.html │ │ │ └── navbar │ │ │ │ └── navbar.component.html │ │ ├── pages-to-generate │ │ │ └── blog │ │ │ │ └── blog.template.html │ │ ├── pages │ │ │ ├── blog │ │ │ │ └── index.html │ │ │ ├── index.html │ │ │ └── licenses.html │ │ ├── scripts │ │ │ ├── main.ts │ │ │ ├── responsive-table │ │ │ │ ├── multiply-heads-as-body-rows.function.ts │ │ │ │ └── set-heights.ts │ │ │ └── typewriter.class.ts │ │ ├── styles │ │ │ └── styles.css │ │ └── yassb-plugins │ │ │ ├── custom-directives │ │ │ ├── date-formatter-directive.class.ts │ │ │ └── months.const.ts │ │ │ ├── custom-renderers │ │ │ ├── blog-list.tsx │ │ │ ├── packages-list.tsx │ │ │ └── sort-blogs-by-date.ts │ │ │ ├── post-processors │ │ │ └── add-div-to-pre-code.function.ts │ │ │ └── styles-parser │ │ │ └── build-tailwind.function.ts │ └── assets │ │ ├── fontawesome-free │ │ ├── css │ │ │ └── all.min.css │ │ └── webfonts │ │ │ ├── fa-brands-400.eot │ │ │ ├── fa-brands-400.svg │ │ │ ├── fa-brands-400.ttf │ │ │ ├── fa-brands-400.woff │ │ │ ├── fa-brands-400.woff2 │ │ │ ├── fa-regular-400.eot │ │ │ ├── fa-regular-400.svg │ │ │ ├── fa-regular-400.ttf │ │ │ ├── fa-regular-400.woff │ │ │ ├── fa-regular-400.woff2 │ │ │ ├── fa-solid-900.eot │ │ │ ├── fa-solid-900.svg │ │ │ ├── fa-solid-900.ttf │ │ │ ├── fa-solid-900.woff │ │ │ └── fa-solid-900.woff2 │ │ ├── icons │ │ └── favicon │ │ │ ├── android-chrome-192x192.png │ │ │ ├── android-chrome-512x512.png │ │ │ ├── apple-touch-icon.png │ │ │ ├── browserconfig.xml │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon.ico │ │ │ ├── mstile-150x150.png │ │ │ ├── safari-pinned-tab.svg │ │ │ └── site.webmanifest │ │ ├── images │ │ ├── abstract-table.png │ │ ├── blog │ │ │ ├── a-simpler-way-to-use-tippyjs-with-react │ │ │ │ └── a-simpler-way-to-use-tippyjs-with-react.jpg │ │ │ ├── data-portability-is-a-hard-problem │ │ │ │ └── data-portability-is-a-hard-problem.jpg │ │ │ ├── hosting-pwa-for-free │ │ │ │ └── hosting-pwa-for-free.jpg │ │ │ ├── most-secure-cloud │ │ │ │ ├── data_centre.jpg │ │ │ │ └── most-secure-cloud.jpg │ │ │ ├── porting │ │ │ │ ├── 2021-11-10_app_initialized.jpg │ │ │ │ ├── 2021-11-10_tailwind.jpg │ │ │ │ ├── 2021-11-21_final_result.jpg │ │ │ │ ├── 2021-11-21_final_result_with_project.jpg │ │ │ │ ├── Anita_angular_no_projects.jpg │ │ │ │ ├── Anita_angular_projects_list.jpg │ │ │ │ └── porting-anita-from-angular-to-react.jpg │ │ │ ├── sqlite-in-a-pwa │ │ │ │ └── database-free.jpg │ │ │ └── switch-with-types │ │ │ │ ├── 2020-02-01_clearElement-example.jpg │ │ │ │ ├── 2020-02-01_meme-code-working.jpg │ │ │ │ ├── 2020-02-01_setElement-example.jpg │ │ │ │ ├── 2020-02-01_updateElement-example.jpg │ │ │ │ └── switches.jpg │ │ └── svg │ │ │ └── data-sync.svg │ │ └── logo │ │ ├── logo_circle.svg │ │ └── logo_square.svg ├── tailwind.config.js └── tsconfig.json ├── package-lock.json ├── package.json └── version-propagator.js /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: ildon -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /tmp 5 | /anita-app.github.io 6 | 7 | # dependencies 8 | node_modules 9 | 10 | # IDEs and editors 11 | /.idea 12 | .project 13 | .classpath 14 | .c9/ 15 | *.launch 16 | .settings/ 17 | *.sublime-workspace 18 | 19 | # IDE - VSCode 20 | .vscode/* 21 | !.vscode/settings.json 22 | !.vscode/tasks.json 23 | !.vscode/launch.json 24 | !.vscode/extensions.json 25 | 26 | # misc 27 | /.sass-cache 28 | /connect.lock 29 | /coverage 30 | /libpeerconnection.log 31 | npm-debug.log 32 | yarn-error.log 33 | testem.log 34 | /typings 35 | 36 | # System Files 37 | .DS_Store 38 | Thumbs.db 39 | yarn.lock 40 | landing/src/app/data-sources/landing-packages.json 41 | landing/src/app/data-sources/app-packages.json 42 | landing/src/app/version.ts 43 | app/src/app/version.ts 44 | app/package-lock.json 45 | -------------------------------------------------------------------------------- /app/.env.development: -------------------------------------------------------------------------------- 1 | BROWSER=none -------------------------------------------------------------------------------- /app/.env.production: -------------------------------------------------------------------------------- 1 | PUBLIC_URL=/app 2 | BUILD_PATH='../anita-app.github.io/app' -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | anita-app.github.io 25 | -------------------------------------------------------------------------------- /app/config-overrides.js: -------------------------------------------------------------------------------- 1 | const NodePolyfillPlugin = require('node-polyfill-webpack-plugin') 2 | const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin') 3 | 4 | module.exports = function override (config) { 5 | config.plugins.push( 6 | new NodePolyfillPlugin() 7 | ) 8 | config.externals = { fs: 'fs' } 9 | config.resolve.plugins = config.resolve.plugins.filter(plugin => !(plugin instanceof ModuleScopePlugin)) 10 | return config 11 | } 12 | -------------------------------------------------------------------------------- /app/craco.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | style: { 3 | postcss: { 4 | plugins: [ 5 | require('tailwindcss'), 6 | require('autoprefixer'), 7 | require('@tailwindcss/forms') 8 | ], 9 | }, 10 | }, 11 | } 12 | -------------------------------------------------------------------------------- /app/public/assets/icons/android/android-launchericon-144-144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/android-launchericon-144-144.png -------------------------------------------------------------------------------- /app/public/assets/icons/android/android-launchericon-192-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/android-launchericon-192-192.png -------------------------------------------------------------------------------- /app/public/assets/icons/android/android-launchericon-48-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/android-launchericon-48-48.png -------------------------------------------------------------------------------- /app/public/assets/icons/android/android-launchericon-512-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/android-launchericon-512-512.png -------------------------------------------------------------------------------- /app/public/assets/icons/android/android-launchericon-72-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/android-launchericon-72-72.png -------------------------------------------------------------------------------- /app/public/assets/icons/android/android-launchericon-96-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/android-launchericon-96-96.png -------------------------------------------------------------------------------- /app/public/assets/icons/android/maskable_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/android/maskable_icon.png -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #002346 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/favicon.ico -------------------------------------------------------------------------------- /app/public/assets/icons/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/100.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/1024.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/114.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/120.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/128.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/144.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/152.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/16.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/167.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/180.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/192.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/20.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/256.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/29.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/32.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/40.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/50.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/512.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/57.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/58.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/60.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/64.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/72.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/76.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/80.png -------------------------------------------------------------------------------- /app/public/assets/icons/ios/87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/ios/87.png -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_align_center.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_align_justify.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_align_left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_align_right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_bold.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_code.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_italic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_list_bulleted.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_list_numbered.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_looks_one.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_looks_two.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_quote.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/format_underlined.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/svg/table.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/LargeTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/LargeTile.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/LargeTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/LargeTile.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/LargeTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/LargeTile.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/LargeTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/LargeTile.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/LargeTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/LargeTile.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SmallTile.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SmallTile.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SmallTile.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SmallTile.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SmallTile.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SmallTile.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SmallTile.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SmallTile.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SmallTile.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SmallTile.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SplashScreen.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SplashScreen.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SplashScreen.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SplashScreen.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SplashScreen.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SplashScreen.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SplashScreen.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SplashScreen.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/SplashScreen.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/SplashScreen.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square150x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square150x150Logo.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square150x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square150x150Logo.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square150x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square150x150Logo.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square150x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square150x150Logo.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square150x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square150x150Logo.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-16.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-20.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-24.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-256.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-30.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-32.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-36.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-40.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-44.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-48.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-60.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-64.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-72.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-80.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-lightunplated_targetsize-96.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-16.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-20.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-24.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-256.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-30.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-32.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-36.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-40.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-44.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-48.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-60.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-64.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-72.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-80.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.altform-unplated_targetsize-96.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-16.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-20.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-24.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-256.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-30.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-30.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-32.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-36.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-40.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-44.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-44.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-48.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-60.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-64.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-72.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-80.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Square44x44Logo.targetsize-96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Square44x44Logo.targetsize-96.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/StoreLogo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/StoreLogo.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/StoreLogo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/StoreLogo.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/StoreLogo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/StoreLogo.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/StoreLogo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/StoreLogo.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/StoreLogo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/StoreLogo.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Wide310x150Logo.scale-100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Wide310x150Logo.scale-100.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Wide310x150Logo.scale-125.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Wide310x150Logo.scale-125.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Wide310x150Logo.scale-150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Wide310x150Logo.scale-150.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Wide310x150Logo.scale-200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Wide310x150Logo.scale-200.png -------------------------------------------------------------------------------- /app/public/assets/icons/windows11/Wide310x150Logo.scale-400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/icons/windows11/Wide310x150Logo.scale-400.png -------------------------------------------------------------------------------- /app/public/assets/logo/logo_square.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/public/assets/sql-wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/app/public/assets/sql-wasm.wasm -------------------------------------------------------------------------------- /app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /app/src/app/components/admin-layout/admin-layout-content.component.tsx: -------------------------------------------------------------------------------- 1 | import { AssistantToolbar } from 'app/components/admin-layout/assistant/assistant-toolbar' 2 | import React, { ReactNode } from 'react' 3 | 4 | interface IContentProps { 5 | children: ReactNode 6 | } 7 | 8 | export const AdminLayoutContent: React.FC = (props) => ( 9 |
10 | 11 | {props.children} 12 |
13 | ) 14 | -------------------------------------------------------------------------------- /app/src/app/components/admin-layout/admin-layout-sidebar-edit-menu-button.component.tsx: -------------------------------------------------------------------------------- 1 | import { useTippyTooltip } from 'app/components/hooks/tippy-tooltip' 2 | import { Icons } from 'app/libs/icons/icons.class' 3 | import React from 'react' 4 | 5 | interface IAdminLayoutSidebarEditMenuButtonProps { 6 | isEditingMenuItemsVisibility: boolean 7 | onClick: () => void 8 | } 9 | 10 | export const AdminLayoutSidebarEditMenuButton: React.FC = (props) => { 11 | useTippyTooltip('editMenu', 'Edit menu') 12 | 13 | return ( 14 | 17 | ) 18 | } 19 | -------------------------------------------------------------------------------- /app/src/app/components/admin-layout/admin-layout-sidebar-menu-item-icon.component.tsx: -------------------------------------------------------------------------------- 1 | import React, { memo } from 'react' 2 | import { Icons, TIconName } from 'app/libs/icons/icons.class' 3 | import { useTippyTooltip } from 'app/components/hooks/tippy-tooltip' 4 | 5 | interface IAdminLayoutSidebarMenuItemIconProps { 6 | elementId: string 7 | tooltip: string 8 | icon: TIconName 9 | onClick: (e: React.MouseEvent) => void 10 | } 11 | 12 | export const AdminLayoutSidebarMenuItemIcon: React.FC = memo(function AdminLayoutSidebarMenuItem (props: IAdminLayoutSidebarMenuItemIconProps) { 13 | useTippyTooltip(props.elementId, props.tooltip) 14 | 15 | return ( 16 | 19 | ) 20 | }) 21 | -------------------------------------------------------------------------------- /app/src/app/components/admin-layout/admin-layout-sidebar-menu.component.tsx: -------------------------------------------------------------------------------- 1 | import { AnitaStore } from 'app/libs/redux/reducers.const' 2 | import { AdminLayoutSidebarProjectPicker } from 'app/components/admin-layout/admin-layout-sidebar-project-picker.component' 3 | import { useSelector } from 'react-redux' 4 | import React from 'react' 5 | import { Manager } from 'app/libs/manager/manager.class' 6 | import { AdminLayoutSidebarMenuItem } from 'app/components/admin-layout/admin-layout-sidebar-menu-item.component' 7 | 8 | interface IAdminLayoutSidebarMenuProps { 9 | isEditingMenuItemsVisibility: boolean 10 | } 11 | 12 | export const AdminLayoutSidebarMenu: React.FC = (props) => { 13 | const project = useSelector((state: AnitaStore) => state.project) 14 | const [currentSelectedSectionId, setCurrentSelectedSectionId] = React.useState(null) 15 | 16 | if (project === null) { 17 | return null 18 | } 19 | 20 | return ( 21 |
22 | 23 | {Manager.getCurrentProject()?.getSectionsDefinitions().map(section => ( 24 | 32 | ))} 33 | 34 |
35 | ) 36 | } 37 | -------------------------------------------------------------------------------- /app/src/app/components/admin-layout/admin-layout.component.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react' 2 | import { AnitaRoutes } from 'app/libs/routing/anita-routes.component' 3 | import { AdminLayoutContent } from 'app/components/admin-layout/admin-layout-content.component' 4 | import { AdminLayoutHeader } from 'app/components/admin-layout/header/header.component' 5 | import { AdminLayoutSidebar } from 'app/components/admin-layout/admin-layout-sidebar.component' 6 | import { useSelector } from 'react-redux' 7 | import { AnitaStore } from 'app/libs/redux/reducers.const' 8 | import { OAuthUtils } from 'app/libs/cloud-sync/o-auth-utils.class' 9 | import { AssistantProvider } from 'app/components/admin-layout/assistant/assistant-provider' 10 | 11 | export const AdminLayout: React.FC = () => { 12 | const project = useSelector((state: AnitaStore) => state.project) 13 | useEffect(() => { 14 | OAuthUtils.redirectToAuthPageIfUrlHasCode() 15 | }, []) 16 | 17 | return ( 18 | 19 | 20 |
21 | {!!project && } 22 | 23 | 24 | 25 |
26 |
27 | ) 28 | } 29 | -------------------------------------------------------------------------------- /app/src/app/components/admin-layout/assistant/assistant-toolbar-enter-api-key.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from 'app/components/shared-components/common-ui-eles/button.component' 2 | import { Type } from 'app/components/shared-components/common-ui-eles/components.const' 3 | import * as React from 'react' 4 | 5 | interface IAssistantToolbarEnterApiKeyProps { 6 | handleApiKeySubmit: (e: React.FormEvent, apiKey: string) => void 7 | } 8 | 9 | export const AssistantToolbarEnterApiKey: React.FC = (props) => { 10 | const [apiKey, setApiKey] = React.useState('') 11 | 12 | const handleApiKeyChange = (e: React.ChangeEvent) => { 13 | setApiKey(e.target.value) 14 | } 15 | return ( 16 |
props.handleApiKeySubmit(e, apiKey)}> 17 | 27 |
25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /app/src/app/components/shared-components/values-renderers/range.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface IPercentageProps { 4 | value: string 5 | } 6 | 7 | export const Percentage: React.FC = ({ value }) => value ? <>{value}% : null 8 | -------------------------------------------------------------------------------- /app/src/app/components/shared-components/values-renderers/rich-text.component.tsx: -------------------------------------------------------------------------------- 1 | import { RichTextEditorElement } from 'app/components/shared-components/rich-text-editor/rich-text-editor-element' 2 | import { RichTextEditorHelpers } from 'app/components/shared-components/rich-text-editor/rich-text-editor-helpers.class' 3 | import { RichTextEditorLeaf } from 'app/components/shared-components/rich-text-editor/rich-text-editor-leaf' 4 | import React, { useCallback, useMemo } from 'react' 5 | import { createEditor } from 'slate' 6 | import { withHistory } from 'slate-history' 7 | import { withReact, Slate, Editable } from 'slate-react' 8 | 9 | interface IRichTextProps { 10 | value: string 11 | } 12 | 13 | export const RichText: React.FC = ({ value }) => { 14 | const renderElement = useCallback((props: any) => , []) 15 | const renderLeaf = useCallback((props: any) => , []) 16 | const editor = useMemo(() => withHistory(withReact(createEditor())), []) 17 | return ( 18 |
19 | 20 | 27 | 28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /app/src/app/components/shared-components/values-renderers/tel.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface ITelProps { 4 | value: string 5 | } 6 | 7 | export const Tel: React.FC = ({ value }) => value 8 | ? {value} 9 | : null 10 | -------------------------------------------------------------------------------- /app/src/app/components/shared-components/values-renderers/text-from-options-by-value.component.tsx: -------------------------------------------------------------------------------- 1 | import { SectionElement } from 'app/models/section-element/section-element.class' 2 | import { IOptionKeysModel } from 'app/components/shared-components/forms-automator/form-automator.types' 3 | 4 | interface ITextFromOptionsByValueProps { 5 | value: string | number 6 | } 7 | 8 | export const TextFromOptionsByValue = (options: Array, { value }: ITextFromOptionsByValueProps) => SectionElement.txtByFieldValue(options, value) 9 | -------------------------------------------------------------------------------- /app/src/app/components/shared-components/values-renderers/url.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface IUrlProps { 4 | value: string 5 | } 6 | 7 | const removeProtocolFromUrl = (url: string) => url.replace(/^(?:https?:\/\/)?(?:www\.)?/i, '') 8 | 9 | export const Url: React.FC = ({ value }) => value 10 | ? {removeProtocolFromUrl(value)} 11 | : null 12 | -------------------------------------------------------------------------------- /app/src/app/components/shared-components/values-renderers/week.component.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | interface IWeekProps { 4 | value: string 5 | } 6 | 7 | export const Week: React.FC = ({ value }) => value 8 | ? <>{value.split('-W').reverse().join('-').replace(/0(\d+)/, '$1')} 9 | : null 10 | -------------------------------------------------------------------------------- /app/src/app/const/keyboard.const.ts: -------------------------------------------------------------------------------- 1 | import { PLATFORM } from 'app/const/platform.const' 2 | 3 | export const KEYBOARD: { MODIFIER_KEY: keyof KeyboardEvent } = { 4 | MODIFIER_KEY: PLATFORM.IS_MAC ? 'metaKey' : 'ctrlKey' 5 | } 6 | -------------------------------------------------------------------------------- /app/src/app/const/platform.const.ts: -------------------------------------------------------------------------------- 1 | function getPlatform () { 2 | // 2022 way of detecting. Note : this userAgentData feature is available only in secure contexts (HTTPS) 3 | if (typeof (navigator as any).userAgentData !== 'undefined' && (navigator as any).userAgentData != null) { 4 | return (navigator as any).userAgentData.platform 5 | } 6 | // Deprecated but still works for most of the browser 7 | if (typeof navigator.platform !== 'undefined') { 8 | if (typeof navigator.userAgent !== 'undefined' && /android/.test(navigator.userAgent.toLowerCase())) { 9 | // android device’s navigator.platform is often set as 'linux', so let’s use userAgent for them 10 | return 'android' 11 | } 12 | return navigator.platform 13 | } 14 | return 'unknown' 15 | } 16 | 17 | const platform = getPlatform().toLowerCase() 18 | 19 | const isOSX = /mac/.test(platform) 20 | const isIOS = ['iphone', 'ipad', 'ipod'].indexOf(platform) !== -1 21 | /** For detecting phones vs. tablets, see: 22 | * https://stackoverflow.com/questions/9533106/detect-phone-vs-tablet 23 | * https://developers.google.com/search/blog/2011/03/mo-better-to-also-detect-mobile-user 24 | * */ 25 | const isPhone = (/mobile/i.test(navigator.userAgent) && !/ipad|tablet/i.test(navigator.userAgent)) 26 | 27 | export const PLATFORM = { 28 | IS_MAC: isOSX, 29 | IS_IOS: isIOS, 30 | IS_APPLE: isOSX || isIOS, 31 | IS_PHONE: isPhone, 32 | IS_WIN: /win/.test(platform), 33 | IS_ANDROID: /android/.test(platform), 34 | IS_LINUX: /linux/.test(platform), 35 | IS_WEB: true 36 | } 37 | -------------------------------------------------------------------------------- /app/src/app/data/local-dbs/db-instances.const.ts: -------------------------------------------------------------------------------- 1 | import { TAnitaUniversalDataStorage } from 'app/models/project/project.declarations' 2 | import { DbConnectorInstance } from 'app/libs/db-connector/models/executers' 3 | import Base from 'deta/dist/types/base' 4 | import Dexie from 'dexie' 5 | import { Database } from 'sql.js' 6 | 7 | interface DbInstances { 8 | system: DbConnectorInstance 9 | [projectIdentifier: string]: DbConnectorInstance 10 | } 11 | 12 | export const dbInstances: DbInstances = { 13 | system: undefined as any 14 | } 15 | -------------------------------------------------------------------------------- /app/src/app/data/local-dbs/local-storage-systems.enum.ts: -------------------------------------------------------------------------------- 1 | export enum LOCAL_STORAGE_SYSTEMS { 2 | json = 1, 3 | IndexedDB = 2, 4 | SQLite = 3, 5 | } 6 | -------------------------------------------------------------------------------- /app/src/app/data/system-local-db/auds-system-sections.enum.ts: -------------------------------------------------------------------------------- 1 | import { audsSections } from 'app/data/system-local-db/sections/auds-sections.const' 2 | import { audsSettings } from 'app/data/system-local-db/sections/auds-settings.const' 3 | 4 | /** 5 | * Identifies the table on which run the query with `db-connector`. 6 | */ 7 | export enum AUDS_SYSTEM_SECTIONS { 8 | auds_settings = 1, 9 | auds_sections 10 | } 11 | 12 | /** 13 | * Array to be passed to `db-connector` to initialize the DB with Dexie, via the IndexedDb plugin. 14 | */ 15 | export const AUDS_SYSTEM_SECTIONS_DEFINITIONS = [audsSettings, audsSections] 16 | -------------------------------------------------------------------------------- /app/src/app/data/system-local-db/client-sections.enum.ts: -------------------------------------------------------------------------------- 1 | import { localSettings } from 'app/data/system-local-db/sections/local-settings.const' 2 | import { projects } from 'app/data/system-local-db/sections/projects.const' 3 | 4 | /** 5 | * Identifies the table on which run the query with `db-connector`. 6 | */ 7 | export const CLIENT_SECTIONS = { 8 | projects: projects.name, 9 | localSettings: localSettings.name 10 | } 11 | 12 | /** 13 | * Optional on first version, required for changes to the structure of the DB 14 | * Must contain on Array for each previous version 15 | * Each array must contain the unaltered SezDefinitions of that version 16 | * 17 | * TODO with Dexie 3.x.x upgrading has been simplified, probably no longer needed. 18 | */ 19 | export const previousVersions: Array> = [ 20 | // insert here previous version on DB structure change 21 | ] 22 | 23 | /** 24 | * Array to be passed to `db-connector` to initialize the DB with Dexie, via the IndexedDb plugin. 25 | */ 26 | export const CLIENT_SEZ_DEFINITIONS = [projects, localSettings] 27 | -------------------------------------------------------------------------------- /app/src/app/data/system-local-db/sections/auds-sections.const.ts: -------------------------------------------------------------------------------- 1 | import { RESERVED_AUDS_KEYS } from 'app/models/project/project.declarations' 2 | import { ISection } from 'app/models/section/section.declarations' 3 | import { SectionDefinition } from 'app/libs/db-connector/db-builder/sez-definition' 4 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 5 | 6 | /** 7 | * Defines the table _sections and its fields for system data of the AnitaUniversalDataStorage structure. 8 | * This is needed to work on system sections with DbConnector. 9 | */ 10 | export const audsSections: SectionDefinition = { 11 | name: RESERVED_AUDS_KEYS._sections, 12 | pk: 'id', 13 | fields: ['title', 'title_short', 'icon', RESERVED_FIELDS.createdAt, RESERVED_FIELDS.updatedAt], 14 | jsonFields: ['childOf', 'formModel', 'viewSettings'] 15 | } 16 | -------------------------------------------------------------------------------- /app/src/app/data/system-local-db/sections/auds-settings.const.ts: -------------------------------------------------------------------------------- 1 | import { IProjectSettings, RESERVED_AUDS_KEYS } from 'app/models/project/project.declarations' 2 | import { SectionDefinition } from 'app/libs/db-connector/db-builder/sez-definition' 3 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 4 | 5 | /** 6 | * Defines the table _settings and its fields for system data of the AnitaUniversalDataStorage structure. 7 | * This is needed to work on system sections with DbConnector. 8 | */ 9 | export const audsSettings: SectionDefinition = { 10 | name: RESERVED_AUDS_KEYS._settings, 11 | pk: 'id', 12 | fields: ['title', 'localStorage', RESERVED_FIELDS.createdAt, RESERVED_FIELDS.updatedAt, 'description'] 13 | } 14 | -------------------------------------------------------------------------------- /app/src/app/data/system-local-db/sections/local-settings.const.ts: -------------------------------------------------------------------------------- 1 | import { SectionDefinition } from 'app/libs/db-connector/db-builder/sez-definition' 2 | 3 | /** 4 | * Defines the table localSettings and its fields for the `indexedDb` plugin of `db-connector` 5 | */ 6 | export const localSettings: SectionDefinition> = { 7 | name: 'localSettings', 8 | fields: [ 9 | 'data' 10 | ] 11 | } 12 | 13 | /** 14 | * Constant to uniquely identify values of local settings. 15 | * As of now there are no settings yet to save, so we keep `testData`. 16 | */ 17 | export enum LOCAL_SETTINGS_KEYS { 18 | testData = 1 19 | } 20 | 21 | /** 22 | * Defines the expected data structure to be saved in `localSettings`. 23 | */ 24 | export interface LocalSettingsData { 25 | id: T 26 | data: DataByKey[T] 27 | } 28 | 29 | /** 30 | * Identifies the data type saved in `localSettings` by the enum value of `LOCAL_SETTINGS_KEYS`. 31 | */ 32 | interface DataByKey { 33 | [LOCAL_SETTINGS_KEYS.testData]: unknown 34 | } 35 | 36 | /** 37 | * Example of the method to store data in localSettings: 38 | * 39 | * private saveLocalSettings(id: T, data: DataByKey[T]): void { 40 | * dbInsertor(CLIENT_SECTIONS.localSettings, { id, data }).autoInsert(); 41 | * } 42 | */ 43 | -------------------------------------------------------------------------------- /app/src/app/data/system-local-db/sections/projects.const.ts: -------------------------------------------------------------------------------- 1 | import { LocalProjectSettings } from 'app/models/project/project.declarations' 2 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 3 | import { SectionDefinition } from 'app/libs/db-connector/db-builder/sez-definition' 4 | 5 | /** 6 | * Defines the table projects and its fields for the `indexedDb` plugin of `db-connector`. 7 | * In the table `projects` are stored only the essential information needed to load the project from its data source. 8 | */ 9 | export const projects: SectionDefinition = { 10 | name: 'projects', 11 | fields: [ 12 | 'title', 13 | 'fileHandle', 14 | 'description', 15 | RESERVED_FIELDS.createdAt 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /app/src/app/libs/cloud-sync/cloud-sync.const.ts: -------------------------------------------------------------------------------- 1 | 2 | export interface IDropboxTokens { 3 | access_token: string 4 | account_id: string 5 | expires_in: number 6 | refresh_token: string 7 | scope: string 8 | token_type: 'bearer' 9 | uid: string 10 | } 11 | 12 | export enum CloudSyncState { 13 | NOT_CONNECTED = 1, 14 | OAUTH_IN_PROGRESS, 15 | NOT_LINKED, 16 | LINKED 17 | } 18 | 19 | export interface ISharedFileMeta { 20 | type: 'folder' | 'file' | 'deleted' 21 | name: string 22 | path: string | undefined 23 | id: string 24 | } 25 | -------------------------------------------------------------------------------- /app/src/app/libs/cloud-sync/sync-manager.class.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable eqeqeq */ 2 | import { LOCAL_STORAGE_SYSTEMS } from 'app/data/local-dbs/local-storage-systems.enum' 3 | import { CloudSyncState } from 'app/libs/cloud-sync/cloud-sync.const' 4 | import { RemoteAndLocalMerger } from 'app/libs/cloud-sync/remote-and-local-merger.class' 5 | import { IS_SAVING_IN_FS, IS_SYNCING } from 'app/libs/cloud-sync/sync-manager.const' 6 | import { Manager } from 'app/libs/manager/manager.class' 7 | 8 | export class SyncManager { 9 | public static syncWithRemoteOrLocal = async (): Promise => { 10 | if (Manager.getCurrentProject()?.syncInfo.getLocalStorage() == LOCAL_STORAGE_SYSTEMS.IndexedDB) { 11 | await this.handleRemoteSync() 12 | } else { 13 | this.handleLocalSync() 14 | } 15 | IS_SAVING_IN_FS.next(false) 16 | } 17 | 18 | private static handleRemoteSync = (): Promise | undefined => { 19 | if (SyncManager.canStartSyncWithRemote()) { 20 | return new RemoteAndLocalMerger(Manager.getCurrentProject()?.syncInfo.getLinkedFileId()!).sync() 21 | } 22 | } 23 | 24 | private static handleLocalSync = () => { 25 | // TODO 26 | } 27 | 28 | private static canStartSyncWithRemote = (): boolean => ( 29 | !IS_SYNCING.getValue() && 30 | Manager.getCurrentProject()?.syncInfo.getCloudSyncState() === CloudSyncState.LINKED && 31 | !!Manager.getCurrentProject()?.syncInfo.getLinkedFileId() 32 | ) 33 | } 34 | -------------------------------------------------------------------------------- /app/src/app/libs/cloud-sync/sync-manager.const.ts: -------------------------------------------------------------------------------- 1 | import { BehaviorSubject } from 'rxjs' 2 | 3 | export const IS_SYNCING = new BehaviorSubject(false) 4 | 5 | export const IS_SAVING_IN_FS = new BehaviorSubject(false) 6 | 7 | export const LAST_SYNCED_ID = new BehaviorSubject(null) 8 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/common-helpers/where-builder.class.ts: -------------------------------------------------------------------------------- 1 | import { WhereArgs } from 'app/libs/db-connector/common-helpers/query-maker.class' 2 | 3 | /** 4 | * Builds where clauses for MySql query string 5 | */ 6 | export class WhereBuilder { 7 | /** 8 | * The where arguments as an Array of WhereArgs { field: string, operator: string, value: string | number } 9 | */ 10 | protected whereArgs: Array = [] 11 | 12 | /** 13 | * Creates an instance of where builder. 14 | * @param args the arguments to use to build the where clauses 15 | */ 16 | constructor ( 17 | protected args: Partial 18 | ) { 19 | this.addWhereFromObj() 20 | } 21 | 22 | /** 23 | * Builds where clauses from an Object. The equals operator `=` is used 24 | */ 25 | protected addWhereFromObj (): void { 26 | if (!this.args) { 27 | return 28 | } 29 | 30 | for (const key in this.args) { 31 | this.addWhere(key, this.args[key]) 32 | } 33 | } 34 | 35 | /** 36 | * Builds the where arguments for the given field, value and oeprator 37 | */ 38 | private addWhere (field: string, value: any, operator = '='): any { 39 | const params: WhereArgs = { 40 | field, 41 | operator, 42 | value 43 | } 44 | this.whereArgs.push(params) 45 | return this 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/crypter/decrypter.class.ts: -------------------------------------------------------------------------------- 1 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 2 | import { CryptHelper } from 'app/libs/db-connector/crypter/crypt-helper.class' 3 | import { stringDecrypter } from 'app/libs/db-connector/crypter/string-decrypter.function' 4 | 5 | /** 6 | * Decrypts one element at a time. 7 | */ 8 | export class Decrypter extends CryptHelper { 9 | /** 10 | * Retrieves the key to decrypt and loops over all fields to decrypt. 11 | */ 12 | public async do (): Promise { 13 | const keyToUse = this.getUserKey() 14 | 15 | if (keyToUse === undefined) { 16 | return this.logNoKeyError() 17 | } 18 | 19 | for (const fieldName in this.element) { 20 | if (!Object.values(RESERVED_FIELDS).includes(fieldName as any)) { 21 | this.decrypt(keyToUse, fieldName) 22 | } 23 | } 24 | } 25 | 26 | /** 27 | * Decrypts the value of the given key. 28 | * 29 | * @param keyToUse the key to be used for decryption. 30 | * @param fieldName the key of the field to decrypt. 31 | */ 32 | private decrypt (keyToUse: string, fieldName: keyof E): void { 33 | if (this.element[fieldName]) { 34 | this.element[fieldName] = stringDecrypter(this.element[fieldName] as unknown as string, keyToUse) as unknown as E[keyof E] 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/crypter/encrypter.class.ts: -------------------------------------------------------------------------------- 1 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 2 | import { CryptHelper } from 'app/libs/db-connector/crypter/crypt-helper.class' 3 | import { stringCrypter } from 'app/libs/db-connector/crypter/string-crypter.function' 4 | 5 | /** 6 | * Encrypts one element at a time. 7 | */ 8 | export class Encrypter extends CryptHelper { 9 | /** 10 | * Retrieves the key to encrypt and loops over all fields to decrypt. 11 | */ 12 | public async do (): Promise { 13 | const keyToUse = this.getUserKey() 14 | 15 | if (keyToUse === undefined) { 16 | return this.logNoKeyError() 17 | } 18 | 19 | for (const fieldName in this.element) { 20 | if (!Object.values(RESERVED_FIELDS).includes(fieldName as any)) { 21 | this.encrypt(keyToUse, fieldName) 22 | } 23 | } 24 | } 25 | 26 | /** 27 | * Encrypts the value of the given key. 28 | * 29 | * @param keyToUse the key to be used for encryption. 30 | * @param fieldName the key of the field to encrypt. 31 | */ 32 | private encrypt (keyToUse: string, fieldName: keyof E): void { 33 | if (this.element[fieldName]) { 34 | this.element[fieldName] = stringCrypter(this.element[fieldName] as unknown as string, keyToUse) as unknown as E[keyof E] 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/crypter/options.constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Algorithm used for encryption. 3 | */ 4 | export const securePassEncrypter = '286b88f2ceb76ce1e7d28117bb2a7659' 5 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/crypter/string-crypter.function.ts: -------------------------------------------------------------------------------- 1 | import CryptoES from 'crypto-es' 2 | 3 | /** 4 | * Encrypts a string 5 | * 6 | * @param value the value to encrypt 7 | * @param keyToUse the key to use for encryption 8 | * @return the encrypted string 9 | */ 10 | export function stringCrypter (value: string, keyToUse: string): string { 11 | const encrypted = CryptoES.AES.encrypt(value, keyToUse) 12 | return encrypted.toString() 13 | } 14 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/crypter/string-decrypter.function.ts: -------------------------------------------------------------------------------- 1 | import CryptoES from 'crypto-es' 2 | 3 | /** 4 | * Decrypts a string 5 | * 6 | * @param value the value to decrypt 7 | * @param keyToUse the key to use for decryption 8 | * @return the decrypted string 9 | */ 10 | export function stringDecrypter (value: string, keyToUse: string): string { 11 | const decrypted = CryptoES.AES.decrypt(value, keyToUse) 12 | return decrypted.toString() 13 | } 14 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/db-builder/default-values.constant.ts: -------------------------------------------------------------------------------- 1 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 2 | 3 | /** 4 | * Default primary key if none is specified. 5 | */ 6 | export const DEFAULT_PK = RESERVED_FIELDS.id 7 | 8 | /** 9 | * Default parent identifier if none is specified. 10 | */ 11 | export const DEFAULT_PARENTS_IDENTIFIER = RESERVED_FIELDS.parentsInfo 12 | 13 | /** 14 | * Default owner identifier if none is specified. 15 | */ 16 | export const DEFAULT_OWNER_IDENTIFIER = RESERVED_FIELDS.createdBy 17 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/db-builder/sez-definition.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Section name 3 | */ 4 | export type SectionName = string; 5 | 6 | /** 7 | * Contains the minimum information needed to build a SectionModel. 8 | */ 9 | export interface SectionDefinition { 10 | name: SectionName 11 | pk?: keyof T & string 12 | fields?: Array 13 | indexes?: Array 14 | orderBy?: keyof T & string 15 | ownerIdentifier?: keyof T & string 16 | childOf?: Array 17 | parentsIdentifiers?: keyof T 18 | jsonFields?: Array 19 | } 20 | 21 | /** 22 | * Defines the information on a section needed to perform queries. 23 | */ 24 | export interface SectionModel extends SectionDefinition { 25 | name: SectionName 26 | pk: keyof T & string 27 | fields: Array 28 | indexes: Array 29 | orderBy: keyof T & string 30 | ownerIdentifier: keyof T & string 31 | jsonFields: Array 32 | } 33 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/models/abstract-model.d.ts: -------------------------------------------------------------------------------- 1 | import { SectionModel } from 'app/libs/db-connector/db-builder/sez-definition' 2 | 3 | /** 4 | * Abstracrt interface of the sections of the data structure of the DB. 5 | */ 6 | export interface AbstractModel { 7 | [key: string]: SectionModel 8 | } 9 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/deta/db-deletor/db-deletor.class.ts: -------------------------------------------------------------------------------- 1 | import { AbstractModel } from 'app/libs/db-connector/models/abstract-model' 2 | import { DbConnectorInstance, Deletor } from 'app/libs/db-connector/models/executers' 3 | import { Logger } from 'app/libs/logger/logger.class' 4 | import Base from 'deta/dist/types/base' 5 | 6 | /** 7 | * Implements the deletor for Deta Base 8 | * @see https://docs.deta.sh/docs/base/sdk 9 | */ 10 | export class DbDeletor implements Deletor { 11 | /** 12 | * Creates an instance of db deletor. 13 | * @param section the section of the element to delete 14 | * @param args the arguments for the query, must include the primary key (`pk`) value 15 | */ 16 | constructor ( 17 | private dbConnector: DbConnectorInstance, 18 | private section: keyof AbstractModel, 19 | private args: Partial 20 | ) { } 21 | 22 | /** 23 | * Perform the delete action on the given element. 24 | */ 25 | public async autoDelete (): Promise { 26 | const key = this.args[this.dbConnector.DS[this.section].pk as keyof E] as unknown as string 27 | if (key) { 28 | await this.dbConnector.dbStore.db.delete(key) 29 | } else { 30 | Logger.error('Error in autoDelete', 'No PK value was found on the element') 31 | } 32 | } 33 | 34 | public async clearSection (): Promise { 35 | // not implemented 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/deta/db-store/dbstore.class.ts: -------------------------------------------------------------------------------- 1 | import { AbstractModel } from 'app/libs/db-connector/models/abstract-model' 2 | import { DbStoreInterface, DsDbInitOptions } from 'app/libs/db-connector/models/executers' 3 | import { Deta as DetaFunction } from 'deta' 4 | import Base from 'deta/dist/types/base' 5 | import Deta from 'deta/dist/types/deta' 6 | 7 | /** 8 | * Implementation of DbStore for Deta Base. 9 | */ 10 | export class DbStore implements DbStoreInterface { 11 | public db!: Base 12 | private deta!: Deta 13 | 14 | constructor ( 15 | private options: DsDbInitOptions, 16 | private DS: AbstractModel 17 | ) { } 18 | 19 | public async initDB (): Promise> { 20 | if (!this.options.detaConnectionData) { 21 | throw Error('No connection data provided. Please provide a name for the database by setting the name on the property detaConnectionData in the options passed to DbConnector.') 22 | } 23 | 24 | this.setDeta() 25 | this.setDb() 26 | return this 27 | } 28 | 29 | public close (): void { 30 | // not implemented 31 | } 32 | 33 | private setDeta (): void { 34 | this.deta = DetaFunction(this.options.detaConnectionData!.projectKey) 35 | } 36 | 37 | private setDb (): void { 38 | this.db = this.deta.Base(this.options.detaConnectionData!.projectId) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/deta/db-updator/db-updator.class.ts: -------------------------------------------------------------------------------- 1 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 2 | import { AbstractModel } from 'app/libs/db-connector/models/abstract-model' 3 | import { DbConnectorInstance, Updator } from 'app/libs/db-connector/models/executers' 4 | import Base from 'deta/dist/types/base' 5 | import { ObjectType } from 'deta/dist/types/types/basic' 6 | 7 | /** 8 | * Implements updator for IndexedDb 9 | */ 10 | export class DbUpdator implements Updator { 11 | /** 12 | * Creates an instance of db updator. 13 | * @param section the section of the element to update 14 | * @param element the full element updated 15 | */ 16 | constructor ( 17 | private dbConnector: DbConnectorInstance, 18 | private section: keyof AbstractModel, 19 | private element: Partial 20 | ) { } 21 | 22 | /** 23 | * Calls `QueryMaker` and updates the element 24 | */ 25 | public async autoUpdate (): Promise { 26 | const element = { ...this.element } 27 | element.key = element[RESERVED_FIELDS.id] 28 | element.section = this.section 29 | element.projectId = this.dbConnector.options.detaConnectionData!.projectId 30 | await this.dbConnector.dbStore.db.update(element as unknown as ObjectType, this.element[RESERVED_FIELDS.id]) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/deta/exporter.constant.ts: -------------------------------------------------------------------------------- 1 | import { DbObjects } from 'app/libs/db-connector/models/executers' 2 | import Base from 'deta/dist/types/base' 3 | import { DbDeletor } from './db-deletor/db-deletor.class' 4 | import { DbInsertor } from './db-insertor/db-insertor.class' 5 | import { DbSelector } from './db-selector/db-selector.class' 6 | import { DbStore } from './db-store/dbstore.class' 7 | import { DbUpdator } from './db-updator/db-updator.class' 8 | 9 | /** 10 | * Constant to be passed to DbInit to use IndexedDB as plugin 11 | */ 12 | export const INDEXEDDB_PLUGIN: DbObjects = { 13 | insertor: DbInsertor, 14 | selector: DbSelector, 15 | updator: DbUpdator, 16 | deletor: DbDeletor, 17 | dbStore: DbStore 18 | } 19 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/file-handles/db-insertor/db-insertor.class.ts: -------------------------------------------------------------------------------- 1 | import { Encrypter } from 'app/libs/db-connector/crypter/encrypter.class' 2 | import { Insertor } from 'app/libs/db-connector/models/executers' 3 | import { ElementAdderToCollection } from 'app/libs/db-connector/plugins/file-handles/helpers/element-adder-to-collection.class' 4 | 5 | /** 6 | * Implements insertor for MySql 7 | */ 8 | export class DbInsertor extends ElementAdderToCollection implements Insertor { 9 | /** 10 | * Adds an element to the collection 11 | */ 12 | public async autoInsert (): Promise { 13 | if (this.dbConnector.options.encrypted) { 14 | await this.handleEncryption() 15 | } 16 | 17 | await this.save() 18 | } 19 | 20 | /** 21 | * Handles encryption with Encrypter 22 | * 23 | * @see Encrypter 24 | */ 25 | private async handleEncryption (): Promise { 26 | if (this.elements instanceof Array) { 27 | for (const element of this.elements) { 28 | await new Encrypter(this.dbConnector, this.section, element).do() 29 | } 30 | } else { 31 | await new Encrypter(this.dbConnector, this.section, this.elements).do() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/file-handles/db-updator/db-updator.class.ts: -------------------------------------------------------------------------------- 1 | import { Encrypter } from 'app/libs/db-connector/crypter/encrypter.class' 2 | import { Updator } from 'app/libs/db-connector/models/executers' 3 | import { ElementAdderToCollection } from 'app/libs/db-connector/plugins/file-handles/helpers/element-adder-to-collection.class' 4 | 5 | /** 6 | * Implements updator for MySql 7 | */ 8 | export class DbUpdator extends ElementAdderToCollection implements Updator { 9 | /** 10 | * Updates an element in the collection 11 | */ 12 | public async autoUpdate (): Promise { 13 | if (this.dbConnector.options.encrypted) { 14 | await this.handleEncryption() 15 | } 16 | 17 | await this.save() 18 | } 19 | 20 | /** 21 | * Handles encryption 22 | * 23 | * @see Encrypter 24 | */ 25 | private async handleEncryption (): Promise { 26 | if (this.elements instanceof Array) { 27 | for (const element of this.elements) { 28 | await new Encrypter(this.dbConnector, this.section, element).do() 29 | } 30 | } else { 31 | await new Encrypter(this.dbConnector, this.section, this.elements).do() 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/file-handles/exporter.constant.ts: -------------------------------------------------------------------------------- 1 | import { TAnitaUniversalDataStorage } from 'app/models/project/project.declarations' 2 | import { DbObjects } from 'app/libs/db-connector/models/executers' 3 | import { DbDeletor } from './db-deletor/db-deletor.class' 4 | import { DbInsertor } from './db-insertor/db-insertor.class' 5 | import { DbSelector } from './db-selector/db-selector.class' 6 | import { DbStore } from './db-store/dbstore.class' 7 | import { DbUpdator } from './db-updator/db-updator.class' 8 | 9 | /** 10 | * Constant to be passed to DbInit to use MySql as plugin 11 | */ 12 | export const FILE_HANDLES_PLUGIN: DbObjects = { 13 | insertor: DbInsertor, 14 | selector: DbSelector, 15 | updator: DbUpdator, 16 | deletor: DbDeletor, 17 | dbStore: DbStore 18 | } 19 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/file-handles/helpers/file-handle-checker.function.ts: -------------------------------------------------------------------------------- 1 | import { DsDbInitOptions } from 'app/libs/db-connector/models/executers' 2 | import { FileSystemDirectoryHandle, FileSystemFileHandle } from 'app/libs/db-connector/plugins/file-handles/helpers/file-system-access-api' 3 | import { FsHelper } from 'app/libs/db-connector/plugins/file-handles/helpers/fs-helper' 4 | import { Logger } from 'app/libs/logger/logger.class' 5 | 6 | /** 7 | * Uses the fileHandle stored in IndexedDB, if any, or asks for a new one 8 | */ 9 | export async function fileHandleChecker ( 10 | options: DsDbInitOptions, 11 | description?: string, 12 | accept?: { [mimeType: string]: Array } 13 | ): Promise { 14 | Logger.info('[fileHandleChecker] verify permissions for fileHandle', options.projectInfo!.fileHandle) 15 | if (!options.projectInfo!.fileHandle) { 16 | return await FsHelper.getNewFileHandle(`anita-fh-${options.projectInfo!.id}`, description, accept) 17 | } 18 | 19 | await FsHelper.verifyPermission(options.projectInfo!.fileHandle, true) 20 | return options.projectInfo!.fileHandle 21 | } 22 | 23 | export async function dirHandleChecker (options: DsDbInitOptions): Promise { 24 | if (!options.projectInfo!.fileHandle) { 25 | return await FsHelper.getDirectoryHandle() 26 | } 27 | 28 | await FsHelper.verifyPermission(options.projectInfo!.fileHandle, true) 29 | return options.projectInfo!.fileHandle as any as FileSystemDirectoryHandle 30 | } 31 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/indexed-db/db-updator/db-updator.class.ts: -------------------------------------------------------------------------------- 1 | import { AbstractModel } from 'app/libs/db-connector/models/abstract-model' 2 | import { DbConnectorInstance, Updator } from 'app/libs/db-connector/models/executers' 3 | import { QueryMaker } from 'app/libs/db-connector/plugins/indexed-db/query-makers/query-maker.class' 4 | import { Logger } from 'app/libs/logger/logger.class' 5 | import Dexie from 'dexie' 6 | 7 | /** 8 | * Implements updator for IndexedDb 9 | */ 10 | export class DbUpdator implements Updator { 11 | /** 12 | * Creates an instance of db updator. 13 | * @param section the section of the element to update 14 | * @param element the full element updated 15 | */ 16 | constructor ( 17 | private dbConnector: DbConnectorInstance, 18 | private section: keyof AbstractModel, 19 | private element: Partial 20 | ) { } 21 | 22 | /** 23 | * Calls `QueryMaker` and updates the element 24 | */ 25 | public autoUpdate (): Promise { 26 | return new QueryMaker(this.dbConnector, this.section, this.element) 27 | .update() 28 | .catch(err => { 29 | Logger.error('Error in autoUpdate', err) 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/indexed-db/exporter.constant.ts: -------------------------------------------------------------------------------- 1 | import { DbObjects } from 'app/libs/db-connector/models/executers' 2 | import { DbStore } from 'app/libs/db-connector/plugins/indexed-db/db-store/dbstore.class' 3 | import Dexie from 'dexie' 4 | import { DbDeletor } from './db-deletor/db-deletor.class' 5 | import { DbInsertor } from './db-insertor/db-insertor.class' 6 | import { DbSelector } from './db-selector/db-selector.class' 7 | import { DbUpdator } from './db-updator/db-updator.class' 8 | 9 | /** 10 | * Constant to be passed to DbInit to use IndexedDB as plugin 11 | */ 12 | export const INDEXEDDB_PLUGIN: DbObjects = { 13 | insertor: DbInsertor, 14 | selector: DbSelector, 15 | updator: DbUpdator, 16 | deletor: DbDeletor, 17 | dbStore: DbStore 18 | } 19 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/sqlite/exporter.constant.ts: -------------------------------------------------------------------------------- 1 | import { DbObjects } from 'app/libs/db-connector/models/executers' 2 | import { Database } from 'sql.js' 3 | import { DbDeletor } from './db-deletor/db-deletor.class' 4 | import { DbInsertor } from './db-insertor/db-insertor.class' 5 | import { DbSelector } from './db-selector/db-selector.class' 6 | import { DbStore } from './db-store/dbstore.class' 7 | import { DbUpdator } from './db-updator/db-updator.class' 8 | 9 | /** 10 | * Constant to be passed to DbInit to use MySql as plugin 11 | */ 12 | export const SQLITE_PLUGIN: DbObjects = { 13 | insertor: DbInsertor, 14 | selector: DbSelector, 15 | updator: DbUpdator, 16 | deletor: DbDeletor, 17 | dbStore: DbStore 18 | } 19 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/sqlite/helpers/execute-query-no-return.function.ts: -------------------------------------------------------------------------------- 1 | import { DbConnectorInstance } from 'app/libs/db-connector/models/executers' 2 | import { Database } from 'sql.js' 3 | 4 | export async function executeQueryNoReturn (dbConnector: DbConnectorInstance, query: string): Promise { 5 | await dbConnector.dbStore.db.run(query) 6 | } 7 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/sqlite/helpers/execute-query-with-return.function.ts: -------------------------------------------------------------------------------- 1 | import { Database } from 'sql.js' 2 | 3 | export async function executeQueryWithReturn (db: Database, query: string): Promise { 4 | return db.exec(query) 5 | } 6 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/sqlite/helpers/schema-creator.class.tsx: -------------------------------------------------------------------------------- 1 | import { AbstractModel } from 'app/libs/db-connector/models/abstract-model' 2 | import { DbStore } from 'app/libs/db-connector/plugins/sqlite/db-store/dbstore.class' 3 | 4 | export class SchemaCreator { 5 | private sql = '' 6 | 7 | constructor ( 8 | private dbStore: DbStore, 9 | private DS: AbstractModel 10 | ) { } 11 | 12 | public async createSchema () { 13 | for (const table in this.DS) { 14 | this.handleTable(this.DS[table].name, this.DS[table].fields) 15 | } 16 | await this.dbStore.db.run(this.sql) 17 | } 18 | 19 | public handleTable (table: string, fields: Array): void { 20 | this.sql += `DROP TABLE IF EXISTS '${table}'; CREATE TABLE '${table}' (${this.createFields(fields)});` 21 | } 22 | 23 | private createFields (fields: Array): string { 24 | let sql = '' 25 | for (const field of fields) { 26 | sql += `${field} TEXT,` 27 | } 28 | return sql.substring(0, sql.length - 1) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/sqlite/helpers/schema-exporter.function.ts: -------------------------------------------------------------------------------- 1 | import { FileSystemDirectoryHandle } from 'app/libs/db-connector/plugins/file-handles/helpers/file-system-access-api' 2 | import { Logger } from 'app/libs/logger/logger.class' 3 | import { Database } from 'sql.js' 4 | 5 | // We can't write in place to the db, see https://wicg.github.io/file-system-access/#api-filesystemdirectoryhandle-removeentry ISSUE 6 6 | export async function schemaExporter (db: Database, dirHandle: FileSystemDirectoryHandle, projectId: string): Promise { 7 | Logger.info('Exporting schema...') 8 | const binaryArray = db.export() 9 | const fileHandle = await dirHandle.getFileHandle(`${projectId}.db`, { create: true }) 10 | const writable = await fileHandle.createWritable({ keepExistingData: true }) 11 | writable.truncate(0) 12 | await writable.write(binaryArray) 13 | await writable.close() 14 | } 15 | -------------------------------------------------------------------------------- /app/src/app/libs/db-connector/plugins/sqlite/helpers/serializer.function.ts: -------------------------------------------------------------------------------- 1 | import { SqlValue } from 'sql.js' 2 | 3 | export function serializer (columns: Array, values: Array>): Array { 4 | return values.map(row => { 5 | const objData = {} as E 6 | row.forEach((cell, index) => { 7 | const key = columns[index] 8 | objData[key] = cell as any 9 | }) 10 | return objData 11 | }) 12 | } 13 | -------------------------------------------------------------------------------- /app/src/app/libs/id-creator/id-creator.class.ts: -------------------------------------------------------------------------------- 1 | import { DateTools } from 'app/libs/tools/date-tools.class' 2 | import CryptoES from 'crypto-es' 3 | 4 | /** 5 | * Randomly generated strings hasehd with sha256 to be used as unique identifiers 6 | */ 7 | export class IdCreator { 8 | /** 9 | * Makes a unique random string with the a name 10 | */ 11 | public static make (name: string): string { 12 | const aliasDate = DateTools.getUtcIsoString() 13 | const randNumber = Math.floor(Math.random() * (9999 - 1000 + 1)) + 1000 14 | 15 | const id = `${name}${aliasDate}${randNumber}` 16 | 17 | return CryptoES.SHA256(id).toString() 18 | } 19 | 20 | /** 21 | * Makes a unique completely random string 22 | */ 23 | public static random (): string { 24 | const aliasDate = DateTools.getUtcIsoString() 25 | const randNumber = Math.floor(Math.random() * (9999 - 1000 + 1)) + 1000 26 | 27 | const id = `${aliasDate}${randNumber}` 28 | 29 | return CryptoES.SHA256(id).toString() 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/app/libs/projects-helpers/projects-handlers/projects-list-loader.class.ts: -------------------------------------------------------------------------------- 1 | import { dbInstances } from 'app/data/local-dbs/db-instances.const' 2 | import { LocalProjectSettings } from 'app/models/project/project.declarations' 3 | import { CLIENT_SECTIONS } from 'app/data/system-local-db/client-sections.enum' 4 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 5 | import { storeDispatcher } from 'app/libs/redux/store-dispatcher.function' 6 | 7 | /** 8 | * Loads the list of projects on the current device 9 | */ 10 | export class ProjectsListLoader { 11 | /** 12 | * The list of projects to sore in the current state 13 | */ 14 | private projectList: Array = [] 15 | 16 | /** 17 | * Loads the projects from IndexedDB and adds them to the current state 18 | */ 19 | public async load (): Promise { 20 | await this.loadFromLocalDB() 21 | this.dispatchProjectList(this.projectList) 22 | } 23 | 24 | /** 25 | * Loads the projects from IndexedDB with db-connector 26 | */ 27 | private async loadFromLocalDB (): Promise { 28 | this.projectList = await dbInstances.system.callSelector(CLIENT_SECTIONS.projects).multiple() 29 | } 30 | 31 | /** 32 | * Dispatchs the project list to the current state 33 | */ 34 | private dispatchProjectList (payload: Array): void { 35 | storeDispatcher(({ type: REDUX_ACTIONS.setProjectList, payload })) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/reducers/form-element.reducer.ts: -------------------------------------------------------------------------------- 1 | import { ISectionElement } from 'app/models/section-element/section-element.declarations' 2 | import { Action } from 'app/libs/redux/action.type' 3 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 4 | 5 | export interface IFormElementState { 6 | element: ISectionElement | undefined | null 7 | } 8 | 9 | /** 10 | * The initial state of the container of the current project 11 | */ 12 | const formElementState: IFormElementState = { 13 | element: null 14 | } 15 | 16 | /** 17 | * Updates the projectState 18 | */ 19 | export const formElementReducer = (state: IFormElementState = formElementState, action: Action): IFormElementState => { 20 | switch (action.type) { 21 | case REDUX_ACTIONS.updateFormElement: 22 | return { 23 | element: { ...action.payload } 24 | } 25 | case REDUX_ACTIONS.updateFormElementKey: 26 | return { 27 | element: { ...state.element, [action.payload.fieldName]: action.payload.value } 28 | } 29 | default: 30 | return state 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/reducers/form-eles-valid-state.reducer.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'app/libs/redux/action.type' 2 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 3 | 4 | export interface IFormElesValidState { 5 | [formEleUniqueId: string]: boolean 6 | } 7 | 8 | /** 9 | * The initial state of the form-eles-valid-state reducer 10 | */ 11 | const initialState: IFormElesValidState = {} 12 | 13 | /** 14 | * The reducer function that sets the valid state of the form elements 15 | */ 16 | export const formElesValidStateReducer = (state: IFormElesValidState = initialState, action: Action): IFormElesValidState => { 17 | switch (action.type) { 18 | case REDUX_ACTIONS.setValidStateForEle: 19 | return { ...state, [action.payload.formEleId]: action.payload.valid } 20 | case REDUX_ACTIONS.unsetValidStateForEle: { 21 | const copyState = { ...state } 22 | delete copyState[action.payload] 23 | return copyState 24 | } default: 25 | return state 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/reducers/layout.reducer.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'app/libs/redux/action.type' 2 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 3 | 4 | export interface ILayoutState { 5 | sidebar: '-translate-x-full' | '' 6 | } 7 | 8 | /** 9 | * The initial state of the container of the current project 10 | */ 11 | const formElementState: ILayoutState = { 12 | sidebar: '-translate-x-full' 13 | } 14 | 15 | /** 16 | * Updates the projectState 17 | */ 18 | export const layoutReducer = (state: ILayoutState = formElementState, action: Action): ILayoutState => { 19 | switch (action.type) { 20 | case REDUX_ACTIONS.toggleSidebar: { 21 | const newState = { ...state } 22 | newState.sidebar = newState.sidebar === '-translate-x-full' ? '' : '-translate-x-full' 23 | return newState 24 | } default: 25 | return state 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/reducers/project.reducer.ts: -------------------------------------------------------------------------------- 1 | import { TSystemData } from 'app/models/project/project.declarations' 2 | import { Action } from 'app/libs/redux/action.type' 3 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 4 | 5 | /** 6 | * The initial state of the container of the current project 7 | */ 8 | const projectState: TSystemData | null = null 9 | 10 | /** 11 | * Updates the projectState 12 | */ 13 | export const projectReducer = (state: TSystemData | null = projectState, action: Action): TSystemData | null => { 14 | switch (action.type) { 15 | case REDUX_ACTIONS.setCurrentProject: 16 | return action.payload 17 | case REDUX_ACTIONS.resetCurrentProject: 18 | return null 19 | case REDUX_ACTIONS.updateSection: { 20 | const newState = { ...state } 21 | const sectionToUpdateIndex = newState._sections!.findIndex(section => section.id === action.payload.id) 22 | newState._sections![sectionToUpdateIndex] = action.payload 23 | return newState as TSystemData 24 | } 25 | default: 26 | return state 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/reducers/projects.reducer.ts: -------------------------------------------------------------------------------- 1 | import { LocalProjectSettings } from 'app/models/project/project.declarations' 2 | import { Action } from 'app/libs/redux/action.type' 3 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 4 | 5 | /** 6 | * The initial state of the container of the current project 7 | */ 8 | export const projectsState: Array | null = null 9 | 10 | /** 11 | * Updates the projectsState 12 | */ 13 | export const projectsReducer = (state: Array | null = projectsState, action: Action) => { 14 | switch (action.type) { 15 | case REDUX_ACTIONS.addProjectToList: 16 | return state ? state.concat(action.payload) : [action.payload] 17 | case REDUX_ACTIONS.setProjectList: 18 | return action.payload 19 | default: 20 | return state 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/reducers/sections-for-child-of-selector.reducer.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'app/libs/redux/action.type' 2 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 3 | import { SectionDetailsDeclaration } from 'app/models/section/section.declarations' 4 | 5 | /** 6 | * The initial state of the container of the sections selectable by the child of selector 7 | */ 8 | const sectionsForChildOfSelectorState: Array = [] 9 | 10 | /** 11 | * Updates the sections selectable by the child of selector component 12 | * 13 | * @see ChildOfSelectorForSectionComponent 14 | */ 15 | export const sectionsForChildOfSelectorReducer = (state: Array = sectionsForChildOfSelectorState, action: Action) => { 16 | switch (action.type) { 17 | case REDUX_ACTIONS.addSectionForChildOfSelector: { 18 | const editableState = state.concat() 19 | 20 | if (!editableState.length) { 21 | editableState.push(action.payload) 22 | } else { 23 | const index = editableState.findIndex(sectionDeclaration => sectionDeclaration.id === action.payload.id) 24 | const position = index >= 0 ? index : editableState.length 25 | editableState[position] = action.payload 26 | } 27 | return editableState 28 | } default: 29 | return state 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/redux-actions.const.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Supported reducer actions 3 | */ 4 | export enum REDUX_ACTIONS { 5 | // LAYOUT 6 | toggleSidebar = 1, 7 | // PROJECTS 8 | addProjectToList, 9 | setProjectList, 10 | // PROJECT 11 | setCurrentProject, 12 | resetCurrentProject, 13 | updateSection, 14 | // SECTIONS_FOR_CHILD_OF_SELECTOR 15 | addSectionForChildOfSelector, 16 | resetSectionForChildOfSelector, 17 | // FORMS 18 | setValidStateForEle, 19 | unsetValidStateForEle, 20 | updateFormElement, 21 | updateFormElementKey, 22 | setFormProject, 23 | setProjectEditorMode, 24 | updateFormProjectSettings, 25 | updateFormProjectAddSection, 26 | updateFormProjectUpdateSection, 27 | updateFormProjectUpdateFormModelOfSection, 28 | updateFormProjectRemoveSection, 29 | updateFormProjectAddFieldToSection, 30 | updateFormProjectRemoveFieldFromSection, 31 | updateFormProjectUpdateFormModelAddOption, 32 | updateFormProjectUpdateFormModelDeleteOption, 33 | updateFormProjectUpdateFormModelOptionValue 34 | } 35 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/state.store.ts: -------------------------------------------------------------------------------- 1 | import { REDUCERS } from 'app/libs/redux/reducers.const' 2 | import { combineReducers, createStore } from 'redux' 3 | 4 | const combinedReducers = combineReducers(REDUCERS) 5 | 6 | export const store = createStore(combinedReducers) 7 | -------------------------------------------------------------------------------- /app/src/app/libs/redux/store-dispatcher.function.ts: -------------------------------------------------------------------------------- 1 | import { Action } from 'app/libs/redux/action.type' 2 | import { REDUX_ACTIONS } from 'app/libs/redux/redux-actions.const' 3 | import { store } from 'app/libs/redux/state.store' 4 | 5 | export function storeDispatcher (action: Action): void { 6 | store.dispatch(action) 7 | } 8 | -------------------------------------------------------------------------------- /app/src/app/libs/routing/anita-routes.constant.ts: -------------------------------------------------------------------------------- 1 | import { EDITOR_MODE } from 'app/components/editor-mode.enum' 2 | 3 | export enum URL_PARAMS { 4 | projectId = 'projectId', 5 | sectionId = 'sectionId', 6 | elementId = 'elementId', 7 | parentId = 'parentId' 8 | } 9 | 10 | /** 11 | * Lists all the routes in the application 12 | * 13 | * @remarks We can't use the enum approach here because we need to concat strings. 14 | */ 15 | export const ANITA_URLS = { 16 | // OAUTH 17 | auth: '/auth', 18 | // PROJECTS 19 | projectsNone: '/projects/none', 20 | projectsList: '/projects/list', 21 | projectAdd: `/projects/${EDITOR_MODE.add}`, 22 | projectEdit: `/projects/${EDITOR_MODE.edit}/:${URL_PARAMS.projectId}`, 23 | // PROJECT 24 | projectDetails: `/project/:${URL_PARAMS.projectId}/info`, 25 | projectSectionElesList: `/project/:${URL_PARAMS.projectId}/list/:${URL_PARAMS.sectionId}`, 26 | projectSectionAddEle: `/project/:${URL_PARAMS.projectId}/:${URL_PARAMS.sectionId}/${EDITOR_MODE.add}`, 27 | projectSectionEditEle: `/project/:${URL_PARAMS.projectId}/:${URL_PARAMS.sectionId}/${EDITOR_MODE.edit}/:${URL_PARAMS.elementId}`, 28 | projectSectionEleDetails: `/project/:${URL_PARAMS.projectId}/:${URL_PARAMS.sectionId}/details/:${URL_PARAMS.elementId}` 29 | } 30 | -------------------------------------------------------------------------------- /app/src/app/libs/routing/url-param-fillers.function.ts: -------------------------------------------------------------------------------- 1 | import { URL_PARAMS } from 'app/libs/routing/anita-routes.constant' 2 | 3 | /** 4 | * Accepts a url and a param and replaces all the occurences of the url param with a given value 5 | */ 6 | export function urlParamFiller (url: string, paramsToFill: Array<{ name: URL_PARAMS; value: string }>): string { 7 | let result = url 8 | paramsToFill.forEach(params => { 9 | result = result.replace(new RegExp(`:${params.name}`, 'g'), params.value) 10 | }) 11 | return result 12 | } 13 | -------------------------------------------------------------------------------- /app/src/app/libs/shortcuts/shortcuts-listener.ts: -------------------------------------------------------------------------------- 1 | import { KEYBOARD } from 'app/const/keyboard.const' 2 | import { IShortcut, IShortcutsByKey, SHORTCUTS_STORE } from 'app/libs/shortcuts/shortcuts-store' 3 | import { Keyboard } from 'app/libs/tools/keyboard.class' 4 | 5 | export class ShortcutsListener { 6 | public static init (): void { 7 | window.addEventListener('keydown', this.handleKeyDown) 8 | } 9 | 10 | public static handleKeyDown = (e: KeyboardEvent): void | boolean => { 11 | const shortcuts = this.getShortcuts(e) 12 | const arrayOfMethods = shortcuts[e.key] || shortcuts[e.key?.toLowerCase()] 13 | 14 | if (!arrayOfMethods?.length) { 15 | return true 16 | } 17 | 18 | return this.executeShortcut(e, arrayOfMethods) 19 | } 20 | 21 | private static getShortcuts (e: KeyboardEvent): IShortcutsByKey { 22 | const metaKey = e[KEYBOARD.MODIFIER_KEY] 23 | 24 | if (metaKey && e.shiftKey) { 25 | return SHORTCUTS_STORE.withMetaKeyAndShift 26 | } else if (metaKey) { 27 | return SHORTCUTS_STORE.withMetaKey 28 | } else if (e.shiftKey) { 29 | return SHORTCUTS_STORE.withShift 30 | } 31 | return SHORTCUTS_STORE.plain 32 | } 33 | 34 | private static executeShortcut (e: KeyboardEvent, arrayOfMethods: Array): void | boolean { 35 | const isInsideInput = Keyboard.isTyping(e.target as HTMLElement) 36 | const shortcutConfig = arrayOfMethods[0] 37 | if ((!shortcutConfig.skipIfInsideInput || !isInsideInput)) { 38 | shortcutConfig.callback(e) 39 | } else { 40 | return true 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /app/src/app/libs/shortcuts/shortcuts-manager.ts: -------------------------------------------------------------------------------- 1 | import { IShortcut, IShortcutsByKey, SHORTCUTS_STORE } from 'app/libs/shortcuts/shortcuts-store' 2 | 3 | export class ShortcutsManager { 4 | public static add (shortcut: IShortcut): void { 5 | const shortcutsByKey = this.getShortcutsStore(shortcut) 6 | shortcutsByKey[shortcut.key] = shortcutsByKey[shortcut.key] || [] 7 | shortcutsByKey[shortcut.key].unshift(shortcut) 8 | } 9 | 10 | public static remove (shortcut: IShortcut): void { 11 | const shortcutsByKey = this.getShortcutsStore(shortcut) 12 | if (shortcutsByKey[shortcut.key]) { 13 | shortcutsByKey[shortcut.key] = shortcutsByKey[shortcut.key].filter(s => s.id !== shortcut.id) 14 | } 15 | } 16 | 17 | private static getShortcutsStore (shortcut: IShortcut): IShortcutsByKey { 18 | switch (true) { 19 | case shortcut.withMetaKey && shortcut.withShift: 20 | return SHORTCUTS_STORE.withMetaKeyAndShift 21 | case shortcut.withMetaKey: 22 | return SHORTCUTS_STORE.withMetaKey 23 | case shortcut.withShift: 24 | return SHORTCUTS_STORE.withShift 25 | default: 26 | return SHORTCUTS_STORE.plain 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /app/src/app/libs/shortcuts/shortcuts-store.ts: -------------------------------------------------------------------------------- 1 | export interface IShortcut { 2 | id?: string 3 | key: string 4 | withMetaKey?: boolean 5 | withShift?: boolean 6 | skipIfInsideInput?: boolean 7 | callback: (...args: Array) => void 8 | } 9 | 10 | export interface IShortcutsByKey { 11 | [key: string]: Array 12 | } 13 | interface IShortcutsStore { 14 | plain: IShortcutsByKey 15 | withMetaKey: IShortcutsByKey 16 | withShift: IShortcutsByKey 17 | withMetaKeyAndShift: IShortcutsByKey 18 | } 19 | 20 | export const SHORTCUTS_STORE: IShortcutsStore = { 21 | plain: {}, 22 | withMetaKey: {}, 23 | withShift: {}, 24 | withMetaKeyAndShift: {} 25 | } 26 | -------------------------------------------------------------------------------- /app/src/app/libs/tools/array-tools.class.ts: -------------------------------------------------------------------------------- 1 | export class ArrayTools { 2 | /** 3 | * Asyncs forEach. Unlike native JS forEach, this one awaits each callback to resolve befofore iterating to the next element of the array 4 | * @param array the array to loop 5 | * @param callbackFn the function to call on each element of the array 6 | */ 7 | public static async asyncForEach (array: Array, callbackFn: (item: T, index: number, arr: Array) => Promise): Promise { 8 | const totalLength = array.length 9 | for (let index = 0; index < totalLength; index++) { 10 | await callbackFn(array[index] as T, index, array) 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/src/app/libs/tools/date-tools.class.ts: -------------------------------------------------------------------------------- 1 | export class DateTools { 2 | public static getUtcIsoString (date: Date = new Date()): string { 3 | return new Date(date.toUTCString()).toISOString() 4 | } 5 | 6 | public static areEqual (date1: Date | string | undefined, date2: Date | string | undefined): boolean { 7 | const d1 = new Date(date1 || 0) 8 | const d2 = new Date(date2 || 0) 9 | return d1.getTime() === d2.getTime() 10 | } 11 | 12 | public static firstIsAfterSecond (date1: Date | string | undefined, date2: Date | string | undefined): boolean { 13 | const d1 = new Date(date1 || 0) 14 | const d2 = new Date(date2 || 0) 15 | return d1.getTime() > d2.getTime() 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /app/src/app/libs/tools/keyboard.class.ts: -------------------------------------------------------------------------------- 1 | export class Keyboard { 2 | public static isTyping (target: HTMLElement): boolean { 3 | return target.matches('input') || target.matches('textarea') 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /app/src/app/libs/tools/text-tools.class.ts: -------------------------------------------------------------------------------- 1 | export class TextTools { 2 | /* eslint-disable n/no-callback-literal */ 3 | // Removes non ascii characters from a string and replaces with ascii chars, plus replaces spaces with - 4 | public static cleanString (source: string): string { 5 | let r = source.toLowerCase() 6 | const nonAsciis = { 7 | a: '[àáâãäå]', 8 | ae: 'æ', 9 | c: 'ç', 10 | e: '[èéêë]', 11 | i: '[ìíîï]', 12 | n: 'ñ', 13 | o: '[òóôõö]', 14 | oe: 'œ', 15 | u: '[ùúûűü]', 16 | y: '[ýÿ]' 17 | } 18 | for (const i in nonAsciis) { 19 | r = r.replace(new RegExp(nonAsciis[i as keyof typeof nonAsciis], 'g'), i) 20 | } 21 | 22 | return r.replace(/\s/g, '-') 23 | .replace(/[^\w-]/gi, '') 24 | } 25 | 26 | /** 27 | * Converts a string from camelCase to kebab-case 28 | */ 29 | public static camelToKebabCase = (str: string) => str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`) 30 | 31 | /** 32 | * Capitalizes the first letter of a string 33 | */ 34 | public static capitalizeFirstLetter = (str: string) => str?.charAt(0).toUpperCase() + str.slice(1) 35 | 36 | /** 37 | * Returns a shortened version of a string 38 | */ 39 | public static shortenString = (str: string, maxLength: number = 20) => str.length > maxLength ? `${str.substring(0, maxLength)}...` : str 40 | } 41 | -------------------------------------------------------------------------------- /app/src/app/models/parent-element/parent-element.declarations.ts: -------------------------------------------------------------------------------- 1 | import { ISectionElement } from 'app/models/section-element/section-element.declarations' 2 | 3 | /** 4 | * Defines the properties of each parent to which a element is connected. 5 | */ 6 | export interface ParentInfoForDetailsView { 7 | txt: string 8 | sectionId: string 9 | element: ISectionElement 10 | } 11 | -------------------------------------------------------------------------------- /app/src/app/models/project/get-parent-info-for-details-view.class.ts: -------------------------------------------------------------------------------- 1 | import { ArrayTools } from 'app/libs/tools/array-tools.class' 2 | import { ParentInfoForDetailsView } from 'app/models/parent-element/parent-element.declarations' 3 | import { Project } from 'app/models/project/project.class' 4 | 5 | export class GetParentInfoForDetailsView { 6 | private parentInfoForDetailsView: Array = [] 7 | 8 | constructor ( 9 | private project: Project, 10 | private listOfParents: Array 11 | ) { } 12 | 13 | public async get (): Promise> { 14 | await ArrayTools.asyncForEach(this.listOfParents, async sectionIdElementId => await this.processElement(sectionIdElementId)) 15 | return this.parentInfoForDetailsView 16 | } 17 | 18 | private async processElement (sectionIdElementId: string): Promise { 19 | const arrInfo = sectionIdElementId.split('|') 20 | const section = this.project.getSectionById(arrInfo[0]) 21 | if (!section) { 22 | return 23 | } 24 | const element = await section.getElementById(arrInfo[1]) 25 | const formEle = section.getFirstUserDefinedField() 26 | this.parentInfoForDetailsView.push({ 27 | sectionId: arrInfo[0], 28 | element: element!, 29 | txt: element[formEle?.fieldName!] 30 | }) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /app/src/app/models/project/syncing/project-uploader.ts: -------------------------------------------------------------------------------- 1 | import { DropboxHelper } from 'app/libs/cloud-sync/dropbox/dropbox-helper.class' 2 | import { ProjectExporter, ExportScope } from 'app/models/project/project-exporter.class' 3 | import { TAnitaUniversalDataStorage } from 'app/models/project/project.declarations' 4 | 5 | export class ProjectUploader { 6 | private projectInJson: string | undefined 7 | private fileName: string | undefined 8 | 9 | constructor ( 10 | private systemData: Partial 11 | ) { 12 | this.fileName = `${this.systemData._settings?.[0].title}.json` 13 | } 14 | 15 | public async uploadToCloudService (path: string): Promise { 16 | this.projectInJson = await new ProjectExporter(this.systemData).getAsJson(ExportScope.all) 17 | DropboxHelper.instance.uploadFile(path, this.fileName!, this.projectInJson) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/app/models/project/syncing/sync-info.class.ts: -------------------------------------------------------------------------------- 1 | import { LOCAL_STORAGE_SYSTEMS } from 'app/data/local-dbs/local-storage-systems.enum' 2 | import { CloudSyncState } from 'app/libs/cloud-sync/cloud-sync.const' 3 | 4 | export class SyncInfo { 5 | private cloudSyncState: CloudSyncState | null = null 6 | private linkedFileId: string | null = null 7 | private localStorage: LOCAL_STORAGE_SYSTEMS | null = null 8 | 9 | public getCloudSyncState = (): CloudSyncState | null => this.cloudSyncState 10 | 11 | public getLinkedFileId = (): string | null => this.linkedFileId 12 | 13 | public getLocalStorage = (): LOCAL_STORAGE_SYSTEMS | null => this.localStorage 14 | 15 | public setCloudSyncState = (cloudSyncState: CloudSyncState | null): void => { 16 | this.cloudSyncState = cloudSyncState 17 | } 18 | 19 | public setLinkedFileId = (linkedFileId: string | null): void => { 20 | this.linkedFileId = linkedFileId 21 | } 22 | 23 | public setLocalStorage = (localStorage: LOCAL_STORAGE_SYSTEMS | null): void => { 24 | this.localStorage = localStorage 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/app/models/reserved-fields.constant.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Defines the fields name that are reserved for the functioning of Anita. 3 | * These fields names cannot be assigned as the value of the `fieldName` of sections' fields. 4 | */ 5 | export enum RESERVED_FIELDS { 6 | id = 'id', 7 | createdAt = 'createdAt', 8 | parentsInfo = 'parentsInfo', 9 | updatedAt = 'updatedAt', 10 | createdBy = 'createdBy' 11 | } 12 | -------------------------------------------------------------------------------- /app/src/app/models/section-element/section-element.declarations.ts: -------------------------------------------------------------------------------- 1 | import { RESERVED_FIELDS } from 'app/models/reserved-fields.constant' 2 | 3 | /** 4 | * Defines the bare minimum fields of an `Element` of a `Section`. 5 | */ 6 | export interface ISectionElement { 7 | [RESERVED_FIELDS.id]?: string 8 | [RESERVED_FIELDS.createdAt]?: string 9 | [RESERVED_FIELDS.createdBy]?: string 10 | [RESERVED_FIELDS.parentsInfo]?: Array 11 | [RESERVED_FIELDS.updatedAt]?: string 12 | [key: string]: any 13 | } 14 | -------------------------------------------------------------------------------- /app/src/app/models/section/view-settings.class.ts: -------------------------------------------------------------------------------- 1 | import { ISectionViewSettings } from 'app/models/section/section.declarations' 2 | import { SupportedViews } from 'app/models/section/view-settings.const' 3 | 4 | export class ViewSettings { 5 | constructor ( 6 | private viewSettings: ISectionViewSettings = {} as ISectionViewSettings 7 | ) { } 8 | 9 | public getPreferredView (): SupportedViews { 10 | return this.viewSettings.preferredView || SupportedViews.table 11 | } 12 | 13 | public setPreferredView (view: SupportedViews) { 14 | this.viewSettings.preferredView = view 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/app/models/section/view-settings.const.ts: -------------------------------------------------------------------------------- 1 | import { TIconName } from 'app/libs/icons/icons.class' 2 | 3 | export enum SupportedViews { 4 | table, 5 | grid 6 | } 7 | 8 | export const SUPPORTED_VIEWS_ICONS: Array = ['table', 'gridOutline'] 9 | -------------------------------------------------------------------------------- /app/src/app/workers/comparator.worker.ts: -------------------------------------------------------------------------------- 1 | import { TAnitaUniversalDataStorage } from 'app/models/project/project.declarations' 2 | import { Comparator } from 'app/models/project/syncing/project-comparator' 3 | 4 | interface IComparatorWorkerPayload { 5 | lastSync: string | undefined 6 | localData: TAnitaUniversalDataStorage 7 | remoteData: TAnitaUniversalDataStorage 8 | } 9 | 10 | self.onmessage = async (event: MessageEvent) => { 11 | const { lastSync, localData, remoteData } = event.data 12 | const comparisonResult = new Comparator(lastSync, localData, remoteData).compare() 13 | self.postMessage(comparisonResult) 14 | } 15 | -------------------------------------------------------------------------------- /app/src/index.tsx: -------------------------------------------------------------------------------- 1 | import 'animate.css' 2 | import { store } from 'app/libs/redux/state.store' 3 | import { Startupper } from 'app/libs/startupper/startupper.class' 4 | import { AdminLayout } from 'app/components/admin-layout/admin-layout.component' 5 | import React from 'react' 6 | import { HashRouter as Router } from 'react-router-dom' 7 | import { Provider as StoreProvider } from 'react-redux' 8 | import './index.css' 9 | import { ModalProvider } from 'app/components/shared-components/modals/modal.component' 10 | import * as ReactDOMClient from 'react-dom/client' 11 | import * as serviceWorkerRegistration from './serviceWorkerRegistration' 12 | import 'tippy.js/dist/tippy.css' 13 | 14 | new Startupper().init() 15 | const rootElement = document.getElementById('root') 16 | const root = ReactDOMClient.createRoot(rootElement!) 17 | root.render( 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | ) 28 | 29 | serviceWorkerRegistration.register() 30 | -------------------------------------------------------------------------------- /app/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /app/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals' 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry) 7 | getFID(onPerfEntry) 8 | getFCP(onPerfEntry) 9 | getLCP(onPerfEntry) 10 | getTTFB(onPerfEntry) 11 | }) 12 | } 13 | } 14 | 15 | export default reportWebVitals 16 | -------------------------------------------------------------------------------- /app/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom' 6 | -------------------------------------------------------------------------------- /app/src/types/date-format.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'date-format' 2 | -------------------------------------------------------------------------------- /app/src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | export {} 2 | 3 | declare global { 4 | interface Window { 5 | showOpenFilePicker: () => void 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /app/tailwind.config.js: -------------------------------------------------------------------------------- 1 | const colors = require('tailwindcss/colors') 2 | 3 | module.exports = { 4 | content: ['./src/**/*.{js,jsx,ts,tsx}', './public/index.html'], 5 | theme: { 6 | colors: { 7 | transparent: 'transparent', 8 | current: 'currentColor', 9 | black: colors.black, 10 | white: colors.white, 11 | rose: colors.rose, 12 | pink: colors.pink, 13 | fuchsia: colors.fuchsia, 14 | purple: colors.purple, 15 | violet: colors.violet, 16 | indigo: colors.indigo, 17 | blue: colors.blue, 18 | 'prussian-blue': { 19 | DEFAULT: '#002346', 20 | 50: '#2D96FF', 21 | 100: '#1389FF', 22 | 200: '#006FDF', 23 | 300: '#0056AC', 24 | 400: '#003C79', 25 | 500: '#002346', 26 | 600: '#002346', 27 | 700: '#000f20', 28 | 800: '#000e1f', 29 | 900: '#000d1b' 30 | }, 31 | sky: colors.sky, 32 | cyan: colors.cyan, 33 | teal: colors.teal, 34 | emerald: colors.emerald, 35 | green: colors.green, 36 | lime: colors.lime, 37 | yellow: colors.yellow, 38 | amber: colors.amber, 39 | orange: colors.orange, 40 | red: colors.red, 41 | stone: colors.stone, 42 | neutral: colors.neutral, 43 | gray: colors.gray, 44 | slate: colors.slate 45 | } 46 | }, 47 | variants: { 48 | extend: {} 49 | }, 50 | plugins: [ 51 | require('@tailwindcss/forms') 52 | ] 53 | } 54 | -------------------------------------------------------------------------------- /app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "lib": [ 5 | "dom", 6 | "dom.iterable", 7 | "esnext" 8 | ], 9 | "allowJs": true, 10 | "skipLibCheck": true, 11 | "esModuleInterop": true, 12 | "allowSyntheticDefaultImports": true, 13 | "strict": true, 14 | "strictPropertyInitialization": true, 15 | "strictNullChecks": true, 16 | "forceConsistentCasingInFileNames": true, 17 | "noFallthroughCasesInSwitch": true, 18 | "module": "esnext", 19 | "moduleResolution": "node", 20 | "resolveJsonModule": true, 21 | "isolatedModules": true, 22 | "noEmit": true, 23 | "jsx": "react-jsx", 24 | "baseUrl": "src" 25 | }, 26 | "include": [ 27 | "src/**/*" 28 | ] 29 | } -------------------------------------------------------------------------------- /assets/AnitaLogo.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/assets/AnitaLogo.sketch -------------------------------------------------------------------------------- /assets/DataSync.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/assets/DataSync.sketch -------------------------------------------------------------------------------- /assets/LandingImages.sketch: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/assets/LandingImages.sketch -------------------------------------------------------------------------------- /landing/.vscode/ltex.disabledRules.en-US.txt: -------------------------------------------------------------------------------- 1 | LIBRARY 2 | -------------------------------------------------------------------------------- /landing/config/default.ts: -------------------------------------------------------------------------------- 1 | import { YassbConfig } from 'yassb-web'; 2 | import { DateFormatterDirective } from '../src/app/yassb-plugins/custom-directives/date-formatter-directive.class'; 3 | import { blogList } from '../src/app/yassb-plugins/custom-renderers/blog-list'; 4 | import { packagesList } from '../src/app/yassb-plugins/custom-renderers/packages-list'; 5 | import { addDivToPreCode } from '../src/app/yassb-plugins/post-processors/add-div-to-pre-code.function'; 6 | import { buildTailwind } from '../src/app/yassb-plugins/styles-parser/build-tailwind.function'; 7 | 8 | export default { 9 | workingDir: { 10 | out: '../anita-app.github.io', 11 | styles: 'styles/styles.css' 12 | }, 13 | stylesParser: buildTailwind, 14 | customRenderers: { 15 | blogList: blogList, 16 | packagesList: packagesList 17 | }, 18 | customDirectives: [DateFormatterDirective], 19 | htmlMinificationOptions: { 20 | removeAttributeQuotes: false, 21 | collapseWhitespace: true, 22 | collapseInlineTagWhitespace: true, 23 | conservativeCollapse: true, 24 | minifyCSS: true, 25 | minifyJS: true, 26 | removeComments: true 27 | }, 28 | grayMatterOption: { 29 | excerpt: true, 30 | excerpt_separator: '' 31 | }, 32 | postProcessors: [addDivToPreCode] 33 | } as YassbConfig; 34 | -------------------------------------------------------------------------------- /landing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "anita-landing", 3 | "description": "Anita landing page", 4 | "version": "0.20.0", 5 | "author": "ilDon", 6 | "scripts": { 7 | "licenses": "npx license-checker --production --json --out src/app/data-sources/landing-packages.json", 8 | "watch": "yassb watch", 9 | "build": "yassb build", 10 | "serve": "yassb serve", 11 | "deploy": "cglg --action=newRelease && npm run do-no-run-this-deploy-directly && cglg --action=closeRelease", 12 | "do-no-run-this-deploy-directly": "npm run build" 13 | }, 14 | "dependencies": { 15 | "showdown": "^1.9.1" 16 | }, 17 | "devDependencies": { 18 | "@tailwindcss/typography": "^0.5.7", 19 | "@types/autoprefixer": "^10.2.0", 20 | "@types/clean-css": "^4.2.5", 21 | "@types/showdown": "^1.9.4", 22 | "@types/tailwindcss": "^2.2.1", 23 | "autoprefixer": "^10.4.8", 24 | "postcss": "^8.4.16", 25 | "react": "^17.0.2", 26 | "react-dom": "^17.0.2", 27 | "tailwindcss": "^3.1.8", 28 | "typescript": "^4.4.4", 29 | "yassb-web": "^1.3.0" 30 | }, 31 | "license": "CC-BY-NC-4.0" 32 | } 33 | -------------------------------------------------------------------------------- /landing/src/app/components/get-started.component.html: -------------------------------------------------------------------------------- 1 | 2 | Get startedBeta 3 | -------------------------------------------------------------------------------- /landing/src/app/components/head/head.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /landing/src/app/components/instructions/builds.md: -------------------------------------------------------------------------------- 1 | yassb build 2 | or 3 | 4 | yassb watch // w/ live reloading! -------------------------------------------------------------------------------- /landing/src/app/components/instructions/installation.md: -------------------------------------------------------------------------------- 1 | npm i -g yassb-web 2 | or 3 | 4 | yarn global add yassb-web -------------------------------------------------------------------------------- /landing/src/app/components/instructions/new-project.md: -------------------------------------------------------------------------------- 1 | yassb new myStaticWebSite 2 | 3 | and 4 | 5 | cd myStaticWebSite -------------------------------------------------------------------------------- /landing/src/app/components/landing/live-demo.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |

Try it now

6 |

A live demo is worth a thousand pictures.

7 |

In the box below Anita is already up and running. You can start trying it out, all data will be saved on your device.

8 |
9 |
10 | 11 |
12 |
13 |
-------------------------------------------------------------------------------- /landing/src/app/pages/blog/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Anita Blog 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 |
18 |
19 |

From the blog

20 |

Articles, guides, in-depth analysis of how Anita works, and anything that is related and relevant for Anita.

21 |
22 |
23 | 24 |
25 |
26 |
27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /landing/src/app/pages/licenses.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Anita ❤ open source 7 | 8 | 9 | 10 | 11 | 12 | 13 |
14 |
15 |
16 |
17 | 18 |

Anita is made possible thanks to the following open source software:

19 |
    20 |
  • Landing page: 21 | 22 |
  • 23 |
  • Progressive web app: 24 | 25 |
  • 26 |
27 |
28 |
29 |
30 |
31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /landing/src/app/scripts/main.ts: -------------------------------------------------------------------------------- 1 | import { Typewriter } from './typewriter.class'; 2 | import { multiplyHeadsAsBodyRows } from './responsive-table/multiply-heads-as-body-rows.function'; 3 | import { setHeights } from './responsive-table/set-heights'; 4 | 5 | window['toggleNavbar'] = collapseID => { 6 | document.getElementById(collapseID).classList.toggle('hidden'); 7 | document.getElementById(collapseID).classList.toggle('block'); 8 | }; 9 | 10 | 11 | multiplyHeadsAsBodyRows(); 12 | 13 | window.onresize = setHeights; 14 | 15 | // on window ready 16 | window.onload = () => { 17 | Typewriter.start() 18 | } -------------------------------------------------------------------------------- /landing/src/app/yassb-plugins/custom-directives/months.const.ts: -------------------------------------------------------------------------------- 1 | export const MONTHS = [ 2 | 'January', 3 | 'February', 4 | 'March', 5 | 'April', 6 | 'May', 7 | 'June', 8 | 'July', 9 | 'August', 10 | 'September', 11 | 'October', 12 | 'November', 13 | 'December' 14 | ] 15 | -------------------------------------------------------------------------------- /landing/src/app/yassb-plugins/custom-renderers/sort-blogs-by-date.ts: -------------------------------------------------------------------------------- 1 | import { FilePathsForPublicFileList, FrontMatterDataStore } from 'yassb-web'; 2 | 3 | export function sortBlogsByDate(source: FilePathsForPublicFileList[], frontMatterStore: FrontMatterDataStore) { 4 | source.sort((eleA, eleB) => { 5 | const dataA = frontMatterStore[eleA.absolutePath]; 6 | const dataB = frontMatterStore[eleB.absolutePath]; 7 | 8 | if (dataA.date < dataB.date) { 9 | return 1; 10 | } else if (dataA.date > dataB.date) { 11 | return -1; 12 | } else { 13 | return 0; 14 | } 15 | }); 16 | } 17 | 18 | -------------------------------------------------------------------------------- /landing/src/app/yassb-plugins/post-processors/add-div-to-pre-code.function.ts: -------------------------------------------------------------------------------- 1 | export function addDivToPreCode(content: string): string { 2 | content = content.replace(/
\s*\n*/g, '
')
3 |   content = content.replace(/<\/code>\s*\n*<\/pre>/g, '
') 4 | return content; 5 | } -------------------------------------------------------------------------------- /landing/src/app/yassb-plugins/styles-parser/build-tailwind.function.ts: -------------------------------------------------------------------------------- 1 | import autoprefixer from 'autoprefixer'; 2 | import CleanCSS from 'clean-css'; 3 | import { readFileSync } from 'fs-extra'; 4 | import postcss from 'postcss'; 5 | import tailwindcss from 'tailwindcss'; 6 | 7 | export const buildTailwind = async (pathToCssFile, from, to): Promise => { 8 | // process.env.NODE_ENV = 'production'; 9 | const css = readFileSync(pathToCssFile); 10 | const result = await postcss([tailwindcss, autoprefixer]).process(css, { 11 | from, 12 | to, 13 | map: false 14 | }); 15 | 16 | const minified = new CleanCSS().minify(result.css); 17 | return minified.styles; 18 | }; 19 | -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-brands-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-brands-400.eot -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-brands-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-brands-400.woff -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-regular-400.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-regular-400.eot -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-regular-400.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-regular-400.woff -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-solid-900.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-solid-900.eot -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-solid-900.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-solid-900.woff -------------------------------------------------------------------------------- /landing/src/assets/fontawesome-free/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/fontawesome-free/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/android-chrome-192x192.png -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/android-chrome-512x512.png -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/apple-touch-icon.png -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #002346 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/favicon.ico -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/icons/favicon/mstile-150x150.png -------------------------------------------------------------------------------- /landing/src/assets/icons/favicon/site.webmanifest: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Anita", 3 | "short_name": "Anita", 4 | "icons": [ 5 | { 6 | "src": "/assets/icons/favicon/android-chrome-192x192.png", 7 | "sizes": "192x192", 8 | "type": "image/png" 9 | }, 10 | { 11 | "src": "/assets/icons/favicon/android-chrome-512x512.png", 12 | "sizes": "512x512", 13 | "type": "image/png" 14 | } 15 | ], 16 | "theme_color": "#002346", 17 | "background_color": "#002346", 18 | "display": "standalone" 19 | } -------------------------------------------------------------------------------- /landing/src/assets/images/abstract-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/abstract-table.png -------------------------------------------------------------------------------- /landing/src/assets/images/blog/a-simpler-way-to-use-tippyjs-with-react/a-simpler-way-to-use-tippyjs-with-react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/a-simpler-way-to-use-tippyjs-with-react/a-simpler-way-to-use-tippyjs-with-react.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/data-portability-is-a-hard-problem/data-portability-is-a-hard-problem.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/data-portability-is-a-hard-problem/data-portability-is-a-hard-problem.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/hosting-pwa-for-free/hosting-pwa-for-free.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/hosting-pwa-for-free/hosting-pwa-for-free.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/most-secure-cloud/data_centre.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/most-secure-cloud/data_centre.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/most-secure-cloud/most-secure-cloud.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/most-secure-cloud/most-secure-cloud.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/2021-11-10_app_initialized.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/2021-11-10_app_initialized.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/2021-11-10_tailwind.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/2021-11-10_tailwind.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/2021-11-21_final_result.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/2021-11-21_final_result.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/2021-11-21_final_result_with_project.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/2021-11-21_final_result_with_project.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/Anita_angular_no_projects.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/Anita_angular_no_projects.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/Anita_angular_projects_list.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/Anita_angular_projects_list.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/porting/porting-anita-from-angular-to-react.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/porting/porting-anita-from-angular-to-react.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/sqlite-in-a-pwa/database-free.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/sqlite-in-a-pwa/database-free.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/switch-with-types/2020-02-01_clearElement-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/switch-with-types/2020-02-01_clearElement-example.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/switch-with-types/2020-02-01_meme-code-working.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/switch-with-types/2020-02-01_meme-code-working.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/switch-with-types/2020-02-01_setElement-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/switch-with-types/2020-02-01_setElement-example.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/switch-with-types/2020-02-01_updateElement-example.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/switch-with-types/2020-02-01_updateElement-example.jpg -------------------------------------------------------------------------------- /landing/src/assets/images/blog/switch-with-types/switches.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/anita-app/anita/dc14c7cd47613f580ec866b743a660a595c579d6/landing/src/assets/images/blog/switch-with-types/switches.jpg -------------------------------------------------------------------------------- /landing/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "angularCompilerOptions": { 4 | "strictTemplates": true 5 | }, 6 | "compilerOptions": { 7 | "jsx": "react", 8 | "baseUrl": "./", 9 | "outDir": "./anita-app.github.io/out-tsc", 10 | "sourceMap": false, 11 | "declaration": false, 12 | "moduleResolution": "node", 13 | "experimentalDecorators": true, 14 | "allowSyntheticDefaultImports": true, 15 | "esModuleInterop": true, 16 | "target": "es5", 17 | "typeRoots": [ 18 | "node_modules/@types" 19 | ], 20 | "lib": [ 21 | "es2017", 22 | "dom" 23 | ], 24 | "paths": { 25 | "@anita/landing/*": [ 26 | "src/app/*" 27 | ] 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /version-propagator.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs-extra'); 2 | 3 | function start() { 4 | const currentJson = fs.readJSONSync('./package.json'); 5 | updateChildPackage('app', currentJson.version); 6 | updateChildPackage('landing', currentJson.version); 7 | storeVersionInTsFile('app', currentJson.version); 8 | storeVersionInTsFile('landing', currentJson.version); 9 | } 10 | 11 | function updateChildPackage(folder, version) { 12 | const file = `./${folder}/package.json`; 13 | const child = fs.readJSONSync(file); 14 | child.version = version; 15 | fs.writeJSONSync(file, child, { spaces: 2 }); 16 | } 17 | 18 | function storeVersionInTsFile(folder, version) { 19 | const file = `./${folder}/src/app/version.ts`; 20 | const contents = `/**\n * Auto generated file, do not edit.\n */\n\nexport const appVersion = '${version}'\n`; 21 | fs.writeFileSync(file, contents); 22 | } 23 | 24 | start() --------------------------------------------------------------------------------