refactor: native feature management (#2350)

This commit is contained in:
Mo
2023-07-12 12:56:08 -05:00
committed by GitHub
parent 49f7581cd8
commit 078ef3772c
223 changed files with 3996 additions and 3438 deletions

View File

@@ -1,8 +1,8 @@
import { ComponentPermission } from '@standardnotes/features'
import { SNComponent } from '../../Syncable/Component'
import { ComponentInterface } from '../../Syncable/Component'
export type PermissionDialog = {
component: SNComponent
component: ComponentInterface
permissions: ComponentPermission[]
permissionsString: string
actionBlock: (approved: boolean) => void

View File

@@ -5,7 +5,7 @@ import { DecryptedItemInterface } from './DecryptedItem'
import { isDecryptedPayload, isDeletedPayload, isEncryptedPayload } from '../../Payload/Interfaces/TypeCheck'
export function isDecryptedItem(item: ItemInterface): item is DecryptedItemInterface {
return isDecryptedPayload(item.payload)
return 'payload' in item && isDecryptedPayload(item.payload)
}
export function isEncryptedItem(item: ItemInterface): item is EncryptedItemInterface {

View File

@@ -19,7 +19,7 @@ export class DecryptedItemMutator<
constructor(item: I, type: MutationType) {
super(item, type)
const mutableCopy = Copy(this.immutablePayload.content)
const mutableCopy = Copy<C>(this.immutablePayload.content)
this.mutableContent = mutableCopy
}

View File

@@ -58,7 +58,7 @@ describe('component model', () => {
package_info: {
note_type: NoteType.Authentication,
},
} as ComponentContent),
} as unknown as ComponentContent),
...PayloadTimestampDefaults(),
},
PayloadSource.Constructor,

View File

@@ -7,9 +7,11 @@ import {
ComponentPermission,
FindNativeFeature,
NoteType,
isEditorFeatureDescription,
} from '@standardnotes/features'
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { ComponentContent, ComponentInterface } from './ComponentContent'
import { ComponentContent } from './ComponentContent'
import { ComponentInterface } from './ComponentInterface'
import { ConflictStrategy } from '../../Abstract/Item/Types/ConflictStrategy'
import { DecryptedItem } from '../../Abstract/Item/Implementations/DecryptedItem'
import { DecryptedPayloadInterface } from '../../Abstract/Payload/Interfaces/DecryptedPayload'
@@ -19,12 +21,24 @@ import { Predicate } from '../../Runtime/Predicate/Predicate'
import { ItemInterface } from '../../Abstract/Item/Interfaces/ItemInterface'
import { DecryptedItemInterface } from './../../Abstract/Item/Interfaces/DecryptedItem'
import { ComponentPackageInfo } from './PackageInfo'
import { isDecryptedItem } from '../../Abstract/Item'
import { ContentType } from '@standardnotes/domain-core'
export const isComponent = (x: ItemInterface): x is SNComponent => x.content_type === ContentType.TYPES.Component
export function isComponent(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}
export const isComponentOrTheme = (x: ItemInterface): x is SNComponent =>
x.content_type === ContentType.TYPES.Component || x.content_type === ContentType.TYPES.Theme
return x.content_type === ContentType.TYPES.Component
}
export function isComponentOrTheme(x: ItemInterface): x is ComponentInterface {
if (!isDecryptedItem(x as DecryptedItemInterface)) {
return false
}
return x.content_type === ContentType.TYPES.Component || x.content_type === ContentType.TYPES.Theme
}
/**
* Components are mostly iframe based extensions that communicate with the SN parent
@@ -32,7 +46,7 @@ export const isComponentOrTheme = (x: ItemInterface): x is SNComponent =>
* only by its url.
*/
export class SNComponent extends DecryptedItem<ComponentContent> implements ComponentInterface {
public readonly componentData: Record<string, unknown>
public readonly legacyComponentData: Record<string, unknown>
/** Items that have requested a component to be disabled in its context */
public readonly disassociatedItemIds: string[]
/** Items that have requested a component to be enabled in its context */
@@ -46,14 +60,12 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
public readonly area: ComponentArea
public readonly permissions: ComponentPermission[] = []
public readonly valid_until: Date
public readonly active: boolean
public readonly legacyActive: boolean
public readonly legacy_url?: string
public readonly isMobileDefault: boolean
constructor(payload: DecryptedPayloadInterface<ComponentContent>) {
super(payload)
/** Custom data that a component can store in itself */
this.componentData = this.payload.content.componentData || {}
if (payload.content.hosted_url && isValidUrl(payload.content.hosted_url)) {
this.hosted_url = payload.content.hosted_url
@@ -65,16 +77,16 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
this.local_url = payload.content.local_url
this.valid_until = new Date(payload.content.valid_until || 0)
this.offlineOnly = payload.content.offlineOnly
this.offlineOnly = payload.content.offlineOnly ?? false
this.name = payload.content.name
this.area = payload.content.area
this.package_info = payload.content.package_info || {}
this.permissions = payload.content.permissions || []
this.active = payload.content.active
this.autoupdateDisabled = payload.content.autoupdateDisabled
this.autoupdateDisabled = payload.content.autoupdateDisabled ?? false
this.disassociatedItemIds = payload.content.disassociatedItemIds || []
this.associatedItemIds = payload.content.associatedItemIds || []
this.isMobileDefault = payload.content.isMobileDefault
this.isMobileDefault = payload.content.isMobileDefault ?? false
/**
* @legacy
* We don't want to set this.url directly, as we'd like to phase it out.
@@ -83,6 +95,10 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
* hosted_url is the url replacement.
*/
this.legacy_url = !payload.content.hosted_url ? payload.content.url : undefined
this.legacyComponentData = this.payload.content.componentData || {}
this.legacyActive = payload.content.active ?? false
}
/** Do not duplicate components under most circumstances. Always keep original */
@@ -123,18 +139,6 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
return this.getAppDomainValue(AppDataField.LastSize)
}
/**
* The key used to look up data that this component may have saved to an item.
* This data will be stored on the item using this key.
*/
public getClientDataKey(): string {
if (this.legacy_url) {
return this.legacy_url
} else {
return this.uuid
}
}
public hasValidHostedUrl(): boolean {
return (this.hosted_url || this.legacy_url) != undefined
}
@@ -180,7 +184,11 @@ export class SNComponent extends DecryptedItem<ComponentContent> implements Comp
}
public get noteType(): NoteType {
return this.package_info.note_type || NoteType.Unknown
if (isEditorFeatureDescription(this.package_info)) {
return this.package_info.note_type ?? NoteType.Unknown
}
return NoteType.Unknown
}
public get isDeprecated(): boolean {

View File

@@ -1,35 +1,40 @@
import { ComponentArea, ComponentPermission } from '@standardnotes/features'
import { ItemContent } from '../../Abstract/Content/ItemContent'
import { ComponentPackageInfo } from './PackageInfo'
import { ItemContent } from '../../Abstract/Content/ItemContent'
/* eslint-disable @typescript-eslint/no-explicit-any */
export interface ComponentInterface {
componentData: Record<string, any>
export type ComponentContentSpecialized = {
/** Items that have requested a component to be disabled in its context */
disassociatedItemIds: string[]
disassociatedItemIds?: string[]
/** Items that have requested a component to be enabled in its context */
associatedItemIds: string[]
associatedItemIds?: string[]
local_url?: string
hosted_url?: string
offlineOnly?: boolean
name: string
autoupdateDisabled?: boolean
package_info: ComponentPackageInfo
area: ComponentArea
permissions?: ComponentPermission[]
valid_until: Date | number
legacy_url?: string
isMobileDefault?: boolean
isDeprecated?: boolean
/** @deprecated */
active?: boolean
/** @deprecated */
url?: string
offlineOnly: boolean
name: string
autoupdateDisabled: boolean
package_info: ComponentPackageInfo
area: ComponentArea
permissions: ComponentPermission[]
valid_until: Date | number
active: boolean
legacy_url?: string
isMobileDefault: boolean
isDeprecated: boolean
isExplicitlyEnabledForItem(uuid: string): boolean
/**
* @deprecated
* Replaced with per-note component data stored in the note's ComponentDataDomain.
*/
componentData?: Record<string, unknown>
}
export type ComponentContent = ComponentInterface & ItemContent
export type ComponentContent = ItemContent & ComponentContentSpecialized

View File

@@ -0,0 +1,63 @@
import {
ComponentArea,
ComponentPermission,
FeatureIdentifier,
NoteType,
ThirdPartyFeatureDescription,
} from '@standardnotes/features'
import { ComponentPackageInfo } from './PackageInfo'
import { DecryptedItemInterface } from '../../Abstract/Item'
import { ComponentContent } from './ComponentContent'
export interface ComponentInterface extends DecryptedItemInterface<ComponentContent> {
/** Items that have requested a component to be disabled in its context */
disassociatedItemIds: string[]
/** Items that have requested a component to be enabled in its context */
associatedItemIds: string[]
local_url?: string
hosted_url?: string
offlineOnly: boolean
name: string
autoupdateDisabled: boolean
package_info: ComponentPackageInfo
area: ComponentArea
permissions: ComponentPermission[]
valid_until: Date
isMobileDefault: boolean
isDeprecated: boolean
isExplicitlyEnabledForItem(uuid: string): boolean
hasValidHostedUrl(): boolean
isTheme(): boolean
isExplicitlyDisabledForItem(uuid: string): boolean
legacyIsDefaultEditor(): boolean
get identifier(): FeatureIdentifier
get noteType(): NoteType
get displayName(): string
get deprecationMessage(): string | undefined
get thirdPartyPackageInfo(): ThirdPartyFeatureDescription
get isExpired(): boolean
/**
* @deprecated
* Replaced with active preferences managed by preferences service.
*/
legacyActive: boolean
/** @deprecated */
legacy_url?: string
/** @deprecated */
url?: string
/**
* @deprecated
* Replaced with per-note component data stored in the note's ComponentDataDomain.
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
legacyComponentData: Record<string, any>
}

View File

@@ -1,23 +1,15 @@
import { addIfUnique, removeFromArray } from '@standardnotes/utils'
import { ComponentPermission, FeatureDescription } from '@standardnotes/features'
import { ComponentFeatureDescription, ComponentPermission } from '@standardnotes/features'
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { ComponentContent } from './ComponentContent'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
export class ComponentMutator extends DecryptedItemMutator<ComponentContent> {
set active(active: boolean) {
this.mutableContent.active = active
}
set isMobileDefault(isMobileDefault: boolean) {
this.mutableContent.isMobileDefault = isMobileDefault
}
set componentData(componentData: Record<string, unknown>) {
this.mutableContent.componentData = componentData
}
set package_info(package_info: FeatureDescription) {
set package_info(package_info: ComponentFeatureDescription) {
this.mutableContent.package_info = package_info
}

View File

@@ -0,0 +1,172 @@
import {
AnyFeatureDescription,
ComponentArea,
ComponentPermission,
EditorFeatureDescription,
FeatureIdentifier,
IframeComponentFeatureDescription,
NoteType,
ThemeDockIcon,
UIFeatureDescriptionTypes,
isEditorFeatureDescription,
isIframeComponentFeatureDescription,
isThemeFeatureDescription,
} from '@standardnotes/features'
import { ComponentInterface } from './ComponentInterface'
import { isTheme } from '../Theme'
function isComponent(x: ComponentInterface | UIFeatureDescriptionTypes): x is ComponentInterface {
return 'uuid' in x
}
function isFeatureDescription(x: ComponentInterface | AnyFeatureDescription): x is AnyFeatureDescription {
return !('uuid' in x)
}
export function isIframeUIFeature(
x: ComponentOrNativeFeature<EditorFeatureDescription | IframeComponentFeatureDescription>,
): x is ComponentOrNativeFeature<IframeComponentFeatureDescription> {
return isIframeComponentFeatureDescription(x.featureDescription)
}
export class ComponentOrNativeFeature<F extends UIFeatureDescriptionTypes> {
constructor(public readonly item: ComponentInterface | F) {}
get isComponent(): boolean {
return isComponent(this.item)
}
get isFeatureDescription(): boolean {
return isFeatureDescription(this.item)
}
get isThemeComponent(): boolean {
return isComponent(this.item) && isTheme(this.item)
}
get asComponent(): ComponentInterface {
if (isComponent(this.item)) {
return this.item
}
throw new Error('Cannot cast item to component')
}
get asFeatureDescription(): F {
if (isFeatureDescription(this.item)) {
return this.item
}
throw new Error('Cannot cast item to feature description')
}
get uniqueIdentifier(): string {
if (isFeatureDescription(this.item)) {
return this.item.identifier
} else {
return this.item.uuid
}
}
get featureIdentifier(): FeatureIdentifier {
return this.item.identifier
}
get noteType(): NoteType {
if (isFeatureDescription(this.item) && isEditorFeatureDescription(this.item)) {
return this.item.note_type ?? NoteType.Unknown
} else if (isComponent(this.item)) {
return this.item.noteType
}
throw new Error('Invalid component or feature description')
}
get fileType(): EditorFeatureDescription['file_type'] {
if (isFeatureDescription(this.item) && isEditorFeatureDescription(this.item)) {
return this.item.file_type
} else if (isComponent(this.item) && isEditorFeatureDescription(this.item.package_info)) {
return this.item.package_info?.file_type ?? 'txt'
}
throw new Error('Invalid component or feature description')
}
get displayName(): string {
if (isFeatureDescription(this.item)) {
return this.item.name ?? ''
} else {
return this.item.displayName
}
}
get description(): string {
if (isFeatureDescription(this.item)) {
return this.item.description ?? ''
} else {
return this.item.package_info.description ?? ''
}
}
get deprecationMessage(): string | undefined {
if (isFeatureDescription(this.item)) {
return this.item.deprecation_message
} else {
return this.item.deprecationMessage
}
}
get expirationDate(): Date | undefined {
if (isFeatureDescription(this.item)) {
return this.item.expires_at ? new Date(this.item.expires_at) : undefined
} else {
return this.item.valid_until
}
}
get featureDescription(): F {
if (isFeatureDescription(this.item)) {
return this.item
} else {
return this.item.package_info as F
}
}
get acquiredPermissions(): ComponentPermission[] {
if (isFeatureDescription(this.item) && isIframeComponentFeatureDescription(this.item)) {
return this.item.component_permissions ?? []
} else if (isComponent(this.item)) {
return this.item.permissions
}
throw new Error('Invalid component or feature description')
}
get area(): ComponentArea {
if ('area' in this.item) {
return this.item.area
}
return ComponentArea.Editor
}
get layerable(): boolean {
if (isComponent(this.item) && isTheme(this.item)) {
return this.item.layerable
} else if (isThemeFeatureDescription(this.asFeatureDescription)) {
return this.asFeatureDescription.layerable ?? false
}
return false
}
get dockIcon(): ThemeDockIcon | undefined {
if (isComponent(this.item) && isTheme(this.item)) {
return this.item.package_info.dock_icon
} else if (isThemeFeatureDescription(this.asFeatureDescription)) {
return this.asFeatureDescription.dock_icon
}
return undefined
}
}

View File

@@ -1,9 +1,9 @@
import { FeatureDescription, ThemeFeatureDescription } from '@standardnotes/features'
import { ComponentFeatureDescription, ThemeFeatureDescription } from '@standardnotes/features'
type ThirdPartyPackageInfo = {
version: string
download_url?: string
}
export type ComponentPackageInfo = FeatureDescription & Partial<ThirdPartyPackageInfo>
export type ThemePackageInfo = FeatureDescription & Partial<ThirdPartyPackageInfo> & ThemeFeatureDescription
export type ComponentPackageInfo = ComponentFeatureDescription & Partial<ThirdPartyPackageInfo>
export type ThemePackageInfo = ThemeFeatureDescription & Partial<ThirdPartyPackageInfo> & ThemeFeatureDescription

View File

@@ -1,3 +1,6 @@
export * from './Component'
export * from './ComponentMutator'
export * from './ComponentContent'
export * from './ComponentInterface'
export * from './ComponentOrNativeFeature'
export * from './PackageInfo'

View File

@@ -1,50 +1,18 @@
import { ComponentArea } from '@standardnotes/features'
import { SNComponent } from '../Component/Component'
import { ConflictStrategy } from '../../Abstract/Item/Types/ConflictStrategy'
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { HistoryEntryInterface } from '../../Runtime/History'
import { DecryptedItemInterface, ItemInterface } from '../../Abstract/Item'
import { ItemInterface } from '../../Abstract/Item'
import { ContentType } from '@standardnotes/domain-core'
import { useBoolean } from '@standardnotes/utils'
import { ThemePackageInfo } from '../Component/PackageInfo'
import { ContentType } from '@standardnotes/domain-core'
import { ThemeInterface } from './ThemeInterface'
export const isTheme = (x: ItemInterface): x is SNTheme => x.content_type === ContentType.TYPES.Theme
export const isTheme = (x: ItemInterface): x is ThemeInterface => x.content_type === ContentType.TYPES.Theme
export class SNTheme extends SNComponent {
export class SNTheme extends SNComponent implements ThemeInterface {
public override area: ComponentArea = ComponentArea.Themes
public declare readonly package_info: ThemePackageInfo
isLayerable(): boolean {
get layerable(): boolean {
return useBoolean(this.package_info && this.package_info.layerable, false)
}
/** Do not duplicate under most circumstances. Always keep original */
override strategyWhenConflictingWithItem(
_item: DecryptedItemInterface,
_previousRevision?: HistoryEntryInterface,
): ConflictStrategy {
return ConflictStrategy.KeepBase
}
getMobileRules() {
return (
this.getAppDomainValue(AppDataField.MobileRules) || {
constants: {},
rules: {},
}
)
}
/** Same as getMobileRules but without default value. */
hasMobileRules() {
return this.getAppDomainValue(AppDataField.MobileRules)
}
getNotAvailOnMobile() {
return this.getAppDomainValue(AppDataField.NotAvailableOnMobile)
}
isMobileActive() {
return this.getAppDomainValue(AppDataField.MobileActive)
}
}

View File

@@ -0,0 +1,7 @@
import { ComponentInterface } from '../Component'
import { ThemePackageInfo } from '../Component/PackageInfo'
export interface ThemeInterface extends ComponentInterface {
get layerable(): boolean
readonly package_info: ThemePackageInfo
}

View File

@@ -1,25 +1,4 @@
import { AppDataField } from '../../Abstract/Item/Types/AppDataField'
import { ComponentContent } from '../Component/ComponentContent'
import { DecryptedItemMutator } from '../../Abstract/Item/Mutator/DecryptedItemMutator'
export class ThemeMutator extends DecryptedItemMutator<ComponentContent> {
setMobileRules(rules: unknown) {
this.setAppDataItem(AppDataField.MobileRules, rules)
}
setNotAvailOnMobile(notAvailable: boolean) {
this.setAppDataItem(AppDataField.NotAvailableOnMobile, notAvailable)
}
set local_url(local_url: string) {
this.mutableContent.local_url = local_url
}
/**
* We must not use .active because if you set that to true, it will also
* activate that theme on desktop/web
*/
setMobileActive(active: boolean) {
this.setAppDataItem(AppDataField.MobileActive, active)
}
}
export class ThemeMutator extends DecryptedItemMutator<ComponentContent> {}

View File

@@ -1,2 +1,3 @@
export * from './Theme'
export * from './ThemeMutator'
export * from './ThemeInterface'

View File

@@ -0,0 +1,7 @@
import { FeatureIdentifier } from '@standardnotes/features'
type UuidString = string
export type AllComponentPreferences = Record<FeatureIdentifier | UuidString, ComponentPreferencesEntry>
export type ComponentPreferencesEntry = Record<string, unknown>

View File

@@ -0,0 +1,7 @@
export enum EditorFontSize {
ExtraSmall = 'ExtraSmall',
Small = 'Small',
Normal = 'Normal',
Medium = 'Medium',
Large = 'Large',
}

View File

@@ -0,0 +1,8 @@
export enum EditorLineHeight {
None = 'None',
Tight = 'Tight',
Snug = 'Snug',
Normal = 'Normal',
Relaxed = 'Relaxed',
Loose = 'Loose',
}

View File

@@ -0,0 +1,6 @@
export enum EditorLineWidth {
Narrow = 'Narrow',
Wide = 'Wide',
Dynamic = 'Dynamic',
FullWidth = 'FullWidth',
}

View File

@@ -0,0 +1,6 @@
export enum NewNoteTitleFormat {
CurrentDateAndTime = 'CurrentDateAndTime',
CurrentNoteCount = 'CurrentNoteCount',
CustomFormat = 'CustomFormat',
Empty = 'Empty',
}

View File

@@ -0,0 +1,49 @@
import { FeatureIdentifier } from '@standardnotes/features'
import { CollectionSort } from '../../Runtime/Collection/CollectionSort'
import { EditorFontSize } from './EditorFontSize'
import { EditorLineHeight } from './EditorLineHeight'
import { EditorLineWidth } from './EditorLineWidth'
import { PrefKey, PrefValue } from './PrefKey'
import { NewNoteTitleFormat } from './NewNoteTitleFormat'
export const PrefDefaults = {
[PrefKey.TagsPanelWidth]: 220,
[PrefKey.NotesPanelWidth]: 350,
[PrefKey.EditorWidth]: null,
[PrefKey.EditorLeft]: null,
[PrefKey.EditorMonospaceEnabled]: false,
[PrefKey.EditorSpellcheck]: true,
[PrefKey.EditorResizersEnabled]: false,
[PrefKey.EditorLineHeight]: EditorLineHeight.Normal,
[PrefKey.EditorLineWidth]: EditorLineWidth.FullWidth,
[PrefKey.EditorFontSize]: EditorFontSize.Normal,
[PrefKey.SortNotesBy]: CollectionSort.CreatedAt,
[PrefKey.SortNotesReverse]: false,
[PrefKey.NotesShowArchived]: false,
[PrefKey.NotesShowTrashed]: false,
[PrefKey.NotesHidePinned]: false,
[PrefKey.NotesHideProtected]: false,
[PrefKey.NotesHideNotePreview]: false,
[PrefKey.NotesHideDate]: false,
[PrefKey.NotesHideTags]: false,
[PrefKey.NotesHideEditorIcon]: false,
[PrefKey.UseSystemColorScheme]: false,
[PrefKey.AutoLightThemeIdentifier]: 'Default',
[PrefKey.AutoDarkThemeIdentifier]: FeatureIdentifier.DarkTheme,
[PrefKey.NoteAddToParentFolders]: true,
[PrefKey.NewNoteTitleFormat]: NewNoteTitleFormat.CurrentDateAndTime,
[PrefKey.CustomNoteTitleFormat]: 'YYYY-MM-DD [at] hh:mm A',
[PrefKey.UpdateSavingStatusIndicator]: true,
[PrefKey.PaneGesturesEnabled]: true,
[PrefKey.MomentsDefaultTagUuid]: undefined,
[PrefKey.ClipperDefaultTagUuid]: undefined,
[PrefKey.DefaultEditorIdentifier]: FeatureIdentifier.PlainEditor,
[PrefKey.SuperNoteExportFormat]: 'json',
[PrefKey.SystemViewPreferences]: {},
[PrefKey.AuthenticatorNames]: '',
[PrefKey.ComponentPreferences]: {},
[PrefKey.ActiveThemes]: [],
[PrefKey.ActiveComponents]: [],
} satisfies {
[key in PrefKey]: PrefValue[key]
}

View File

@@ -2,6 +2,11 @@ import { CollectionSortProperty } from '../../Runtime/Collection/CollectionSort'
import { EditorIdentifier, FeatureIdentifier } from '@standardnotes/features'
import { SystemViewId } from '../SmartView'
import { TagPreferences } from '../Tag'
import { NewNoteTitleFormat } from './NewNoteTitleFormat'
import { EditorLineHeight } from './EditorLineHeight'
import { EditorLineWidth } from './EditorLineWidth'
import { EditorFontSize } from './EditorFontSize'
import { AllComponentPreferences } from './ComponentPreferences'
export enum PrefKey {
TagsPanelWidth = 'tagsPanelWidth',
@@ -38,37 +43,9 @@ export enum PrefKey {
SuperNoteExportFormat = 'superNoteExportFormat',
AuthenticatorNames = 'authenticatorNames',
PaneGesturesEnabled = 'paneGesturesEnabled',
}
export enum NewNoteTitleFormat {
CurrentDateAndTime = 'CurrentDateAndTime',
CurrentNoteCount = 'CurrentNoteCount',
CustomFormat = 'CustomFormat',
Empty = 'Empty',
}
export enum EditorLineHeight {
None = 'None',
Tight = 'Tight',
Snug = 'Snug',
Normal = 'Normal',
Relaxed = 'Relaxed',
Loose = 'Loose',
}
export enum EditorLineWidth {
Narrow = 'Narrow',
Wide = 'Wide',
Dynamic = 'Dynamic',
FullWidth = 'FullWidth',
}
export enum EditorFontSize {
ExtraSmall = 'ExtraSmall',
Small = 'Small',
Normal = 'Normal',
Medium = 'Medium',
Large = 'Large',
ComponentPreferences = 'componentPreferences',
ActiveThemes = 'activeThemes',
ActiveComponents = 'activeComponents',
}
export type PrefValue = {
@@ -106,4 +83,7 @@ export type PrefValue = {
[PrefKey.SuperNoteExportFormat]: 'json' | 'md' | 'html'
[PrefKey.AuthenticatorNames]: string
[PrefKey.PaneGesturesEnabled]: boolean
[PrefKey.ComponentPreferences]: AllComponentPreferences
[PrefKey.ActiveThemes]: string[]
[PrefKey.ActiveComponents]: string[]
}

View File

@@ -1,3 +1,9 @@
export * from './UserPrefs'
export * from './UserPrefsMutator'
export * from './PrefKey'
export * from './EditorLineHeight'
export * from './EditorFontSize'
export * from './EditorLineWidth'
export * from './NewNoteTitleFormat'
export * from './ComponentPreferences'
export * from './PrefDefaults'